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