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...)
58int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
61int WvTaskMan::links, WvTaskMan::magic_number;
62WvTaskList WvTaskMan::all_tasks, WvTaskMan::free_tasks;
63ucontext_t WvTaskMan::stackmaster_task, WvTaskMan::get_stack_return,
65WvTask *WvTaskMan::current_task, *WvTaskMan::stack_target;
66char *WvTaskMan::stacktop;
68static int context_return;
71static int assert_getcontext(ucontext_t *__ucp)
75 result = getcontext(__ucp);
81static bool use_shared_stack()
83 return RUNNING_ON_VALGRIND;
87static void valgrind_fix(
char *stacktop)
89#ifdef HAVE_VALGRIND_MEMCHECK_H
92 assert(stacktop > &val);
94 VALGRIND_MAKE_MEM_DEFINED(&val, stacktop - &val);
98WvTask::WvTask(
WvTaskMan &_man,
size_t _stacksize) : man(_man)
100 stacksize = _stacksize;
101 running = recycled =
false;
107 magic_number = WVTASK_MAGIC;
110 man.get_stack(*
this, stacksize);
112 man.all_tasks.append(
this,
false);
125void WvTask::start(
WvStringParm _name, TaskFunc *_func,
void *_userdata)
130 userdata = _userdata;
136void WvTask::recycle()
140 if (!running && !recycled)
142 man.free_tasks.append(
this,
true);
157void WvTaskMan::unlink()
168static inline const char *Yes_No(
bool val)
170 return val?
"Yes":
"No";
175 WvStreamsDebugger::ResultCallback result_cb,
void *)
177 const char *format_str =
"%5s%s%7s%s%8s%s%6s%s%s";
179 result.append(format_str,
"--TID",
"-",
"Running",
"-",
"Recycled",
"-",
"-StkSz",
"-",
"Name-----");
180 result_cb(cmd, result);
181 WvTaskList::Iter i(all_tasks);
182 for (i.rewind(); i.next(); )
185 result.append(format_str, i->tid,
" ",
186 Yes_No(i->running),
" ",
187 Yes_No(i->recycled),
" ",
190 result_cb(cmd, result);
192 return WvString::null;
196WvTaskMan::WvTaskMan()
198 static bool first =
true;
202 WvStreamsDebugger::add_command(
"tasks", 0, debugger_tasks_run_cb, 0);
207 magic_number = -WVTASK_MAGIC;
209 stacktop = (
char *)alloca(0);
212 assert_getcontext(&get_stack_return);
213 if (context_return == 0)
222WvTaskMan::~WvTaskMan()
230 WvTask::TaskFunc *func,
void *userdata,
235 WvTaskList::Iter i(free_tasks);
236 for (i.rewind(); i.next(); )
238 if (i().stacksize >= stacksize)
241 i.set_autofree(
false);
244 t->start(name, func, userdata);
250 t =
new WvTask(*
this, stacksize);
251 t->start(name, func, userdata);
256int WvTaskMan::run(
WvTask &task,
int val)
258 assert(magic_number == -WVTASK_MAGIC);
259 assert(task.magic_number == WVTASK_MAGIC);
260 assert(!task.recycled);
262 Dprintf(
"WvTaskMan: running task #%d with value %d (%s)\n",
263 task.tid, val, (
const char *)task.name);
265 if (&task == current_task)
268 WvTask *old_task = current_task;
269 current_task = &task;
275 state = &old_task->mystate;
278 assert_getcontext(state);
279 int newval = context_return;
283 context_return = val;
284 setcontext(&task.mystate);
290 VALGRIND_MAKE_MEM_DEFINED(&state,
sizeof(state));
292 if (state != &toplevel)
293 valgrind_fix(stacktop);
294 current_task = old_task;
300int WvTaskMan::yield(
int val)
305 Dprintf(
"WvTaskMan: yielding from task #%d with value %d (%s)\n",
306 current_task->tid, val, (
const char *)current_task->name);
308 assert(current_task->stack_magic);
311 VALGRIND_MAKE_MEM_DEFINED(current_task->stack_magic,
312 sizeof(current_task->stack_magic));
313 assert(*current_task->stack_magic == WVTASK_MAGIC);
316 if (use_shared_stack())
319 char *stackbottom = (
char *)(current_task->stack_magic + 1);
320 for (stackleft = 0; stackleft < current_task->stacksize; stackleft++)
322 if (stackbottom[stackleft] != 0x42)
325 Dprintf(
"WvTaskMan: remaining stack after #%d (%s): %ld/%ld\n",
326 current_task->tid, current_task->name.
cstr(), (
long)stackleft,
327 (
long)current_task->stacksize);
332 assert_getcontext(¤t_task->mystate);
333 int newval = context_return;
337 context_return = val;
338 setcontext(&toplevel);
345 valgrind_fix(stacktop);
351void WvTaskMan::get_stack(
WvTask &task,
size_t size)
354 assert_getcontext(&get_stack_return);
355 if (context_return == 0)
357 assert(magic_number == -WVTASK_MAGIC);
358 assert(task.magic_number == WVTASK_MAGIC);
360 if (!use_shared_stack())
362#if defined(__linux__) && (defined(__386__) || defined(__i386) || defined(__i386__))
363 static char *next_stack_addr = (
char *)0xB0000000;
364 static const size_t stack_shift = 0x00100000;
366 next_stack_addr -= stack_shift;
368 static char *next_stack_addr = NULL;
371 task.stack = mmap(next_stack_addr, task.stacksize,
372 PROT_READ | PROT_WRITE,
374 MAP_PRIVATE | MAP_ANONYMOUS,
382 stack_target = &task;
383 context_return = size/1024 + (size%1024 > 0);
384 setcontext(&stackmaster_task);
389 valgrind_fix(stacktop);
390 assert(magic_number == -WVTASK_MAGIC);
391 assert(task.magic_number == WVTASK_MAGIC);
399void WvTaskMan::stackmaster()
408void WvTaskMan::_stackmaster()
413 Dprintf(
"stackmaster 1\n");
419 assert(magic_number == -WVTASK_MAGIC);
422 assert_getcontext(&stackmaster_task);
423 val = context_return;
426 assert(magic_number == -WVTASK_MAGIC);
432 setcontext(&get_stack_return);
436 valgrind_fix(stacktop);
437 assert(magic_number == -WVTASK_MAGIC);
439 total = (val+1) * (
size_t)1024;
441 if (!use_shared_stack())
449 assert(magic_number == -WVTASK_MAGIC);
455 stack_target->stack_magic = (
int *)alloca(
sizeof(
int));
456 *stack_target->stack_magic = WVTASK_MAGIC;
461 memset(stack_target->stack_magic + 1, 0x42, total - 1024);
468void WvTaskMan::call_func(
WvTask *task)
470 Dprintf(
"WvTaskMan: calling task #%d (%s)\n",
471 task->tid, (
const char *)task->name);
472 task->func(task->userdata);
473 Dprintf(
"WvTaskMan: returning from task #%d (%s)\n",
474 task->tid, (
const char *)task->name);
479void WvTaskMan::do_task()
481 assert(magic_number == -WVTASK_MAGIC);
482 WvTask *task = stack_target;
483 assert(task->magic_number == WVTASK_MAGIC);
487 assert_getcontext(&task->mystate);
488 if (context_return == 0)
498 Dprintf(
"stackmaster 5\n");
505 valgrind_fix(stacktop);
508 assert(magic_number == -WVTASK_MAGIC);
510 assert(task->magic_number == WVTASK_MAGIC);
512 if (task->func && task->running)
514 if (use_shared_stack())
519 task->func(task->userdata);
523 assert_getcontext(&task->func_call);
524 task->func_call.uc_stack.ss_size = task->stacksize;
525 task->func_call.uc_stack.ss_sp = task->stack;
526 task->func_call.uc_stack.ss_flags = 0;
527 task->func_call.uc_link = &task->func_return;
528 Dprintf(
"WvTaskMan: makecontext #%d (%s)\n",
529 task->tid, (
const char *)task->name);
530 makecontext(&task->func_call,
531 (void (*)(
void))call_func, 1, task);
534 assert_getcontext(&task->func_return);
535 if (context_return == 0)
536 setcontext(&task->func_call);
541 task->running =
false;
550const void *WvTaskMan::current_top_of_stack()
552#ifdef HAVE_LIBC_STACK_END
553 extern const void *__libc_stack_end;
554 if (use_shared_stack() || current_task == NULL)
555 return __libc_stack_end;
557 return (
const char *)current_task->stack + current_task->stacksize;
564size_t WvTaskMan::current_stacksize_limit()
566 if (use_shared_stack() || current_task == NULL)
569 if (getrlimit(RLIMIT_STACK, &rl) == 0)
570 return size_t(rl.rlim_cur);
575 return size_t(current_task->stacksize);
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
const char * cstr() const
return a (const char *) for this string.
This is a WvList of WvStrings, and is a really handy way to parse strings.
WvString is an implementation of a simple and efficient printable-string class.
Provides co-operative multitasking support among WvTask instances.
static WvTaskMan * get()
get/dereference the singleton global WvTaskMan
Represents a single thread of control.