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
121
122
123
124
125
126
127
|
#include <stdlib.h>
#include <QtCore/QtGlobal>
#include <QtCore/QThreadStorage>
#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.
\omit ### outdated \endomit
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:
void myFiber()
{
qDebug() << "1";
Fiber::yield();
qDebug() << "2";
}
MyFiber fib(&myFiber);
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(StartFunction startFunction, int stackSize)
: _startFunction(startFunction)
, _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()
: _startFunction(0)
, _stackData(0)
, _stackPointer(0)
, _previousFiber(0)
, _status(Running)
{
}
Fiber::~Fiber()
{
if (_stackData)
free(_stackData);
}
static QThreadStorage<Fiber *> qt_currentFiber;
Fiber *Fiber::currentFiber()
{
Fiber *current = qt_currentFiber.localData();
if (current)
return current;
// establish a context for the starting fiber
current = new Fiber;
qt_currentFiber.setLocalData(current);
return current;
}
void Fiber::entryPoint()
{
qt_currentFiber.localData()->_startFunction();
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);
_status = Running;
_previousFiber = qt_currentFiber.localData();
qt_currentFiber.setLocalData(this);
switchStack(_stackPointer, &_previousFiber->_stackPointer);
return _status != Terminated;
}
void Fiber::yield()
{
yieldHelper(Stopped);
}
void Fiber::yieldHelper(Status stopStatus)
{
Fiber *stoppingFiber = qt_currentFiber.localData();
Q_ASSERT(stoppingFiber);
Q_ASSERT(stoppingFiber->_status == Running);
stoppingFiber->_status = stopStatus;
Fiber *continuingFiber = stoppingFiber->_previousFiber;
Q_ASSERT(continuingFiber);
stoppingFiber->_previousFiber = 0;
qt_currentFiber.setLocalData(continuingFiber);
switchStack(continuingFiber, &stoppingFiber->_stackPointer);
}
|