Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvcont.cc

Go to the documentation of this file.
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * WvCont provides "continuations", which are apparently also known as 00006 * semi-coroutines. See wvcont.h for more details. 00007 */ 00008 #include "wvcont.h" 00009 #include "wvtask.h" 00010 00011 // private data that doesn't need to be in the header 00012 struct WvCont::Data 00013 { 00014 int links; // the refcount of this Data object 00015 int mydepth; // this task's depth in the call stack 00016 bool finishing; // true if we're trying to terminate this task ASAP 00017 // (generally because WvCont is being destroyed) 00018 size_t stacksize; 00019 WvTaskMan *taskman; 00020 WvTask *task; 00021 00022 Callback cb; // the callback we want to call inside our WvTask 00023 R ret; 00024 P1 p1; 00025 00026 Data(const Callback &_cb, size_t _stacksize) : cb(_cb) 00027 { links = 1; finishing = false; stacksize = _stacksize; mydepth = 0; 00028 taskman = WvTaskMan::get(); 00029 task = NULL; report(); } 00030 ~Data() 00031 { assert(!links); taskman->unlink(); report(); } 00032 00033 void link() 00034 { links++; report(); } 00035 void unlink() 00036 { links--; report(); if (!links) delete this; } 00037 00038 void report() 00039 { /* printf("%p: links=%d\n", this, links); */ } 00040 }; 00041 00042 00043 WvCont::Data *WvCont::curdata = NULL; 00044 int WvCont::taskdepth = 0; 00045 00046 00047 WvCont::WvCont(const WvCont &cb) 00048 { 00049 data = cb.data; 00050 data->link(); 00051 } 00052 00053 00054 WvCont::WvCont(const Callback &cb, unsigned long _stacksize) 00055 { 00056 data = new Data(cb, (size_t)_stacksize); 00057 } 00058 00059 00060 WvCont::~WvCont() 00061 { 00062 if (data->links == 1) 00063 { 00064 // run the task until it finishes. We can't delete it until then! 00065 data->finishing = true; // make WvCont::isok() false 00066 while (data->task && data->task->isrunning()) 00067 call(); 00068 00069 if (data->task) 00070 data->task->recycle(); 00071 } 00072 data->unlink(); 00073 } 00074 00075 00076 // note: assumes data->task is already running! 00077 void WvCont::call() 00078 { 00079 Data *olddata = curdata; 00080 curdata = data; 00081 00082 // enforce the call stack. If we didn't do this, a yield() five calls 00083 // deep would return to the very top, rather to the second-innermost 00084 // context. 00085 // 00086 // Note that this implementation has the interesting side-effect of 00087 // short-circuiting recursion (a calls b, b calls c, c calls a), since 00088 // calling 'a' if it's already running means the same as "yield all the 00089 // way back to a", and this loop enforces one-level-at-a-time yielding. 00090 // 00091 // Because that behaviour is probably undesirable, we make 'mydepth' into 00092 // a member variable instead of just putting it on the stack. This is 00093 // only needed so that we can have the assert(). 00094 assert(!data->mydepth); 00095 data->mydepth = ++taskdepth; 00096 do 00097 { 00098 data->taskman->run(*data->task); 00099 } while (taskdepth > data->mydepth); 00100 assert(taskdepth == data->mydepth); 00101 taskdepth--; 00102 data->mydepth = 0; 00103 00104 curdata = olddata; 00105 } 00106 00107 00108 WvCont::WvCont(Data *data) 00109 { 00110 this->data = data; 00111 data->link(); 00112 } 00113 00114 00115 WvCont::R WvCont::operator() (P1 p1) 00116 { 00117 data->ret = R(-42); 00118 00119 if (!data->task) 00120 data->task = data->taskman->start("wvcont", bouncer, data, 00121 data->stacksize); 00122 else if (!data->task->isrunning()) 00123 data->task->start("wvcont+", bouncer, data); 00124 00125 data->p1 = p1; 00126 call(); 00127 return data->ret; 00128 } 00129 00130 00131 WvCont WvCont::current() 00132 { 00133 assert(curdata); 00134 assert(curdata->task == curdata->taskman->whoami()); 00135 assert(isok()); // this assertion is a bit aggressive... 00136 return WvCont(curdata); 00137 } 00138 00139 00140 WvCont::P1 WvCont::yield(R ret) 00141 { 00142 assert(curdata); 00143 assert(curdata->task == curdata->taskman->whoami()); 00144 assert(isok()); // this assertion is a bit aggressive... 00145 curdata->ret = ret; 00146 curdata->taskman->yield(); 00147 return curdata->p1; 00148 } 00149 00150 00151 bool WvCont::isok() 00152 { 00153 assert(curdata); 00154 assert(curdata->task == curdata->taskman->whoami()); 00155 return !curdata->finishing; 00156 } 00157 00158 00159 void WvCont::bouncer(void *userdata) 00160 { 00161 Data *data = (Data *)userdata; 00162 00163 // DON'T BE FOOLED! 00164 // all yield() calls stay inside the inner function; our return value 00165 // is only for the final run after data->cb() returns. 00166 data->ret = data->cb(data->p1); 00167 } 00168 00169

Generated on Tue Oct 5 01:09:20 2004 for WvStreams by doxygen 1.3.7