2014/1/10 Niall Douglas
Where I am finding things harder is what yield means to ASIO which takes a callback function of specification void (*callable)(const boost::system::error_code& error, size_t bytes_transferred) so fiber yield must supply a prototype matching that specification. You said above this:
- yield is an instance of boost::fibers::asio::yield_context which represents the fiber running this code; it is used by asio's async result feature
- yield[ec] is put to an async-operation in order suspending the current fiber and pass an error (if happend during execution of async-op) back to the calling code, for instance EOF if socket was closed
So I figure that yield will store somewhere a new bit of context, do a std::bind() encapsulating that context and hand off the functor to ASIO. ASIO then schedules the async i/o. When that completes, ASIO will do an io_service::post() with the bound functor, and so one of the fibers currently executing run() will get woken up and will invoke the bound functor.
So far so good. But here is where I fall down: the fibre receives in an error state and bytes transferred, and obviously transmits that back to the fiber which called ASIO async_read() by switching back to the original context. I would understand just fine if yield() suspended the calling fiber, but it surely cannot because yield() will get executed before ASIO async_read() does as it's a parameter to async_read(). So I therefore must be missing something very important, and this is why I am confused. Is it possible that yield() takes a *copy* of the context like setjmp()?
It's this kind of stuff which documentation is for, and why at least one person i.e. me needs hand holding through mentally groking how the ASIO support in Fiber works. Sorry for being a bit stupid.
you have to distinct between to 'yield' symbols - in the context of fibers without ASIO , e.g. calling boost::this_fiber::yield() the current fiber is supended at added at the end of the ready-queue of the fiber-scheduler (the fiber will be resumed if dequeued from this list). the other case you are referring to is the async-feature of boost.asio. Chris has already implemented the async-feature for coroutines (from boost.coroutine). so the best source of the internal working is the docu of boost.asio. I've added support of asio's async-feature for fibers, e.g. if provided some classes required to use fibers in the context of asio and its async-operations (for instance async_read() see examples). usaly this code should belong to boost.asio instead boost.fiber - anyway, because boost.fiber is new I've added it in my lib. the yield instance you use with the async-ops of asio (async_read() for isntance) is a global of type yield_t. this type holds an error_code as member and operator[error_code &] let you retrieve the error_code from the async-op. boost::fibers::asio::spawn() is the function which starts a new fiber