00001
00002
00003
00004
00005
00006
00007
00008 #include "wvtask.h"
00009 #include <stdio.h>
00010 #include <stdlib.h>
00011 #include <malloc.h>
00012 #include <assert.h>
00013
00014 #if 0
00015 # define Dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
00016 #else
00017 # define Dprintf(fmt, args...)
00018 #endif
00019
00020
00021 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
00022
00023
00024 WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man)
00025 {
00026 stacksize = _stacksize;
00027 running = recycled = false;
00028
00029 tid = ++taskcount;
00030 numtasks++;
00031 magic_number = WVTASK_MAGIC;
00032
00033 Dprintf("task %d initializing\n", tid);
00034 man.get_stack(*this, stacksize);
00035 Dprintf("task %d initialized\n", tid);
00036 }
00037
00038
00039 WvTask::~WvTask()
00040 {
00041 numtasks--;
00042 Dprintf("task %d stopping (%d tasks left)\n", tid, numtasks);
00043
00044 if (running)
00045 {
00046 Dprintf("WARNING: task %d was running -- bad stuff may happen!\n",
00047 tid);
00048 numrunning--;
00049 }
00050
00051 magic_number = 42;
00052 }
00053
00054
00055 void WvTask::start(const WvString &_name, TaskFunc *_func, void *_userdata)
00056 {
00057 assert(!recycled);
00058 name = _name;
00059 name.unique();
00060 Dprintf("task %d (%s) starting\n", tid, (const char *)name);
00061 func = _func;
00062 userdata = _userdata;
00063 running = true;
00064 numrunning++;
00065 }
00066
00067
00068 void WvTask::recycle()
00069 {
00070 if (!running && !recycled)
00071 {
00072 man.free_tasks.append(this, true);
00073 recycled = true;
00074 }
00075 }
00076
00077
00078 WvTaskMan::WvTaskMan()
00079 {
00080 Dprintf("task manager up\n");
00081 current_task = NULL;
00082 magic_number = -WVTASK_MAGIC;
00083
00084 if (setjmp(get_stack_return) == 0)
00085 {
00086
00087 stackmaster();
00088 }
00089
00090 }
00091
00092
00093 WvTaskMan::~WvTaskMan()
00094 {
00095 Dprintf("task manager down\n");
00096 if (WvTask::numrunning != 0)
00097 Dprintf("WARNING! %d tasks still running at WvTaskMan shutdown!\n",
00098 WvTask::numrunning);
00099 magic_number = -42;
00100 }
00101
00102
00103 WvTask *WvTaskMan::start(const WvString &name,
00104 WvTask::TaskFunc *func, void *userdata,
00105 size_t stacksize)
00106 {
00107 WvTask *t;
00108
00109 WvTaskList::Iter i(free_tasks);
00110 for (i.rewind(); i.next(); )
00111 {
00112 if (i().stacksize >= stacksize)
00113 {
00114 t = &i();
00115 i.link->auto_free = false;
00116 i.unlink();
00117 t->recycled = false;
00118 t->start(name, func, userdata);
00119 return t;
00120 }
00121 }
00122
00123
00124 t = new WvTask(*this, stacksize);
00125 t->start(name, func, userdata);
00126 return t;
00127 }
00128
00129
00130 int WvTaskMan::run(WvTask &task, int val)
00131 {
00132 assert(magic_number == -WVTASK_MAGIC);
00133 assert(task.magic_number == WVTASK_MAGIC);
00134 assert(!task.recycled);
00135
00136 if (&task == current_task)
00137 return val;
00138
00139 Dprintf("WvTaskMan: switching to task #%d (%s)\n",
00140 task.tid, (const char *)task.name);
00141
00142 WvTask *old_task = current_task;
00143 current_task = &task;
00144 jmp_buf *state;
00145
00146 if (!old_task)
00147 state = &toplevel;
00148 else
00149 state = &old_task->mystate;
00150
00151 int newval = setjmp(*state);
00152 if (newval == 0)
00153 {
00154
00155 longjmp(task.mystate, val);
00156 }
00157 else
00158 {
00159
00160 current_task = old_task;
00161 return newval;
00162 }
00163 }
00164
00165
00166 int WvTaskMan::yield(int val)
00167 {
00168 if (!current_task)
00169 return 0;
00170
00171 Dprintf("WvTaskMan: yielding from task #%d (%s)\n",
00172 current_task->tid, (const char *)current_task->name);
00173
00174 int newval = setjmp(current_task->mystate);
00175 if (newval == 0)
00176 {
00177
00178 longjmp(toplevel, val);
00179 }
00180 else
00181 {
00182
00183
00184 return newval;
00185 }
00186 }
00187
00188
00189 void WvTaskMan::get_stack(WvTask &task, size_t size)
00190 {
00191 if (setjmp(get_stack_return) == 0)
00192 {
00193 assert(magic_number == -WVTASK_MAGIC);
00194 assert(task.magic_number == WVTASK_MAGIC);
00195
00196
00197 stack_target = &task;
00198 longjmp(stackmaster_task, size/1024 + (size%1024 > 0));
00199 }
00200 else
00201 {
00202 assert(magic_number == -WVTASK_MAGIC);
00203 assert(task.magic_number == WVTASK_MAGIC);
00204
00205
00206 return;
00207 }
00208 }
00209
00210
00211 void WvTaskMan::stackmaster()
00212 {
00213
00214 alloca(1024*1024);
00215
00216 _stackmaster();
00217 }
00218
00219
00220 void WvTaskMan::_stackmaster()
00221 {
00222 int val;
00223
00224 Dprintf("stackmaster 1\n");
00225
00226 for (;;)
00227 {
00228 assert(magic_number == -WVTASK_MAGIC);
00229
00230 Dprintf("stackmaster 2\n");
00231 val = setjmp(stackmaster_task);
00232 if (val == 0)
00233 {
00234 assert(magic_number == -WVTASK_MAGIC);
00235
00236
00237
00238
00239 Dprintf("stackmaster 3\n");
00240 longjmp(get_stack_return, 1);
00241 }
00242 else
00243 {
00244 assert(magic_number == -WVTASK_MAGIC);
00245
00246
00247 do_task();
00248
00249 assert(magic_number == -WVTASK_MAGIC);
00250
00251
00252 alloca(val * (size_t)1024);
00253 }
00254 }
00255 }
00256
00257
00258 void WvTaskMan::do_task()
00259 {
00260 assert(magic_number == -WVTASK_MAGIC);
00261 WvTask *task = stack_target;
00262 assert(task->magic_number == WVTASK_MAGIC);
00263
00264
00265 Dprintf("stackmaster 4\n");
00266 if (setjmp(task->mystate) == 0)
00267 {
00268
00269
00270
00271
00272 Dprintf("stackmaster 5\n");
00273 return;
00274 }
00275 else
00276 {
00277
00278
00279 for (;;)
00280 {
00281 Dprintf("stackmaster 6\n");
00282 assert(magic_number == -WVTASK_MAGIC);
00283 assert(task->magic_number == WVTASK_MAGIC);
00284
00285 if (task->func && task->running)
00286 {
00287 task->func(task->userdata);
00288 task->name = "DEAD";
00289 task->running = false;
00290 task->numrunning--;
00291 }
00292 Dprintf("stackmaster 7\n");
00293 yield();
00294 }
00295 }
00296 }