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 #include <sys/mman.h>
00036 #include <signal.h>
00037 #include <unistd.h>
00038 #include <sys/resource.h>
00039
00040 #ifdef HAVE_VALGRIND_MEMCHECK_H
00041 #include <valgrind/memcheck.h>
00042 #else
00043 #define VALGRIND_MAKE_READABLE(x, y)
00044 #define RUNNING_ON_VALGRIND 0
00045 #endif
00046
00047 #define TASK_DEBUG 0
00048 #if TASK_DEBUG
00049 # define Dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
00050 #else
00051 # define Dprintf(fmt, args...)
00052 #endif
00053
00054 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
00055
00056 WvTaskMan *WvTaskMan::singleton;
00057 int WvTaskMan::links, WvTaskMan::magic_number;
00058 WvTaskList WvTaskMan::all_tasks, WvTaskMan::free_tasks;
00059 ucontext_t WvTaskMan::stackmaster_task, WvTaskMan::get_stack_return,
00060 WvTaskMan::toplevel;
00061 WvTask *WvTaskMan::current_task, *WvTaskMan::stack_target;
00062 char *WvTaskMan::stacktop;
00063
00064 static int context_return;
00065
00066
00067 static bool use_shared_stack()
00068 {
00069 return RUNNING_ON_VALGRIND;
00070 }
00071
00072
00073 static void valgrind_fix(char *stacktop)
00074 {
00075 #ifdef HAVE_VALGRIND_MEMCHECK_H
00076 char val;
00077
00078 assert(stacktop > &val);
00079 #endif
00080 VALGRIND_MAKE_READABLE(&val, stacktop - &val);
00081 }
00082
00083
00084 WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man)
00085 {
00086 stacksize = _stacksize;
00087 running = recycled = false;
00088 func = NULL;
00089 userdata = NULL;
00090
00091 tid = ++taskcount;
00092 numtasks++;
00093 magic_number = WVTASK_MAGIC;
00094 stack_magic = NULL;
00095
00096 man.get_stack(*this, stacksize);
00097
00098 man.all_tasks.append(this, false);
00099 }
00100
00101
00102 WvTask::~WvTask()
00103 {
00104 numtasks--;
00105 if (running)
00106 numrunning--;
00107 magic_number = 42;
00108 }
00109
00110
00111 void WvTask::start(WvStringParm _name, TaskFunc *_func, void *_userdata)
00112 {
00113 assert(!recycled);
00114 name = _name;
00115 func = _func;
00116 userdata = _userdata;
00117 running = true;
00118 numrunning++;
00119 }
00120
00121
00122 void WvTask::recycle()
00123 {
00124 assert(!running);
00125
00126 if (!running && !recycled)
00127 {
00128 man.free_tasks.append(this, true);
00129 recycled = true;
00130 }
00131 }
00132
00133
00134 WvTaskMan *WvTaskMan::get()
00135 {
00136 if (!links)
00137 singleton = new WvTaskMan;
00138 links++;
00139 return singleton;
00140 }
00141
00142
00143 void WvTaskMan::unlink()
00144 {
00145 links--;
00146 if (!links)
00147 {
00148 delete singleton;
00149 singleton = NULL;
00150 }
00151 }
00152
00153
00154 static inline const char *Yes_No(bool val)
00155 {
00156 return val? "Yes": "No";
00157 }
00158
00159
00160 WvString WvTaskMan::debugger_tasks_run_cb(WvStringParm cmd, WvStringList &args,
00161 WvStreamsDebugger::ResultCallback result_cb, void *)
00162 {
00163 const char *format_str = "%5s%s%7s%s%8s%s%6s%s%s";
00164 WvStringList result;
00165 result.append(format_str, "--TID", "-", "Running", "-", "Recycled", "-", "-StkSz", "-", "Name-----");
00166 result_cb(cmd, result);
00167 WvTaskList::Iter i(all_tasks);
00168 for (i.rewind(); i.next(); )
00169 {
00170 result.zap();
00171 result.append(format_str, i->tid, " ",
00172 Yes_No(i->running), " ",
00173 Yes_No(i->recycled), " ",
00174 i->stacksize, " ",
00175 i->name);
00176 result_cb(cmd, result);
00177 }
00178 return WvString::null;
00179 }
00180
00181
00182 WvTaskMan::WvTaskMan()
00183 {
00184 static bool first = true;
00185 if (first)
00186 {
00187 first = false;
00188 WvStreamsDebugger::add_command("tasks", 0, debugger_tasks_run_cb, 0);
00189 }
00190
00191 stack_target = NULL;
00192 current_task = NULL;
00193 magic_number = -WVTASK_MAGIC;
00194
00195 stacktop = (char *)alloca(0);
00196
00197 context_return = 0;
00198 assert(getcontext(&get_stack_return) == 0);
00199 if (context_return == 0)
00200 {
00201
00202 stackmaster();
00203 }
00204
00205 }
00206
00207
00208 WvTaskMan::~WvTaskMan()
00209 {
00210 magic_number = -42;
00211 free_tasks.zap();
00212 }
00213
00214
00215 WvTask *WvTaskMan::start(WvStringParm name,
00216 WvTask::TaskFunc *func, void *userdata,
00217 size_t stacksize)
00218 {
00219 WvTask *t;
00220
00221 WvTaskList::Iter i(free_tasks);
00222 for (i.rewind(); i.next(); )
00223 {
00224 if (i().stacksize >= stacksize)
00225 {
00226 t = &i();
00227 i.set_autofree(false);
00228 i.unlink();
00229 t->recycled = false;
00230 t->start(name, func, userdata);
00231 return t;
00232 }
00233 }
00234
00235
00236 t = new WvTask(*this, stacksize);
00237 t->start(name, func, userdata);
00238 return t;
00239 }
00240
00241
00242 int WvTaskMan::run(WvTask &task, int val)
00243 {
00244 assert(magic_number == -WVTASK_MAGIC);
00245 assert(task.magic_number == WVTASK_MAGIC);
00246 assert(!task.recycled);
00247
00248 Dprintf("WvTaskMan: running task #%d with value %d (%s)\n",
00249 task.tid, val, (const char *)task.name);
00250
00251 if (&task == current_task)
00252 return val;
00253
00254 WvTask *old_task = current_task;
00255 current_task = &task;
00256 ucontext_t *state;
00257
00258 if (!old_task)
00259 state = &toplevel;
00260 else
00261 state = &old_task->mystate;
00262
00263 context_return = 0;
00264 assert(getcontext(state) == 0);
00265 int newval = context_return;
00266 if (newval == 0)
00267 {
00268
00269 context_return = val;
00270 setcontext(&task.mystate);
00271 return -1;
00272 }
00273 else
00274 {
00275
00276 VALGRIND_MAKE_READABLE(&state, sizeof(state));
00277
00278 if (state != &toplevel)
00279 valgrind_fix(stacktop);
00280 current_task = old_task;
00281 return newval;
00282 }
00283 }
00284
00285
00286 int WvTaskMan::yield(int val)
00287 {
00288 if (!current_task)
00289 return 0;
00290
00291 Dprintf("WvTaskMan: yielding from task #%d with value %d (%s)\n",
00292 current_task->tid, val, (const char *)current_task->name);
00293
00294 assert(current_task->stack_magic);
00295
00296
00297 VALGRIND_MAKE_READABLE(current_task->stack_magic,
00298 sizeof(current_task->stack_magic));
00299 assert(*current_task->stack_magic == WVTASK_MAGIC);
00300
00301 #if TASK_DEBUG
00302 if (use_shared_stack())
00303 {
00304 size_t stackleft;
00305 char *stackbottom = (char *)(current_task->stack_magic + 1);
00306 for (stackleft = 0; stackleft < current_task->stacksize; stackleft++)
00307 {
00308 if (stackbottom[stackleft] != 0x42)
00309 break;
00310 }
00311 Dprintf("WvTaskMan: remaining stack after #%d (%s): %ld/%ld\n",
00312 current_task->tid, current_task->name.cstr(), (long)stackleft,
00313 (long)current_task->stacksize);
00314 }
00315 #endif
00316
00317 context_return = 0;
00318 assert(getcontext(¤t_task->mystate) == 0);
00319 int newval = context_return;
00320 if (newval == 0)
00321 {
00322
00323 context_return = val;
00324 setcontext(&toplevel);
00325 return -1;
00326 }
00327 else
00328 {
00329
00330
00331 valgrind_fix(stacktop);
00332 return newval;
00333 }
00334 }
00335
00336
00337 void WvTaskMan::get_stack(WvTask &task, size_t size)
00338 {
00339 context_return = 0;
00340 assert(getcontext(&get_stack_return) == 0);
00341 if (context_return == 0)
00342 {
00343 assert(magic_number == -WVTASK_MAGIC);
00344 assert(task.magic_number == WVTASK_MAGIC);
00345
00346 if (!use_shared_stack())
00347 {
00348 #if defined(__linux__) && (defined(__386__) || defined(__i386) || defined(__i386__))
00349 static char *next_stack_addr = (char *)0xB0000000;
00350 static const size_t stack_shift = 0x00100000;
00351
00352 next_stack_addr -= stack_shift;
00353 #else
00354 static char *next_stack_addr = NULL;
00355 #endif
00356
00357 task.stack = mmap(next_stack_addr, task.stacksize,
00358 PROT_READ | PROT_WRITE,
00359 MAP_PRIVATE | MAP_ANONYMOUS,
00360 -1, 0);
00361 }
00362
00363
00364 stack_target = &task;
00365 context_return = size/1024 + (size%1024 > 0);
00366 setcontext(&stackmaster_task);
00367 }
00368 else
00369 {
00370 if (current_task)
00371 valgrind_fix(stacktop);
00372 assert(magic_number == -WVTASK_MAGIC);
00373 assert(task.magic_number == WVTASK_MAGIC);
00374
00375
00376 return;
00377 }
00378 }
00379
00380
00381 void WvTaskMan::stackmaster()
00382 {
00383
00384 alloca(1024*1024);
00385
00386 _stackmaster();
00387 }
00388
00389
00390 void WvTaskMan::_stackmaster()
00391 {
00392 int val;
00393 size_t total;
00394
00395 Dprintf("stackmaster 1\n");
00396
00397
00398
00399 for (;;)
00400 {
00401 assert(magic_number == -WVTASK_MAGIC);
00402
00403 context_return = 0;
00404 assert(getcontext(&stackmaster_task) == 0);
00405 val = context_return;
00406 if (val == 0)
00407 {
00408 assert(magic_number == -WVTASK_MAGIC);
00409
00410
00411
00412
00413 context_return = 1;
00414 setcontext(&get_stack_return);
00415 }
00416 else
00417 {
00418 valgrind_fix(stacktop);
00419 assert(magic_number == -WVTASK_MAGIC);
00420
00421 total = (val+1) * (size_t)1024;
00422
00423 if (!use_shared_stack())
00424 total = 1024;
00425
00426
00427
00428
00429 do_task();
00430
00431 assert(magic_number == -WVTASK_MAGIC);
00432
00433
00434 alloca(total);
00435
00436
00437 stack_target->stack_magic = (int *)alloca(sizeof(int));
00438 *stack_target->stack_magic = WVTASK_MAGIC;
00439
00440
00441
00442 #if TASK_DEBUG
00443 memset(stack_target->stack_magic + 1, 0x42, total - 1024);
00444 #endif
00445 }
00446 }
00447 }
00448
00449
00450 void WvTaskMan::call_func(WvTask *task)
00451 {
00452 Dprintf("WvTaskMan: calling task #%d (%s)\n",
00453 task->tid, (const char *)task->name);
00454 task->func(task->userdata);
00455 Dprintf("WvTaskMan: returning from task #%d (%s)\n",
00456 task->tid, (const char *)task->name);
00457 context_return = 1;
00458 }
00459
00460
00461 void WvTaskMan::do_task()
00462 {
00463 assert(magic_number == -WVTASK_MAGIC);
00464 WvTask *task = stack_target;
00465 assert(task->magic_number == WVTASK_MAGIC);
00466
00467
00468 context_return = 0;
00469 assert(getcontext(&task->mystate) == 0);
00470 if (context_return == 0)
00471 {
00472
00473
00474
00475
00476
00477
00478
00479
00480 Dprintf("stackmaster 5\n");
00481 return;
00482 }
00483 else
00484 {
00485
00486
00487 valgrind_fix(stacktop);
00488 for (;;)
00489 {
00490 assert(magic_number == -WVTASK_MAGIC);
00491 assert(task);
00492 assert(task->magic_number == WVTASK_MAGIC);
00493
00494 if (task->func && task->running)
00495 {
00496 if (use_shared_stack())
00497 {
00498
00499
00500
00501 task->func(task->userdata);
00502 }
00503 else
00504 {
00505 assert(getcontext(&task->func_call) == 0);
00506 task->func_call.uc_stack.ss_size = task->stacksize;
00507 task->func_call.uc_stack.ss_sp = task->stack;
00508 task->func_call.uc_stack.ss_flags = 0;
00509 task->func_call.uc_link = &task->func_return;
00510 Dprintf("WvTaskMan: makecontext #%d (%s)\n",
00511 task->tid, (const char *)task->name);
00512 makecontext(&task->func_call,
00513 (void (*)(void))call_func, 1, task);
00514
00515 context_return = 0;
00516 assert(getcontext(&task->func_return) == 0);
00517 if (context_return == 0)
00518 setcontext(&task->func_call);
00519 }
00520
00521
00522 task->name = "DEAD";
00523 task->running = false;
00524 task->numrunning--;
00525 }
00526 yield();
00527 }
00528 }
00529 }
00530
00531
00532 const void *WvTaskMan::current_top_of_stack()
00533 {
00534 extern const void *__libc_stack_end;
00535 if (use_shared_stack() || current_task == NULL)
00536 return __libc_stack_end;
00537 else
00538 return (const char *)current_task->stack + current_task->stacksize;
00539 }
00540
00541
00542 size_t WvTaskMan::current_stacksize_limit()
00543 {
00544 if (use_shared_stack() || current_task == NULL)
00545 {
00546 struct rlimit rl;
00547 if (getrlimit(RLIMIT_STACK, &rl) == 0)
00548 return size_t(rl.rlim_cur);
00549 else
00550 return 0;
00551 }
00552 else
00553 return size_t(current_task->stacksize);
00554 }
00555
00556