summaryrefslogtreecommitdiffstats
path: root/src/coroutine.cpp
diff options
context:
space:
mode:
authorChristian Kamm <christian.d.kamm@nokia.com>2010-03-22 12:32:11 +0100
committerChristian Kamm <christian.d.kamm@nokia.com>2010-03-26 13:05:02 +0100
commit883dee678eca4f55faf3848a248253d4b4b8a40e (patch)
treef13c70472015d70cac2707a4dd5b8d295a437b5a /src/coroutine.cpp
parentb03a4b990d9d26f62958cb1968bcf463e6d87eb5 (diff)
Rename Fiber -> Coroutine.
* Avoid confusion with Windows fibers. * Easier to search for.
Diffstat (limited to 'src/coroutine.cpp')
-rw-r--r--src/coroutine.cpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/coroutine.cpp b/src/coroutine.cpp
new file mode 100644
index 0000000..eb6ebc5
--- /dev/null
+++ b/src/coroutine.cpp
@@ -0,0 +1,127 @@
+#include <stdlib.h>
+#include <QtCore/QtGlobal>
+#include <QtCore/QThreadStorage>
+
+#include "coroutine.h"
+
+/*!
+ \class Coroutine
+ \brief The Coroutine class provides cooperatively scheduled stacks of execution.
+
+ Coroutines, also known as fibers, allow managing multiple stacks in the same
+ thread.
+
+ \omit ### outdated \endomit
+ To create a coroutine, subclass Coroutine and override the run() method. To run it,
+ call cont(). This will execute the code in run() until it calls Coroutine::yield().
+ At that point, the call to cont() returns. Subsequent calls to cont() will
+ continue execution of the coroutine just after the yield().
+
+ Example:
+ void myCoroutine()
+ {
+ qDebug() << "1";
+ Coroutine::yield();
+ qDebug() << "2";
+ }
+
+ MyCoroutine c(&myCoroutine);
+ qDebug() << "0.5";
+ c.cont(); // prints 1
+ qDebug() << "1.5";
+ c.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
+
+Coroutine::Coroutine(StartFunction startFunction, int stackSize)
+ : _startFunction(startFunction)
+ , _stackData(0)
+ , _stackPointer(0)
+ , _previousCoroutine(0)
+ , _status(NotStarted)
+{
+ // establish starting coroutine context if necessary
+ currentCoroutine();
+
+ _stackData = malloc(stackSize);
+ initializeStack(_stackData, stackSize, &entryPoint, &_stackPointer);
+}
+
+Coroutine::Coroutine()
+ : _startFunction(0)
+ , _stackData(0)
+ , _stackPointer(0)
+ , _previousCoroutine(0)
+ , _status(Running)
+{
+}
+
+Coroutine::~Coroutine()
+{
+ if (_stackData)
+ free(_stackData);
+}
+
+static QThreadStorage<Coroutine *> qt_currentCoroutine;
+
+Coroutine *Coroutine::currentCoroutine()
+{
+ Coroutine *current = qt_currentCoroutine.localData();
+ if (current)
+ return current;
+
+ // establish a context for the starting coroutine
+ current = new Coroutine;
+ qt_currentCoroutine.setLocalData(current);
+ return current;
+}
+
+void Coroutine::entryPoint()
+{
+ qt_currentCoroutine.localData()->_startFunction();
+ yieldHelper(Terminated);
+ Q_ASSERT(0); // unreachable
+}
+
+// returns whether it can be continued again
+bool Coroutine::cont()
+{
+ Q_ASSERT(_status == NotStarted || _status == Stopped);
+ Q_ASSERT(!_previousCoroutine);
+
+ _status = Running;
+
+ _previousCoroutine = qt_currentCoroutine.localData();
+ qt_currentCoroutine.setLocalData(this);
+ switchStack(_stackPointer, &_previousCoroutine->_stackPointer);
+ return _status != Terminated;
+}
+
+void Coroutine::yield()
+{
+ yieldHelper(Stopped);
+}
+
+void Coroutine::yieldHelper(Status stopStatus)
+{
+ Coroutine *stoppingCoroutine = qt_currentCoroutine.localData();
+ Q_ASSERT(stoppingCoroutine);
+ Q_ASSERT(stoppingCoroutine->_status == Running);
+ stoppingCoroutine->_status = stopStatus;
+
+ Coroutine *continuingCoroutine = stoppingCoroutine->_previousCoroutine;
+ Q_ASSERT(continuingCoroutine);
+
+ stoppingCoroutine->_previousCoroutine = 0;
+ qt_currentCoroutine.setLocalData(continuingCoroutine);
+ switchStack(continuingCoroutine, &stoppingCoroutine->_stackPointer);
+}