// // coroutine.h // ~~~~~~~~~~~ // // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #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: coroutine() : value_(0) {} virtual ~coroutine() {} bool is_child() const { return value_ < 0; } bool is_parent() const { return !is_child(); } bool is_complete() const { return value_ == -1; } int get_value() const { return value_; } private: friend class coroutine_ref; int value_; }; class coroutine_ref { public: coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {} coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {} ~coroutine_ref() { if (!modified_) value_ = -1; } operator int() const { return value_; } int& operator=(int v) { modified_ = true; return value_ = v; } private: void operator=(const coroutine_ref&); int& value_; bool modified_; }; #define CORO_REENTER(c) \ switch (coroutine_ref _coro_value = c) \ case -1: if (_coro_value) \ { \ goto terminate_coroutine; \ terminate_coroutine: \ _coro_value = -1; \ goto bail_out_of_coroutine; \ bail_out_of_coroutine: \ break; \ } \ else case 0: #define CORO_YIELD \ for (_coro_value = __LINE__;;) \ if (_coro_value == 0) \ { \ case __LINE__: ; \ break; \ } \ else \ switch (_coro_value ? 0 : 1) \ for (;;) \ case -1: if (_coro_value) \ goto terminate_coroutine; \ else for (;;) \ case 1: if (_coro_value) \ goto bail_out_of_coroutine; \ else case 0: #define CORO_FORK \ for (_coro_value = -__LINE__;; _coro_value = __LINE__) \ if (_coro_value == __LINE__) \ { \ case -__LINE__: ; \ break; \ } \ else #endif // COROUTINE_HPP