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 }