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;
62int volatile WvTaskMan::magic_number;
63WvTaskList WvTaskMan::all_tasks, WvTaskMan::free_tasks;
64ucontext_t WvTaskMan::stackmaster_task, WvTaskMan::get_stack_return,
66WvTask *WvTaskMan::current_task, *WvTaskMan::stack_target;
67char *WvTaskMan::stacktop;
69static int context_return;
72static bool use_shared_stack()
74 return RUNNING_ON_VALGRIND;
78static void valgrind_fix(
char *stacktop)
80#ifdef HAVE_VALGRIND_MEMCHECK_H
83 assert(stacktop > &val);
85 VALGRIND_MAKE_MEM_DEFINED(&val, stacktop - &val);
89WvTask::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);
116void WvTask::start(
WvStringParm _name, TaskFunc *_func,
void *_userdata)
121 userdata = _userdata;
127void WvTask::recycle()
131 if (!running && !recycled)
133 man.free_tasks.append(
this,
true);
148void WvTaskMan::unlink()
159static 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;
187WvTaskMan::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)
213WvTaskMan::~WvTaskMan()
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);
247int 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;
291int 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);
342void 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);
390void WvTaskMan::stackmaster()
399void 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);
459void 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);
470void 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;
541const 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;
555size_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);
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.