Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | 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 Wed Dec 15 15:08:10 2004 for WvStreams by  doxygen 1.3.9.1