00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "wvautoconf.h"
00010 #ifdef __GNUC__
00011 # define alloca __builtin_alloca
00012 #else
00013 # ifdef _MSC_VER
00014 # include <malloc.h>
00015 # define alloca _alloca
00016 # else
00017 # if HAVE_ALLOCA_H
00018 # include <alloca.h>
00019 # else
00020 # ifdef _AIX
00021 #pragma alloca
00022 # else
00023 # ifndef alloca
00024 char *alloca ();
00025 # endif
00026 # endif
00027 # endif
00028 # endif
00029 #endif
00030
00031 #include "wvtask.h"
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <assert.h>
00035
00036 #ifdef HAVE_VALGRIND_MEMCHECK_H
00037 #include <valgrind/memcheck.h>
00038 #else
00039 #define VALGRIND_MAKE_READABLE(x, y)
00040 #endif
00041
00042 #define TASK_DEBUG 0
00043 #if TASK_DEBUG
00044 # define Dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
00045 #else
00046 # define Dprintf(fmt, args...)
00047 #endif
00048
00049 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
00050
00051 WvTaskMan *WvTaskMan::singleton;
00052 int WvTaskMan::links, WvTaskMan::magic_number;
00053 WvTaskList WvTaskMan::free_tasks;
00054 jmp_buf WvTaskMan::stackmaster_task, WvTaskMan::get_stack_return,
00055 WvTaskMan::toplevel;
00056 WvTask *WvTaskMan::current_task, *WvTaskMan::stack_target;
00057 char *WvTaskMan::stacktop;
00058
00059
00060 static void valgrind_fix(char *stacktop)
00061 {
00062 #ifdef HAVE_VALGRIND_MEMCHECK_H
00063 char val;
00064
00065 assert(stacktop > &val);
00066 #endif
00067 VALGRIND_MAKE_READABLE(&val, stacktop - &val);
00068 }
00069
00070
00071 WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man)
00072 {
00073 stacksize = _stacksize;
00074 running = recycled = false;
00075 func = NULL;
00076 userdata = NULL;
00077
00078 tid = ++taskcount;
00079 numtasks++;
00080 magic_number = WVTASK_MAGIC;
00081 stack_magic = NULL;
00082
00083 man.get_stack(*this, stacksize);
00084 }
00085
00086
00087 WvTask::~WvTask()
00088 {
00089 numtasks--;
00090 if (running)
00091 numrunning--;
00092 magic_number = 42;
00093 }
00094
00095
00096 void WvTask::start(WvStringParm _name, TaskFunc *_func, void *_userdata)
00097 {
00098 assert(!recycled);
00099 name = _name;
00100 func = _func;
00101 userdata = _userdata;
00102 running = true;
00103 numrunning++;
00104 }
00105
00106
00107 void WvTask::recycle()
00108 {
00109 assert(!running);
00110
00111 if (!running && !recycled)
00112 {
00113 man.free_tasks.append(this, true);
00114 recycled = true;
00115 }
00116 }
00117
00118
00119 WvTaskMan *WvTaskMan::get()
00120 {
00121 if (!links)
00122 singleton = new WvTaskMan;
00123 links++;
00124 return singleton;
00125 }
00126
00127
00128 void WvTaskMan::unlink()
00129 {
00130 links--;
00131 if (!links)
00132 {
00133 delete singleton;
00134 singleton = NULL;
00135 }
00136 }
00137
00138
00139 WvTaskMan::WvTaskMan()
00140 {
00141 stack_target = NULL;
00142 current_task = NULL;
00143 magic_number = -WVTASK_MAGIC;
00144
00145 stacktop = (char *)alloca(0);
00146
00147 if (setjmp(get_stack_return) == 0)
00148 {
00149
00150 stackmaster();
00151 }
00152
00153 }
00154
00155
00156 WvTaskMan::~WvTaskMan()
00157 {
00158 magic_number = -42;
00159 free_tasks.zap();
00160 }
00161
00162
00163 WvTask *WvTaskMan::start(WvStringParm name,
00164 WvTask::TaskFunc *func, void *userdata,
00165 size_t stacksize)
00166 {
00167 WvTask *t;
00168
00169 WvTaskList::Iter i(free_tasks);
00170 for (i.rewind(); i.next(); )
00171 {
00172 if (i().stacksize >= stacksize)
00173 {
00174 t = &i();
00175 i.set_autofree(false);
00176 i.unlink();
00177 t->recycled = false;
00178 t->start(name, func, userdata);
00179 return t;
00180 }
00181 }
00182
00183
00184 t = new WvTask(*this, stacksize);
00185 t->start(name, func, userdata);
00186 return t;
00187 }
00188
00189
00190 int WvTaskMan::run(WvTask &task, int val)
00191 {
00192 assert(magic_number == -WVTASK_MAGIC);
00193 assert(task.magic_number == WVTASK_MAGIC);
00194 assert(!task.recycled);
00195
00196 Dprintf("WvTaskMan: running task #%d with value %d (%s)\n",
00197 task.tid, val, (const char *)task.name);
00198
00199 if (&task == current_task)
00200 return val;
00201
00202 WvTask *old_task = current_task;
00203 current_task = &task;
00204 jmp_buf *state;
00205
00206 if (!old_task)
00207 state = &toplevel;
00208 else
00209 state = &old_task->mystate;
00210
00211 int newval = setjmp(*state);
00212 if (newval == 0)
00213 {
00214
00215 longjmp(task.mystate, val);
00216 }
00217 else
00218 {
00219
00220 VALGRIND_MAKE_READABLE(&state, sizeof(state));
00221
00222 if (state != &toplevel)
00223 valgrind_fix(stacktop);
00224 current_task = old_task;
00225 return newval;
00226 }
00227 }
00228
00229
00230 int WvTaskMan::yield(int val)
00231 {
00232 if (!current_task)
00233 return 0;
00234
00235 Dprintf("WvTaskMan: yielding from task #%d with value %d (%s)\n",
00236 current_task->tid, val, (const char *)current_task->name);
00237
00238 assert(current_task->stack_magic);
00239
00240
00241 VALGRIND_MAKE_READABLE(current_task->stack_magic,
00242 sizeof(current_task->stack_magic));
00243 assert(*current_task->stack_magic == WVTASK_MAGIC);
00244
00245 #if TASK_DEBUG
00246 size_t stackleft;
00247 char *stackbottom = (char *)(current_task->stack_magic + 1);
00248 for (stackleft = 0; stackleft < current_task->stacksize; stackleft++)
00249 {
00250 if (stackbottom[stackleft] != 0x42)
00251 break;
00252 }
00253 Dprintf("WvTaskMan: remaining stack after #%d (%s): %ld/%ld\n",
00254 current_task->tid, current_task->name.cstr(), (long)stackleft,
00255 (long)current_task->stacksize);
00256 #endif
00257
00258 int newval = setjmp(current_task->mystate);
00259 if (newval == 0)
00260 {
00261
00262 longjmp(toplevel, val);
00263 }
00264 else
00265 {
00266
00267
00268 valgrind_fix(stacktop);
00269 return newval;
00270 }
00271 }
00272
00273
00274 void WvTaskMan::get_stack(WvTask &task, size_t size)
00275 {
00276 if (setjmp(get_stack_return) == 0)
00277 {
00278 assert(magic_number == -WVTASK_MAGIC);
00279 assert(task.magic_number == WVTASK_MAGIC);
00280
00281
00282 stack_target = &task;
00283 longjmp(stackmaster_task, size/1024 + (size%1024 > 0));
00284 }
00285 else
00286 {
00287 if (current_task)
00288 valgrind_fix(stacktop);
00289 assert(magic_number == -WVTASK_MAGIC);
00290 assert(task.magic_number == WVTASK_MAGIC);
00291
00292
00293 return;
00294 }
00295 }
00296
00297
00298 void WvTaskMan::stackmaster()
00299 {
00300
00301 alloca(1024*1024);
00302
00303 _stackmaster();
00304 }
00305
00306
00307 void WvTaskMan::_stackmaster()
00308 {
00309 int val;
00310 size_t total;
00311
00312 Dprintf("stackmaster 1\n");
00313
00314
00315
00316 for (;;)
00317 {
00318 assert(magic_number == -WVTASK_MAGIC);
00319
00320 val = setjmp(stackmaster_task);
00321 if (val == 0)
00322 {
00323 assert(magic_number == -WVTASK_MAGIC);
00324
00325
00326
00327
00328 longjmp(get_stack_return, 1);
00329 }
00330 else
00331 {
00332 valgrind_fix(stacktop);
00333 assert(magic_number == -WVTASK_MAGIC);
00334
00335
00336
00337 do_task();
00338
00339 assert(magic_number == -WVTASK_MAGIC);
00340
00341
00342 total = (val+1) * (size_t)1024;
00343 alloca(total);
00344
00345
00346 stack_target->stack_magic = (int *)alloca(sizeof(int));
00347 *stack_target->stack_magic = WVTASK_MAGIC;
00348
00349
00350
00351 #if TASK_DEBUG
00352 memset(stack_target->stack_magic + 1, 0x42, total - 1024);
00353 #endif
00354 }
00355 }
00356 }
00357
00358
00359 void WvTaskMan::do_task()
00360 {
00361 assert(magic_number == -WVTASK_MAGIC);
00362 WvTask *task = stack_target;
00363 assert(task->magic_number == WVTASK_MAGIC);
00364
00365
00366 if (setjmp(task->mystate) == 0)
00367 {
00368
00369
00370
00371
00372
00373
00374
00375
00376 Dprintf("stackmaster 5\n");
00377 return;
00378 }
00379 else
00380 {
00381
00382
00383 valgrind_fix(stacktop);
00384 for (;;)
00385 {
00386 assert(magic_number == -WVTASK_MAGIC);
00387 assert(task);
00388 assert(task->magic_number == WVTASK_MAGIC);
00389
00390 if (task->func && task->running)
00391 {
00392
00393
00394
00395 task->func(task->userdata);
00396
00397
00398 task->name = "DEAD";
00399 task->running = false;
00400 task->numrunning--;
00401 }
00402 yield();
00403 }
00404 }
00405 }