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.
10 #include "wvlinklist.h"
13 // private data that doesn't need to be in the header
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)
24 WvContCallback cb
; // the callback we want to call inside our WvTask
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);
39 { links
++; report(); }
41 { links
--; report(); if (!links
) delete this; }
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;
61 WvStreamsDebugger::add_command("conts", 0,
62 debugger_conts_run_cb
, 0);
70 WvCont::WvCont(const WvContCallback
&cb
, unsigned long _stacksize
)
72 data
= new Data(cb
, (size_t)_stacksize
);
76 WvCont::WvCont(Data
*data
)
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())
104 //printf("%p: deleting\n", this);
107 data_list
->unlink(this);
108 if (data_list
->isempty())
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";
127 result
.append(format
, "Links", "-", "Depth", "-", "Finishing", "-", "Stack Size",
128 "-", "Task ID", "-", "Task Name------");
129 result_cb(cmd
, result
);
132 return WvString::null
;
134 DataList::Iter
i(*data_list
);
135 for (i
.rewind(); i
.next(); )
138 result
.append(format
,
139 i
->links
, " ", i
->mydepth
, " ", Yes_No(i
->finishing
), " ",
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
;
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
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
;
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());
184 } while (taskdepth
> data
->mydepth
);
185 assert(taskdepth
== data
->mydepth
);
189 void *ret
= data
->ret
;
196 void *WvCont::operator() (void *p1
)
198 data
->ret
= reinterpret_cast<void*>(-42);
201 data
->task
= data
->taskman
->start("wvcont", bouncer
, data
,
203 else if (!data
->task
->isrunning())
204 data
->task
->start("wvcont+", bouncer
, data
);
213 WvCont
WvCont::current()
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
)
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.
233 curdata
->taskman
->yield();
240 // if we're not using WvCont, it's not okay to yield
244 assert(curdata
->task
== curdata
->taskman
->whoami());
245 return !curdata
->finishing
;
249 void WvCont::bouncer(void *userdata
)
251 Data
*data
= (Data
*)userdata
;
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
);