9 #include "wvautoconf.h"
11 # define alloca __builtin_alloca
15 # define alloca _alloca
38 #include <sys/resource.h>
40 #ifdef HAVE_VALGRIND_MEMCHECK_H
41 #include <valgrind/memcheck.h>
43 #ifndef VALGRIND_MAKE_MEM_DEFINED
44 #define VALGRIND_MAKE_MEM_DEFINED VALGRIND_MAKE_READABLE
47 #define VALGRIND_MAKE_MEM_DEFINED(x, y)
48 #define RUNNING_ON_VALGRIND 0
53 # define Dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
55 # define Dprintf(fmt, args...)
58 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
61 int WvTaskMan::links, WvTaskMan::magic_number;
62 WvTaskList WvTaskMan::all_tasks, WvTaskMan::free_tasks;
63 ucontext_t WvTaskMan::stackmaster_task, WvTaskMan::get_stack_return,
65 WvTask *WvTaskMan::current_task, *WvTaskMan::stack_target;
66 char *WvTaskMan::stacktop;
68 static int context_return;
71 static bool use_shared_stack()
73 return RUNNING_ON_VALGRIND;
77 static void valgrind_fix(
char *stacktop)
79 #ifdef HAVE_VALGRIND_MEMCHECK_H
82 assert(stacktop > &val);
84 VALGRIND_MAKE_MEM_DEFINED(&val, stacktop - &val);
88 WvTask::WvTask(
WvTaskMan &_man,
size_t _stacksize) : man(_man)
90 stacksize = _stacksize;
91 running = recycled =
false;
97 magic_number = WVTASK_MAGIC;
100 man.get_stack(*
this, stacksize);
102 man.all_tasks.append(
this,
false);
115 void WvTask::start(
WvStringParm _name, TaskFunc *_func,
void *_userdata)
120 userdata = _userdata;
126 void WvTask::recycle()
130 if (!running && !recycled)
132 man.free_tasks.append(
this,
true);
147 void WvTaskMan::unlink()
158 static inline const char *Yes_No(
bool val)
160 return val?
"Yes":
"No";
165 WvStreamsDebugger::ResultCallback result_cb,
void *)
167 const char *format_str =
"%5s%s%7s%s%8s%s%6s%s%s";
169 result.append(format_str,
"--TID",
"-",
"Running",
"-",
"Recycled",
"-",
"-StkSz",
"-",
"Name-----");
170 result_cb(cmd, result);
171 WvTaskList::Iter i(all_tasks);
172 for (i.rewind(); i.next(); )
175 result.append(format_str, i->tid,
" ",
176 Yes_No(i->running),
" ",
177 Yes_No(i->recycled),
" ",
180 result_cb(cmd, result);
182 return WvString::null;
186 WvTaskMan::WvTaskMan()
188 static bool first =
true;
192 WvStreamsDebugger::add_command(
"tasks", 0, debugger_tasks_run_cb, 0);
197 magic_number = -WVTASK_MAGIC;
199 stacktop = (
char *)alloca(0);
202 assert(getcontext(&get_stack_return) == 0);
203 if (context_return == 0)
212 WvTaskMan::~WvTaskMan()
220 WvTask::TaskFunc *func,
void *userdata,
225 WvTaskList::Iter i(free_tasks);
226 for (i.rewind(); i.next(); )
228 if (i().stacksize >= stacksize)
231 i.set_autofree(
false);
234 t->start(name, func, userdata);
240 t =
new WvTask(*
this, stacksize);
241 t->start(name, func, userdata);
246 int WvTaskMan::run(
WvTask &task,
int val)
248 assert(magic_number == -WVTASK_MAGIC);
249 assert(task.magic_number == WVTASK_MAGIC);
250 assert(!task.recycled);
252 Dprintf(
"WvTaskMan: running task #%d with value %d (%s)\n",
253 task.tid, val, (
const char *)task.name);
255 if (&task == current_task)
258 WvTask *old_task = current_task;
259 current_task = &task;
265 state = &old_task->mystate;
268 assert(getcontext(state) == 0);
269 int newval = context_return;
273 context_return = val;
274 setcontext(&task.mystate);
280 VALGRIND_MAKE_MEM_DEFINED(&state,
sizeof(state));
282 if (state != &toplevel)
283 valgrind_fix(stacktop);
284 current_task = old_task;
290 int WvTaskMan::yield(
int val)
295 Dprintf(
"WvTaskMan: yielding from task #%d with value %d (%s)\n",
296 current_task->tid, val, (
const char *)current_task->name);
298 assert(current_task->stack_magic);
301 VALGRIND_MAKE_MEM_DEFINED(current_task->stack_magic,
302 sizeof(current_task->stack_magic));
303 assert(*current_task->stack_magic == WVTASK_MAGIC);
306 if (use_shared_stack())
309 char *stackbottom = (
char *)(current_task->stack_magic + 1);
310 for (stackleft = 0; stackleft < current_task->stacksize; stackleft++)
312 if (stackbottom[stackleft] != 0x42)
315 Dprintf(
"WvTaskMan: remaining stack after #%d (%s): %ld/%ld\n",
316 current_task->tid, current_task->name.
cstr(), (
long)stackleft,
317 (
long)current_task->stacksize);
322 assert(getcontext(¤t_task->mystate) == 0);
323 int newval = context_return;
327 context_return = val;
328 setcontext(&toplevel);
335 valgrind_fix(stacktop);
341 void WvTaskMan::get_stack(
WvTask &task,
size_t size)
344 assert(getcontext(&get_stack_return) == 0);
345 if (context_return == 0)
347 assert(magic_number == -WVTASK_MAGIC);
348 assert(task.magic_number == WVTASK_MAGIC);
350 if (!use_shared_stack())
352 #if defined(__linux__) && (defined(__386__) || defined(__i386) || defined(__i386__))
353 static char *next_stack_addr = (
char *)0xB0000000;
354 static const size_t stack_shift = 0x00100000;
356 next_stack_addr -= stack_shift;
358 static char *next_stack_addr = NULL;
361 task.stack = mmap(next_stack_addr, task.stacksize,
362 PROT_READ | PROT_WRITE,
364 MAP_PRIVATE | MAP_ANONYMOUS,
372 stack_target = &task;
373 context_return = size/1024 + (size%1024 > 0);
374 setcontext(&stackmaster_task);
379 valgrind_fix(stacktop);
380 assert(magic_number == -WVTASK_MAGIC);
381 assert(task.magic_number == WVTASK_MAGIC);
389 void WvTaskMan::stackmaster()
398 void WvTaskMan::_stackmaster()
403 Dprintf(
"stackmaster 1\n");
409 assert(magic_number == -WVTASK_MAGIC);
412 assert(getcontext(&stackmaster_task) == 0);
413 val = context_return;
416 assert(magic_number == -WVTASK_MAGIC);
422 setcontext(&get_stack_return);
426 valgrind_fix(stacktop);
427 assert(magic_number == -WVTASK_MAGIC);
429 total = (val+1) * (
size_t)1024;
431 if (!use_shared_stack())
439 assert(magic_number == -WVTASK_MAGIC);
445 stack_target->stack_magic = (
int *)alloca(
sizeof(
int));
446 *stack_target->stack_magic = WVTASK_MAGIC;
451 memset(stack_target->stack_magic + 1, 0x42, total - 1024);
458 void WvTaskMan::call_func(
WvTask *task)
460 Dprintf(
"WvTaskMan: calling task #%d (%s)\n",
461 task->tid, (
const char *)task->name);
462 task->func(task->userdata);
463 Dprintf(
"WvTaskMan: returning from task #%d (%s)\n",
464 task->tid, (
const char *)task->name);
469 void WvTaskMan::do_task()
471 assert(magic_number == -WVTASK_MAGIC);
472 WvTask *task = stack_target;
473 assert(task->magic_number == WVTASK_MAGIC);
477 assert(getcontext(&task->mystate) == 0);
478 if (context_return == 0)
488 Dprintf(
"stackmaster 5\n");
495 valgrind_fix(stacktop);
498 assert(magic_number == -WVTASK_MAGIC);
500 assert(task->magic_number == WVTASK_MAGIC);
502 if (task->func && task->running)
504 if (use_shared_stack())
509 task->func(task->userdata);
513 assert(getcontext(&task->func_call) == 0);
514 task->func_call.uc_stack.ss_size = task->stacksize;
515 task->func_call.uc_stack.ss_sp = task->stack;
516 task->func_call.uc_stack.ss_flags = 0;
517 task->func_call.uc_link = &task->func_return;
518 Dprintf(
"WvTaskMan: makecontext #%d (%s)\n",
519 task->tid, (
const char *)task->name);
520 makecontext(&task->func_call,
521 (void (*)(
void))call_func, 1, task);
524 assert(getcontext(&task->func_return) == 0);
525 if (context_return == 0)
526 setcontext(&task->func_call);
531 task->running =
false;
540 const void *WvTaskMan::current_top_of_stack()
542 #ifdef HAVE_LIBC_STACK_END
543 extern const void *__libc_stack_end;
544 if (use_shared_stack() || current_task == NULL)
545 return __libc_stack_end;
547 return (
const char *)current_task->stack + current_task->stacksize;
554 size_t WvTaskMan::current_stacksize_limit()
556 if (use_shared_stack() || current_task == NULL)
559 if (getrlimit(RLIMIT_STACK, &rl) == 0)
560 return size_t(rl.rlim_cur);
565 return size_t(current_task->stacksize);