1
2
3
4
5
6
7
8
9
10
11
12
13
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
66
67
68
69
70
71
72
73
74
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
|
#include <stdlib.h>
#include <QtCore/QtGlobal>
#include "fiber.h"
/*!
\class Fiber
\brief The Fiber class provides cooperatively scheduled stacks of execution.
Fibers, also known as coroutines, allow managing multiple stacks in the same
thread.
To create a fiber, subclass Fiber and override the run() method. To run it,
call cont(). This will execute the code in run() until it calls Fiber::yield().
At that point, the call to cont() returns. Subsequent calls to cont() will
continue execution of the fiber just after the yield().
Example:
class MyFiber : public Fiber
{
virtual void run()
{
qDebug() << "1";
Fiber::yield();
qDebug() << "2";
}
}
MyFiber fib;
qDebug() << "0.5";
fib.cont(); // prints 1
qDebug() << "1.5";
fib.cont(); // prints 2
*/
#ifdef Q_OS_MAC
extern "C" void switchStackInternal(void* to, void** from);
void initializeStack(void *data, int size, void (*entry)(), void **stackPointer);
void switchStack(void* to, void** from) { switchStackInternal(to, from); }
#else
extern "C" void _switchStackInternal(void* to, void** from);
void initializeStack(void *data, int size, void (*entry)(), void **stackPointer);
void switchStack(void* to, void** from) { _switchStackInternal(to, from); }
#endif
Fiber *Fiber::_currentFiber = 0;
Fiber::Fiber(int stackSize)
: _stackData(0)
, _stackPointer(0)
, _previousFiber(0)
, _status(NotStarted)
{
// establish starting fiber context if necessary
currentFiber();
_stackData = malloc(stackSize);
initializeStack(_stackData, stackSize, &entryPoint, &_stackPointer);
}
Fiber::Fiber(bool)
: _stackData(0)
, _stackPointer(0)
, _previousFiber(0)
, _status(Running)
{
}
Fiber::~Fiber()
{
if (_stackData)
free(_stackData);
}
Fiber *Fiber::currentFiber()
{
// establish a context for the starting fiber
if (!_currentFiber)
_currentFiber = new Fiber(true);
return _currentFiber;
}
void Fiber::entryPoint()
{
_currentFiber->run();
yieldHelper(Terminated);
Q_ASSERT(0); // unreachable
}
// returns whether it can be continued again
bool Fiber::cont()
{
Q_ASSERT(_status == NotStarted || _status == Stopped);
Q_ASSERT(!_previousFiber);
_previousFiber = _currentFiber;
_currentFiber = this;
_status = Running;
switchStack(_stackPointer, &_previousFiber->_stackPointer);
return _status != Terminated;
}
void Fiber::yield()
{
yieldHelper(Stopped);
}
void Fiber::yieldHelper(Status stopStatus)
{
Fiber *stoppingFiber = _currentFiber;
Q_ASSERT(stoppingFiber);
Q_ASSERT(stoppingFiber->_previousFiber);
Q_ASSERT(stoppingFiber->_status == Running);
_currentFiber = stoppingFiber->_previousFiber;
stoppingFiber->_previousFiber = 0;
stoppingFiber->_status = stopStatus;
switchStack(_currentFiber->_stackPointer, &stoppingFiber->_stackPointer);
}
|