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;
62 int volatile WvTaskMan::magic_number;
63 WvTaskList WvTaskMan::all_tasks, WvTaskMan::free_tasks;
64 ucontext_t WvTaskMan::stackmaster_task, WvTaskMan::get_stack_return,
66 WvTask *WvTaskMan::current_task, *WvTaskMan::stack_target;
67 char *WvTaskMan::stacktop;
69 static int context_return;
72 static bool use_shared_stack()
74 return RUNNING_ON_VALGRIND;
78 static void valgrind_fix(
char *stacktop)
80 #ifdef HAVE_VALGRIND_MEMCHECK_H
83 assert(stacktop > &val);
85 VALGRIND_MAKE_MEM_DEFINED(&val, stacktop - &val);
89 WvTask::WvTask(
WvTaskMan &_man,
size_t _stacksize) : man(_man)
91 stacksize = _stacksize;
92 running = recycled =
false;
98 magic_number = WVTASK_MAGIC;
101 man.get_stack(*
this, stacksize);
103 man.all_tasks.append(
this,
false);
116 void WvTask::start(WvStringParm _name, TaskFunc *_func,
void *_userdata)
121 userdata = _userdata;
127 void WvTask::recycle()
131 if (!running && !recycled)
133 man.free_tasks.append(
this,
true);
148 void WvTaskMan::unlink()
159 static inline const char *Yes_No(
bool val)
161 return val?
"Yes":
"No";
166 WvStreamsDebugger::ResultCallback result_cb,
void *)
168 const char *format_str =
"%5s%s%7s%s%8s%s%6s%s%s";
170 result.append(format_str,
"--TID",
"-",
"Running",
"-",
"Recycled",
"-",
"-StkSz",
"-",
"Name-----");
171 result_cb(cmd, result);
172 WvTaskList::Iter i(all_tasks);
173 for (i.rewind(); i.next(); )
176 result.append(format_str, i->tid,
" ",
177 Yes_No(i->running),
" ",
178 Yes_No(i->recycled),
" ",
181 result_cb(cmd, result);
183 return WvString::null;
187 WvTaskMan::WvTaskMan()
189 static bool first =
true;
193 WvStreamsDebugger::add_command(
"tasks", 0, debugger_tasks_run_cb, 0);
198 magic_number = -WVTASK_MAGIC;
200 stacktop = (
char *)alloca(0);
203 assert(getcontext(&get_stack_return) == 0);
204 if (context_return == 0)
213 WvTaskMan::~WvTaskMan()
220 WvTask *WvTaskMan::start(WvStringParm name,
221 WvTask::TaskFunc *func,
void *userdata,
226 WvTaskList::Iter i(free_tasks);
227 for (i.rewind(); i.next(); )
229 if (i().stacksize >= stacksize)
232 i.set_autofree(
false);
235 t->start(name, func, userdata);
241 t =
new WvTask(*
this, stacksize);
242 t->start(name, func, userdata);
247 int WvTaskMan::run(
WvTask &task,
int val)
249 assert(magic_number == -WVTASK_MAGIC);
250 assert(task.magic_number == WVTASK_MAGIC);
251 assert(!task.recycled);
253 Dprintf(
"WvTaskMan: running task #%d with value %d (%s)\n",
254 task.tid, val, (
const char *)task.name);
256 if (&task == current_task)
259 WvTask *old_task = current_task;
260 current_task = &task;
266 state = &old_task->mystate;
269 assert(getcontext(state) == 0);
270 int newval = context_return;
274 context_return = val;
275 setcontext(&task.mystate);
281 VALGRIND_MAKE_MEM_DEFINED(&state,
sizeof(state));
283 if (state != &toplevel)
284 valgrind_fix(stacktop);
285 current_task = old_task;
291 int WvTaskMan::yield(
int val)
296 Dprintf(
"WvTaskMan: yielding from task #%d with value %d (%s)\n",
297 current_task->tid, val, (
const char *)current_task->name);
299 assert(current_task->stack_magic);
302 VALGRIND_MAKE_MEM_DEFINED(current_task->stack_magic,
303 sizeof(current_task->stack_magic));
304 assert(*current_task->stack_magic == WVTASK_MAGIC);
307 if (use_shared_stack())
310 char *stackbottom = (
char *)(current_task->stack_magic + 1);
311 for (stackleft = 0; stackleft < current_task->stacksize; stackleft++)
313 if (stackbottom[stackleft] != 0x42)
316 Dprintf(
"WvTaskMan: remaining stack after #%d (%s): %ld/%ld\n",
317 current_task->tid, current_task->name.
cstr(), (
long)stackleft,
318 (
long)current_task->stacksize);
323 assert(getcontext(¤t_task->mystate) == 0);
324 int newval = context_return;
328 context_return = val;
329 setcontext(&toplevel);
336 valgrind_fix(stacktop);
342 void WvTaskMan::get_stack(
WvTask &task,
size_t size)
345 assert(getcontext(&get_stack_return) == 0);
346 if (context_return == 0)
348 assert(magic_number == -WVTASK_MAGIC);
349 assert(task.magic_number == WVTASK_MAGIC);
351 if (!use_shared_stack())
353 #if defined(__linux__) && (defined(__386__) || defined(__i386) || defined(__i386__))
354 static char *next_stack_addr = (
char *)0xB0000000;
355 static const size_t stack_shift = 0x00100000;
357 next_stack_addr -= stack_shift;
359 static char *next_stack_addr = NULL;
362 task.stack = mmap(next_stack_addr, task.stacksize,
363 PROT_READ | PROT_WRITE,
365 MAP_PRIVATE | MAP_ANONYMOUS,
373 stack_target = &task;
374 context_return = size/1024 + (size%1024 > 0);
375 setcontext(&stackmaster_task);
380 valgrind_fix(stacktop);
381 assert(magic_number == -WVTASK_MAGIC);
382 assert(task.magic_number == WVTASK_MAGIC);
390 void WvTaskMan::stackmaster()
399 void WvTaskMan::_stackmaster()
404 Dprintf(
"stackmaster 1\n");
410 assert(magic_number == -WVTASK_MAGIC);
413 assert(getcontext(&stackmaster_task) == 0);
414 val = context_return;
417 assert(magic_number == -WVTASK_MAGIC);
423 setcontext(&get_stack_return);
427 valgrind_fix(stacktop);
428 assert(magic_number == -WVTASK_MAGIC);
430 total = (val+1) * (
size_t)1024;
432 if (!use_shared_stack())
440 assert(magic_number == -WVTASK_MAGIC);
446 stack_target->stack_magic = (
int *)alloca(
sizeof(
int));
447 *stack_target->stack_magic = WVTASK_MAGIC;
452 memset(stack_target->stack_magic + 1, 0x42, total - 1024);
459 void WvTaskMan::call_func(
WvTask *task)
461 Dprintf(
"WvTaskMan: calling task #%d (%s)\n",
462 task->tid, (
const char *)task->name);
463 task->func(task->userdata);
464 Dprintf(
"WvTaskMan: returning from task #%d (%s)\n",
465 task->tid, (
const char *)task->name);
470 void WvTaskMan::do_task()
472 assert(magic_number == -WVTASK_MAGIC);
473 WvTask *task = stack_target;
474 assert(task->magic_number == WVTASK_MAGIC);
478 assert(getcontext(&task->mystate) == 0);
479 if (context_return == 0)
489 Dprintf(
"stackmaster 5\n");
496 valgrind_fix(stacktop);
499 assert(magic_number == -WVTASK_MAGIC);
501 assert(task->magic_number == WVTASK_MAGIC);
503 if (task->func && task->running)
505 if (use_shared_stack())
510 task->func(task->userdata);
514 assert(getcontext(&task->func_call) == 0);
515 task->func_call.uc_stack.ss_size = task->stacksize;
516 task->func_call.uc_stack.ss_sp = task->stack;
517 task->func_call.uc_stack.ss_flags = 0;
518 task->func_call.uc_link = &task->func_return;
519 Dprintf(
"WvTaskMan: makecontext #%d (%s)\n",
520 task->tid, (
const char *)task->name);
521 makecontext(&task->func_call,
522 (void (*)(
void))call_func, 1, task);
525 assert(getcontext(&task->func_return) == 0);
526 if (context_return == 0)
527 setcontext(&task->func_call);
532 task->running =
false;
541 const void *WvTaskMan::current_top_of_stack()
543 #ifdef HAVE_LIBC_STACK_END
544 extern const void *__libc_stack_end;
545 if (use_shared_stack() || current_task == NULL)
546 return __libc_stack_end;
548 return (
const char *)current_task->stack + current_task->stacksize;
555 size_t WvTaskMan::current_stacksize_limit()
557 if (use_shared_stack() || current_task == NULL)
560 if (getrlimit(RLIMIT_STACK, &rl) == 0)
561 return size_t(rl.rlim_cur);
566 return size_t(current_task->stacksize);