diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/coroutine/coroutine.h | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/ext/coroutine/coroutine.h b/ext/coroutine/coroutine.h new file mode 100644 index 0000000000..985888bf13 --- /dev/null +++ b/ext/coroutine/coroutine.h @@ -0,0 +1,133 @@ +// +// 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 + |