본문 바로가기

진리는어디에

boost::asio::spawn이 하는 일은 뭔가요?

spawn의 전체적인 과정이 머리속에서 안그려짐.

1. spawn(io_service, my_coroutine)을 호출 했을 때, my_coroutine을 감싸고 있는 새로운 핸들러가 io_service 큐에 추가 되는건가?

-> spawn이 호출 될 때, Boost.Asio는 몇몇 셋팅 작업을 거친 후 사용자가 제공한 엔트리 포인트로써의 함수를 사용하는 코루틴을 생성하는 내부 핸들러를 dispatch() 하기 위해 strand를 사용하게 된다. 이런 조건에서, 내부 핸들러는 spawn 내에서 호출 가능하다. 그리고 다른 때는 지연된 호출을 위해 io_service에 post 된다.

 

2. 코루틴 안에서 인자로 넘어온 yield_context를 호출 했을 때, async operation이 완료 될때 까지 코루틴이 suspend 되는 건가?

-> 코루틴은 전체 오퍼레이션이 완료 되어 컴플리션 핸들러가 호출 되거나, io_service 객체가 파괴 되거나, Boost.Asio가 코루틴이 더 이상 리쥼되지 않는데 suspend 되어 있다고 판단하기 까지 전까지 suspend 되어 있다. 

 

void my_coroutine(yield_context yield) {
	... async_foo(params ..., yield); 
	... // control comes here only once the async_foo operation completes 
}

내가 이해를 못하고 있는 것은 어떻게 기다리는 것을 피할 수 있는가이다. 만일 my_coroutine이 tcp 컨넥션을 다루고 있다면, 어떻게 async_foo가 완료 되기를 기다리고 있는 특정 인스턴스가 suspend되어 있는 동안 , 다른 my_coroutine인스턴스가 호출 되는가?


spawn()이 호출 되었을 때, Boost.Asio는 몇몇 셋팅 작업을 거치고 내부 핸들러를 호출하기 위해 strand를 사용한다. yield_context 객체가 비동기 작업의 완료 핸들러로 넘어 왔을 때, Boost.Asio는 비동기 작업의 초기화(completion handler를 초기화, 결과를 복사하고, coroutine을 resume하는 완료 핸들러 초기화) 작업을 마치는 즉시 yield 한다. 

 

코루틴이 가지고 있는 strand는 yield가 resume 이전에 호출 됨을 보장한다,. 아래 코드를 보자.

 

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

boost::asio::io_service io_service;

void other_work()
{
  std::cout << "Other work" << std::endl;
}

void my_work(boost::asio::yield_context yield_context)
{
  // Add more work to the io_service.
  io_service.post(&other_work);

  // Wait on a timer within the coroutine.
  boost::asio::deadline_timer timer(io_service);
  timer.expires_from_now(boost::posix_time::seconds(1));
  std::cout << "Start wait" << std::endl;
  timer.async_wait(yield_context);
  std::cout << "Woke up" << std::endl;    
}

int main ()
{
  boost::asio::spawn(io_service, &my_work);
  io_service.run();
}

위 예제의 결과는 아래와 같다 : 

Start wait

Other work

Woke up

 

아래는 위 예제의 설명을 도식화 한것이다. | 는 활성화된 스택, :는 suspend되어 있는 스택이다. 그리고 화살표는 컨트롤 전환이다.

 

boost::asio::io_service io_service;
boost::asio::spawn(io_service, &my_work);
`-- dispatch a coroutine creator
    into the io_service.
io_service.run();
|-- invoke the coroutine creator
|   handler.
|   |-- create and jump into
|   |   into coroutine         ----> my_work()
:   :                                |-- post &other_work onto
:   :                                |   the io_service
:   :                                |-- create timer
:   :                                |-- set timer expiration
:   :                                |-- cout << "Start wait" << endl;
:   :                                |-- timer.async_wait(yield)
:   :                                |   |-- create error_code on stack
:   :                                |   |-- initiate async_wait operation,
:   :                                |   |   passing in completion handler that
:   :                                |   |   will resume the coroutine
|   `-- return                 <---- |   |-- yield
|-- io_service has work (the         :   :
|   &other_work and async_wait)      :   :
|-- invoke other_work()              :   :
|   `-- cout << "Other work"         :   :
|       << endl;                     :   :
|-- io_service still has work        :   :
|   (the async_wait operation)       :   :
|   ...async wait completes...       :   :
|-- invoke completion handler        :   :
|   |-- copies error_code            :   :
|   |   provided by service          :   :
|   |   into the one on the          :   :
|   |   coroutine stack              :   :
|   |-- resume                 ----> |   `-- return error code
:   :                                |-- cout << "Woke up." << endl;
:   :                                |-- exiting my_work block, timer is 
:   :                                |   destroyed.
|   `-- return                 <---- `-- coroutine done, yielding
`-- no outstanding work in 
    io_service, return.

stackoverflow.com/questions/30557582/what-does-boostasiospawn-do

 

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!