Fix several warnings that appear in gcc 4.3.2.
[wvstreams.git] / utils / wvtask.cc
blobcdcd544f8a95a0c2c54af6766de4f3ceaf097263
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * A set of classes that provide co-operative multitasking support. See
6 * wvtask.h for more information.
7 */
9 #include "wvautoconf.h"
10 #ifdef __GNUC__
11 # define alloca __builtin_alloca
12 #else
13 # ifdef _MSC_VER
14 # include <malloc.h>
15 # define alloca _alloca
16 # else
17 # if HAVE_ALLOCA_H
18 # include <alloca.h>
19 # else
20 # ifdef _AIX
21 #pragma alloca
22 # else
23 # ifndef alloca /* predefined by HP cc +Olibcalls */
24 char *alloca ();
25 # endif
26 # endif
27 # endif
28 # endif
29 #endif
31 #include "wvtask.h"
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <assert.h>
35 #include <sys/mman.h>
36 #include <signal.h>
37 #include <unistd.h>
38 #include <sys/resource.h>
40 #ifdef HAVE_VALGRIND_MEMCHECK_H
41 #include <valgrind/memcheck.h>
42 // Compatibility for Valgrind 3.1 and previous
43 #ifndef VALGRIND_MAKE_MEM_DEFINED
44 #define VALGRIND_MAKE_MEM_DEFINED VALGRIND_MAKE_READABLE
45 #endif
46 #else
47 #define VALGRIND_MAKE_MEM_DEFINED(x, y)
48 #define RUNNING_ON_VALGRIND 0
49 #endif
51 #define TASK_DEBUG 0
52 #if TASK_DEBUG
53 # define Dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
54 #else
55 # define Dprintf(fmt, args...)
56 #endif
58 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
60 WvTaskMan *WvTaskMan::singleton;
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,
64 WvTaskMan::toplevel;
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
80 char val;
81 //printf("valgrind fix: %p-%p\n", &val, stacktop);
82 assert(stacktop > &val);
83 #endif
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;
92 func = NULL;
93 userdata = NULL;
95 tid = ++taskcount;
96 numtasks++;
97 magic_number = WVTASK_MAGIC;
98 stack_magic = NULL;
100 man.get_stack(*this, stacksize);
102 man.all_tasks.append(this, false);
106 WvTask::~WvTask()
108 numtasks--;
109 if (running)
110 numrunning--;
111 magic_number = 42;
115 void WvTask::start(WvStringParm _name, TaskFunc *_func, void *_userdata)
117 assert(!recycled);
118 name = _name;
119 func = _func;
120 userdata = _userdata;
121 running = true;
122 numrunning++;
126 void WvTask::recycle()
128 assert(!running);
130 if (!running && !recycled)
132 man.free_tasks.append(this, true);
133 recycled = true;
138 WvTaskMan *WvTaskMan::get()
140 if (!links)
141 singleton = new WvTaskMan;
142 links++;
143 return singleton;
147 void WvTaskMan::unlink()
149 links--;
150 if (!links)
152 delete singleton;
153 singleton = NULL;
158 static inline const char *Yes_No(bool val)
160 return val? "Yes": "No";
164 WvString WvTaskMan::debugger_tasks_run_cb(WvStringParm cmd, WvStringList &args,
165 WvStreamsDebugger::ResultCallback result_cb, void *)
167 const char *format_str = "%5s%s%7s%s%8s%s%6s%s%s";
168 WvStringList result;
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(); )
174 result.zap();
175 result.append(format_str, i->tid, " ",
176 Yes_No(i->running), " ",
177 Yes_No(i->recycled), " ",
178 i->stacksize, " ",
179 i->name);
180 result_cb(cmd, result);
182 return WvString::null;
186 WvTaskMan::WvTaskMan()
188 static bool first = true;
189 if (first)
191 first = false;
192 WvStreamsDebugger::add_command("tasks", 0, debugger_tasks_run_cb, 0);
195 stack_target = NULL;
196 current_task = NULL;
197 magic_number = -WVTASK_MAGIC;
199 stacktop = (char *)alloca(0);
201 context_return = 0;
202 assert(getcontext(&get_stack_return) == 0);
203 if (context_return == 0)
205 // initial setup - start the stackmaster() task (never returns!)
206 stackmaster();
208 // if we get here, stackmaster did a longjmp back to us.
212 WvTaskMan::~WvTaskMan()
214 magic_number = -42;
215 free_tasks.zap();
219 WvTask *WvTaskMan::start(WvStringParm name,
220 WvTask::TaskFunc *func, void *userdata,
221 size_t stacksize)
223 WvTask *t;
225 WvTaskList::Iter i(free_tasks);
226 for (i.rewind(); i.next(); )
228 if (i().stacksize >= stacksize)
230 t = &i();
231 i.set_autofree(false);
232 i.unlink();
233 t->recycled = false;
234 t->start(name, func, userdata);
235 return t;
239 // if we get here, no matching task was found.
240 t = new WvTask(*this, stacksize);
241 t->start(name, func, userdata);
242 return t;
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)
256 return val; // that's easy!
258 WvTask *old_task = current_task;
259 current_task = &task;
260 ucontext_t *state;
262 if (!old_task)
263 state = &toplevel; // top-level call (not in an actual task yet)
264 else
265 state = &old_task->mystate;
267 context_return = 0;
268 assert(getcontext(state) == 0);
269 int newval = context_return;
270 if (newval == 0)
272 // saved the state, now run the task.
273 context_return = val;
274 setcontext(&task.mystate);
275 return -1;
277 else
279 // need to make state readable to see if we need to make more readable..
280 VALGRIND_MAKE_MEM_DEFINED(&state, sizeof(state));
281 // someone did yield() (if toplevel) or run() on our old task; done.
282 if (state != &toplevel)
283 valgrind_fix(stacktop);
284 current_task = old_task;
285 return newval;
290 int WvTaskMan::yield(int val)
292 if (!current_task)
293 return 0; // weird...
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);
300 // if this fails, this task overflowed its stack. Make it bigger!
301 VALGRIND_MAKE_MEM_DEFINED(current_task->stack_magic,
302 sizeof(current_task->stack_magic));
303 assert(*current_task->stack_magic == WVTASK_MAGIC);
305 #if TASK_DEBUG
306 if (use_shared_stack())
308 size_t stackleft;
309 char *stackbottom = (char *)(current_task->stack_magic + 1);
310 for (stackleft = 0; stackleft < current_task->stacksize; stackleft++)
312 if (stackbottom[stackleft] != 0x42)
313 break;
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);
319 #endif
321 context_return = 0;
322 assert(getcontext(&current_task->mystate) == 0);
323 int newval = context_return;
324 if (newval == 0)
326 // saved the task state; now yield to the toplevel.
327 context_return = val;
328 setcontext(&toplevel);
329 return -1;
331 else
333 // back via longjmp, because someone called run() again. Let's go
334 // back to our running task...
335 valgrind_fix(stacktop);
336 return newval;
341 void WvTaskMan::get_stack(WvTask &task, size_t size)
343 context_return = 0;
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;
357 #else
358 static char *next_stack_addr = NULL;
359 #endif
361 task.stack = mmap(next_stack_addr, task.stacksize,
362 PROT_READ | PROT_WRITE,
363 #ifndef MACOS
364 MAP_PRIVATE | MAP_ANONYMOUS,
365 #else
366 MAP_PRIVATE,
367 #endif
368 -1, 0);
371 // initial setup
372 stack_target = &task;
373 context_return = size/1024 + (size%1024 > 0);
374 setcontext(&stackmaster_task);
376 else
378 if (current_task)
379 valgrind_fix(stacktop);
380 assert(magic_number == -WVTASK_MAGIC);
381 assert(task.magic_number == WVTASK_MAGIC);
383 // back from stackmaster - the task is now set up.
384 return;
389 void WvTaskMan::stackmaster()
391 // leave lots of room on the "main" stack before doing our magic
392 alloca(1024*1024);
394 _stackmaster();
398 void WvTaskMan::_stackmaster()
400 int val;
401 size_t total;
403 Dprintf("stackmaster 1\n");
405 // the main loop runs once from the constructor, and then once more
406 // after each stack allocation.
407 for (;;)
409 assert(magic_number == -WVTASK_MAGIC);
411 context_return = 0;
412 assert(getcontext(&stackmaster_task) == 0);
413 val = context_return;
414 if (val == 0)
416 assert(magic_number == -WVTASK_MAGIC);
418 // just did setjmp; save stackmaster's current state (with
419 // all current stack allocations) and go back to get_stack
420 // (or the constructor, if that's what called us)
421 context_return = 1;
422 setcontext(&get_stack_return);
424 else
426 valgrind_fix(stacktop);
427 assert(magic_number == -WVTASK_MAGIC);
429 total = (val+1) * (size_t)1024;
431 if (!use_shared_stack())
432 total = 1024; // enough to save the do_task stack frame
434 // set up a stack frame for the new task. This runs once
435 // per get_stack.
436 //alloc_stack_and_switch(total);
437 do_task();
439 assert(magic_number == -WVTASK_MAGIC);
441 // allocate the stack area so we never use it again
442 alloca(total);
444 // a little sentinel so we can detect stack overflows
445 stack_target->stack_magic = (int *)alloca(sizeof(int));
446 *stack_target->stack_magic = WVTASK_MAGIC;
448 // clear the stack to 0x42 so we can count unused stack
449 // space later.
450 #if TASK_DEBUG
451 memset(stack_target->stack_magic + 1, 0x42, total - 1024);
452 #endif
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);
465 context_return = 1;
469 void WvTaskMan::do_task()
471 assert(magic_number == -WVTASK_MAGIC);
472 WvTask *task = stack_target;
473 assert(task->magic_number == WVTASK_MAGIC);
475 // back here from longjmp; someone wants stack space.
476 context_return = 0;
477 assert(getcontext(&task->mystate) == 0);
478 if (context_return == 0)
480 // done the setjmp; that means the target task now has
481 // a working jmp_buf all set up. Leave space on the stack
482 // for his data, then repeat the loop in _stackmaster (so we can
483 // return to get_stack(), and allocate more stack for someone later)
485 // Note that nothing on the allocated stack needs to be valid; when
486 // they longjmp to task->mystate, they'll have a new stack pointer
487 // and they'll already know what to do (in the 'else' clause, below)
488 Dprintf("stackmaster 5\n");
489 return;
491 else
493 // someone did a run() on the task, which
494 // means they're ready to make it go. Do it.
495 valgrind_fix(stacktop);
496 for (;;)
498 assert(magic_number == -WVTASK_MAGIC);
499 assert(task);
500 assert(task->magic_number == WVTASK_MAGIC);
502 if (task->func && task->running)
504 if (use_shared_stack())
506 // this is the task's main function. It can call yield()
507 // to give up its timeslice if it wants. Either way, it
508 // only returns to *us* if the function actually finishes.
509 task->func(task->userdata);
511 else
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);
523 context_return = 0;
524 assert(getcontext(&task->func_return) == 0);
525 if (context_return == 0)
526 setcontext(&task->func_call);
529 // the task's function terminated.
530 task->name = "DEAD";
531 task->running = false;
532 task->numrunning--;
534 yield();
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;
546 else
547 return (const char *)current_task->stack + current_task->stacksize;
548 #else
549 return 0;
550 #endif
554 size_t WvTaskMan::current_stacksize_limit()
556 if (use_shared_stack() || current_task == NULL)
558 struct rlimit rl;
559 if (getrlimit(RLIMIT_STACK, &rl) == 0)
560 return size_t(rl.rlim_cur);
561 else
562 return 0;
564 else
565 return size_t(current_task->stacksize);