|
@@ -11,6 +11,58 @@
|
|
|
#ifndef COROUTINE_HPP
|
|
|
#define COROUTINE_HPP
|
|
|
|
|
|
+
|
|
|
+// \brief Coroutine object
|
|
|
+//
|
|
|
+// A coroutine object maintains the state of a re-enterable routine. It
|
|
|
+// is assignable and copy-constructable, and can be used as a base class
|
|
|
+// for a class that uses it, or as a data member. The copy overhead is
|
|
|
+// a single int.
|
|
|
+//
|
|
|
+// A reenterable function contains a CORO_REENTER (coroutine) { ... }
|
|
|
+// block. Whenever an asychrnonous operation is initiated within the
|
|
|
+// routine, the function is provided as the handler object. (The simplest
|
|
|
+// way to do this is to have the reenterable function be the operator()
|
|
|
+// member for the coroutine object itself.) For example:
|
|
|
+//
|
|
|
+// CORO_YIELD socket->async_read_some(buffer, *this);
|
|
|
+//
|
|
|
+// The CORO_YIELD keyword updates the current status of the coroutine to
|
|
|
+// indicate the line number currently being executed. The
|
|
|
+// async_read_some() call is initiated, with a copy of the updated
|
|
|
+// corotutine as its handler object, and the current coroutine exits. When
|
|
|
+// the async_read_some() call finishes, the copied coroutine will be
|
|
|
+// called, and will resume processing exactly where the original one left
|
|
|
+// off--right after asynchronous call. This allows asynchronous I/O
|
|
|
+// routines to be written with a logical flow, step following step, rather
|
|
|
+// than as a linked chain of separate handler functions.
|
|
|
+//
|
|
|
+// When necessary, a coroutine can fork itself using the CORO_FORK keyword.
|
|
|
+// This updates the status of the coroutine and makes a copy. The copy can
|
|
|
+// then be called directly or posted to the ASIO service queue so that both
|
|
|
+// coroutines will continue forward, one "parent" and one "child". The
|
|
|
+// is_parent() and is_child() methods indicate which is which.
|
|
|
+//
|
|
|
+// The CORO_REENTER, CORO_YIELD and CORO_FORK keywords are implemented
|
|
|
+// via preprocessor macros. The CORO_REENTER block is actually a large,
|
|
|
+// complex switch statement. Because of this, inline variable declaration
|
|
|
+// is impossible within CORO_REENTER unless it is done in a subsidiary
|
|
|
+// scope--and if it is, that scope cannot contain CORO_YIELD or CORO_FORK
|
|
|
+// keywords.
|
|
|
+//
|
|
|
+// Because coroutines are frequently copied, it is best to minimize copy
|
|
|
+// overhead by limiting the size of data members in derived classes.
|
|
|
+//
|
|
|
+// It should be noted that when a coroutine falls out of scope its memory
|
|
|
+// is reclaimed, even though it may be scheduled to resume when an
|
|
|
+// asynchronous operation completes. Any shared_ptr<> objects declared in
|
|
|
+// the coroutine may be destroyed if their reference count drops to zero,
|
|
|
+// in which case the coroutine will have serious problems once it resumes.
|
|
|
+// One solution so this is to have the space that will be used by a
|
|
|
+// coroutine pre-allocated and stored on a free list; a new coroutine can
|
|
|
+// fetch the block of space off a free list, place a shared pointer to it
|
|
|
+// on an "in use" list, and carry on. The reference in the "in use" list
|
|
|
+// would prevent the data from being destroyed.
|
|
|
class coroutine
|
|
|
{
|
|
|
public:
|