coroutine.h 4.78 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
//
// 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

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

// \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.
66
67
68
69
class coroutine
{
public:
  coroutine() : value_(0) {}
Jelte Jansen's avatar
Jelte Jansen committed
70
  virtual ~coroutine() {}
71
72
73
  bool is_child() const { return value_ < 0; }
  bool is_parent() const { return !is_child(); }
  bool is_complete() const { return value_ == -1; }
74
  int get_value() const { return value_; }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
133