123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- //
- // 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-constructible, 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 reentrant function contains a CORO_REENTER (coroutine) { ... }
- // block. Whenever an asynchronous operation is initiated within the
- // routine, the function is provided as the handler object. (The simplest
- // way to do this is to have the reentrant 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
- // coroutine 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
|