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