Fix compilation with old g++ 3.3.5 and debian-sarge.
[wvstreams.git] / utils / wvcont.cc
blob0e03a8387246a09bf2e023ceeeaa4ef307b796a1
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
5 * WvCont provides "continuations", which are apparently also known as
6 * semi-coroutines. See wvcont.h for more details.
7 */
8 #include "wvcont.h"
9 #include "wvtask.h"
10 #include "wvlinklist.h"
11 #include <assert.h>
13 // private data that doesn't need to be in the header
14 struct WvCont::Data
16 int links; // the refcount of this Data object
17 int mydepth; // this task's depth in the call stack
18 bool finishing; // true if we're trying to terminate this task ASAP
19 // (generally because WvCont is being destroyed)
20 size_t stacksize;
21 WvTaskMan *taskman;
22 WvTask *task;
24 WvContCallback cb; // the callback we want to call inside our WvTask
25 void *ret;
26 void *p1;
28 Data(const WvContCallback &_cb, size_t _stacksize) : cb(_cb)
29 { links = 1; finishing = false; stacksize = _stacksize; mydepth = 0;
30 taskman = WvTaskMan::get();
31 task = NULL; report();
32 if (data_list == NULL)
33 data_list = new DataList;
34 data_list->append(this, false);
36 ~Data();
38 void link()
39 { links++; report(); }
40 void unlink()
41 { links--; report(); if (!links) delete this; }
43 void report()
44 { /* printf("%p: links=%d\n", this, links); */ }
48 WvCont::Data *WvCont::curdata = NULL;
49 int WvCont::taskdepth = 0;
52 WvCont::DataList *WvCont::data_list = NULL;
55 WvCont::WvCont(const WvCont &cb)
57 static bool first = true;
58 if (first)
60 first = false;
61 WvStreamsDebugger::add_command("conts", 0,
62 debugger_conts_run_cb, 0);
65 data = cb.data;
66 data->link();
70 WvCont::WvCont(const WvContCallback &cb, unsigned long _stacksize)
72 data = new Data(cb, (size_t)_stacksize);
76 WvCont::WvCont(Data *data)
78 this->data = data;
79 data->link();
83 WvCont::~WvCont()
85 if (data->links == 1) // I'm the last link, and it's not currently running
87 data->finishing = true;
88 data->p1 = NULL; // don't re-pass invalid data
89 while (data->task && data->task->isrunning())
90 call();
93 data->unlink();
97 WvCont::Data::~Data()
99 assert(!links);
101 if (task)
102 task->recycle();
103 taskman->unlink();
104 //printf("%p: deleting\n", this);
105 report();
107 data_list->unlink(this);
108 if (data_list->isempty())
110 delete data_list;
111 data_list = NULL;
116 static inline const char *Yes_No(bool val)
118 return val? "Yes": "No";
122 WvString WvCont::debugger_conts_run_cb(WvStringParm cmd, WvStringList &args,
123 WvStreamsDebugger::ResultCallback result_cb, void *)
125 const char *format = "%5s%s%5s%s%9s%s%10s%s%7s%s%s";
126 WvStringList result;
127 result.append(format, "Links", "-", "Depth", "-", "Finishing", "-", "Stack Size",
128 "-", "Task ID", "-", "Task Name------");
129 result_cb(cmd, result);
131 if (!data_list)
132 return WvString::null;
134 DataList::Iter i(*data_list);
135 for (i.rewind(); i.next(); )
137 result.zap();
138 result.append(format,
139 i->links, " ", i->mydepth, " ", Yes_No(i->finishing), " ",
140 i->stacksize, " ",
141 i->task? WvString(i->task->get_tid()): WvString("n/a"), " ",
142 i->task? i->task->get_name(): WvString("n/a"));
143 result_cb(cmd, result);
146 return WvString::null;
150 // note: assumes data->task is already running!
151 void *WvCont::_call(Data *data)
153 Data *olddata = curdata;
154 curdata = data;
155 data->link(); // don't delete this context while it's running!
157 // enforce the call stack. If we didn't do this, a yield() five calls
158 // deep would return to the very top, rather to the second-innermost
159 // context.
161 // Note that this implementation has the interesting side-effect of
162 // short-circuiting recursion (a calls b, b calls c, c calls a), since
163 // calling 'a' if it's already running means the same as "yield all the
164 // way back to a", and this loop enforces one-level-at-a-time yielding.
166 // Because that behaviour is probably undesirable, we make 'mydepth' into
167 // a member variable instead of just putting it on the stack. This is
168 // only needed so that we can have the assert().
169 assert(!data->mydepth);
170 data->mydepth = ++taskdepth;
173 assert(data->task);
176 data->taskman->run(*data->task);
177 if (data->links == 1)
179 data->finishing = true; // make WvCont::isok() false
180 data->p1 = NULL; // don't re-pass invalid data
182 } while (data->finishing && data->task && data->task->isrunning());
183 assert(data->links);
184 } while (taskdepth > data->mydepth);
185 assert(taskdepth == data->mydepth);
186 taskdepth--;
187 data->mydepth = 0;
189 void *ret = data->ret;
190 data->unlink();
191 curdata = olddata;
192 return ret;
196 void *WvCont::operator() (void *p1)
198 data->ret = reinterpret_cast<void*>(-42);
200 if (!data->task)
201 data->task = data->taskman->start("wvcont", bouncer, data,
202 data->stacksize);
203 else if (!data->task->isrunning())
204 data->task->start("wvcont+", bouncer, data);
206 assert(data->task);
208 data->p1 = p1;
209 return call();
213 WvCont WvCont::current()
215 assert(curdata);
216 assert(curdata->task == curdata->taskman->whoami());
217 assert(isok()); // this assertion is a bit aggressive...
218 return WvCont(curdata);
222 void *WvCont::yield(void *ret)
224 assert(curdata);
225 assert(curdata->task == curdata->taskman->whoami());
227 // this assertion is a bit aggressive, but on purpose; a callback that
228 // does yield() instead of returning when its context should be dying
229 // is pretty badly behaved.
230 assert(isok());
232 curdata->ret = ret;
233 curdata->taskman->yield();
234 return curdata->p1;
238 bool WvCont::isok()
240 // if we're not using WvCont, it's not okay to yield
241 if (!curdata)
242 return false;
244 assert(curdata->task == curdata->taskman->whoami());
245 return !curdata->finishing;
249 void WvCont::bouncer(void *userdata)
251 Data *data = (Data *)userdata;
253 // DON'T BE FOOLED!
254 // all yield() calls stay inside the inner function; our return value
255 // is only for the final run after data->cb() returns.
256 data->ret = data->cb(data->p1);