The ProtoThread
'trick' is to use the C++ preprocessor to textually replace the OOSMOS
Thread
APIs with switch
, case
and default
statements.
Each thread function is continuously entered, and then exited when it is determined that an asynchronous operation cannot be completed during this execution. Each thread function is then again reentered, picking up where it left off the last time until the current asynchronous operation is complete, when it then drops down to subsequent statements where it may encounter another asynchronous operation (an OOSMOS
Thread
function) and the process repeats.
For reference:
#define OOSMOS_THREAD_CONTEXT_BEGIN (-1)
#define OOSMOS_THREAD_CONTEXT_END (-3)
__LINE__ Original source code Preprocessor output
======== ================================================= ========================================
2 #line 3 "thread_test.cpp"
3
4 using namespace std; using namespace std;
5
6 struct cMyObject : public OOSMOS::cObject { struct cMyObject : public OOSMOS::cObject {
7 cStack BlinkingThread_Data; cStack BlinkingThread_Data;
8
9 void BlinkingThread(cStack& rStack) { void BlinkingThread(cStack& rStack) {
10 ThreadBegin(); switch (rStack.m_ThreadContext) { case (-1):;
11 for (;;) { for (;;) {
12 cout << "BlinkingThread: LED On" << endl; cout << "BlinkingThread: LED On" << endl;
13 ThreadDelayMS(250); case 13: rStack.m_ThreadContext = 13; if (!rStack.OOSMOS_ThreadDelayMS(250)) return;
14 cout << "BlinkingThread: LED Off" << endl; cout << "BlinkingThread: LED Off" << endl;
15 ThreadDelayMS(750); case 15: rStack.m_ThreadContext = 15; if (!rStack.OOSMOS_ThreadDelayMS(750)) return;
16 } }
17 ThreadEnd(); default: rStack.m_ThreadContext = (-3); } return;
18 } }
-
On line 2: the preprocessor synchronizes line numbers, indicating that #line 3 is the next line.
-
On line 10, the
ThreadBegin()
macro is replaced with theswitch
andcase
statement. When the thread was started,rStack.m_ThreadContext
was initialized toOOSMOS_THREAD_CONTEXT_BEGIN
which is-1
, so, on entry toBlinkingThread
, theswitch
first executescase (-1)
and then drops into thefor
statement. (Nobreak
statement.) -
Line 11, the
for
statement, is executed. -
Line 12, the
cout
statement is executed and then drops into line 13. (Nobreak
statement.) -
Line 13, the
ThreadDelayMS
statement is executed. Note thatrStack.m_ThreadContext
is set to 13 (via special preprocessor symbol__LINE__
), which is the current line number. We then call the internalOOSMOS
functionOOSMOS_ThreadDelayMS(250)
which will returntrue
if the delay has expired. If it has, then it will drop through to line 14 (nobreak
statement). If it has not, then thereturn
statement causes an immediate return from theBlinkingThread
function. But recall that we remembered line 13, the current line, in therStack.m_ThreadContext
variable. Therefore, the next time we enterBlinkingThread
and execute theswitch
statement, we will come right back to line 13 again to check if theThreadDelayMS
has expired. We continue toreturn
and reenterBlinkingThread
until the delay has expired. This line alone demonstrates the magic of the ProtoThread technique. -
Line 14 is the next
cout
statement. Nothing special here. -
Line 15 is handled just like line 13.
-
Line 17,
rStack.m_ThreadContext
is set to-3
, so the next time we execute theBlinkingThread
and theswitch
statement, it will continue to execute thedefault
case, effectively ending the thread.