Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

wvtask.cc

Go to the documentation of this file.
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * A set of classes that provide co-operative multitasking support.  See
00006  * wvtask.h for more information.
00007  */
00008 #include "wvtask.h"
00009 #include <stdio.h>
00010 #include <stdlib.h>
00011 #include <malloc.h> // for alloca()
00012 #include <assert.h>
00013 
00014 #if 0
00015 # define Dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
00016 #else
00017 # define Dprintf(fmt, args...)
00018 #endif
00019 
00020 
00021 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
00022 
00023 
00024 WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man)
00025 {
00026     stacksize = _stacksize;
00027     running = recycled = false;
00028     
00029     tid = ++taskcount;
00030     numtasks++;
00031     magic_number = WVTASK_MAGIC;
00032     
00033     Dprintf("task %d initializing\n", tid);
00034     man.get_stack(*this, stacksize);
00035     Dprintf("task %d initialized\n", tid);
00036 }
00037 
00038 
00039 WvTask::~WvTask()
00040 {
00041     numtasks--;
00042     Dprintf("task %d stopping (%d tasks left)\n", tid, numtasks);
00043     
00044     if (running)
00045     {
00046         Dprintf("WARNING: task %d was running -- bad stuff may happen!\n",
00047                tid);
00048         numrunning--;
00049     }
00050     
00051     magic_number = 42;
00052 }
00053 
00054 
00055 void WvTask::start(const WvString &_name, TaskFunc *_func, void *_userdata)
00056 {
00057     assert(!recycled);
00058     name = _name;
00059     name.unique();
00060     Dprintf("task %d (%s) starting\n", tid, (const char *)name);
00061     func = _func;
00062     userdata = _userdata;
00063     running = true;
00064     numrunning++;
00065 }
00066 
00067 
00068 void WvTask::recycle()
00069 {
00070     if (!running && !recycled)
00071     {
00072         man.free_tasks.append(this, true);
00073         recycled = true;
00074     }
00075 }
00076 
00077 
00078 WvTaskMan::WvTaskMan()
00079 {
00080     Dprintf("task manager up\n");
00081     current_task = NULL;
00082     magic_number = -WVTASK_MAGIC;
00083     
00084     if (setjmp(get_stack_return) == 0)
00085     {
00086         // initial setup - start the stackmaster() task (never returns!)
00087         stackmaster();
00088     }
00089     // if we get here, stackmaster did a longjmp back to us.
00090 }
00091 
00092 
00093 WvTaskMan::~WvTaskMan()
00094 {
00095     Dprintf("task manager down\n");
00096     if (WvTask::numrunning != 0)
00097         Dprintf("WARNING!  %d tasks still running at WvTaskMan shutdown!\n",
00098                WvTask::numrunning);
00099     magic_number = -42;
00100 }
00101 
00102 
00103 WvTask *WvTaskMan::start(const WvString &name, 
00104                          WvTask::TaskFunc *func, void *userdata,
00105                          size_t stacksize)
00106 {
00107     WvTask *t;
00108     
00109     WvTaskList::Iter i(free_tasks);
00110     for (i.rewind(); i.next(); )
00111     {
00112         if (i().stacksize >= stacksize)
00113         {
00114             t = &i();
00115             i.link->auto_free = false;
00116             i.unlink();
00117             t->recycled = false;
00118             t->start(name, func, userdata);
00119             return t;
00120         }
00121     }
00122     
00123     // if we get here, no matching task was found.
00124     t = new WvTask(*this, stacksize);
00125     t->start(name, func, userdata);
00126     return t;
00127 }
00128 
00129 
00130 int WvTaskMan::run(WvTask &task, int val)
00131 {
00132     assert(magic_number == -WVTASK_MAGIC);
00133     assert(task.magic_number == WVTASK_MAGIC);
00134     assert(!task.recycled);
00135     
00136     if (&task == current_task)
00137         return val; // that's easy!
00138     
00139     Dprintf("WvTaskMan: switching to task #%d (%s)\n",
00140            task.tid, (const char *)task.name);
00141     
00142     WvTask *old_task = current_task;
00143     current_task = &task;
00144     jmp_buf *state;
00145     
00146     if (!old_task)
00147         state = &toplevel; // top-level call (not in an actual task yet)
00148     else
00149         state = &old_task->mystate;
00150     
00151     int newval = setjmp(*state);
00152     if (newval == 0)
00153     {
00154         // saved the state, now run the task.
00155         longjmp(task.mystate, val);
00156     }
00157     else
00158     {
00159         // someone did yield() (if toplevel) or run() on our task; exit
00160         current_task = old_task;
00161         return newval;
00162     }
00163 }
00164 
00165 
00166 int WvTaskMan::yield(int val)
00167 {
00168     if (!current_task)
00169         return 0; // weird...
00170     
00171     Dprintf("WvTaskMan: yielding from task #%d (%s)\n",
00172            current_task->tid, (const char *)current_task->name);
00173     
00174     int newval = setjmp(current_task->mystate);
00175     if (newval == 0)
00176     {
00177         // saved the task state; now yield to the toplevel.
00178         longjmp(toplevel, val);
00179     }
00180     else
00181     {
00182         // back via longjmp, because someone called run() again.  Let's go
00183         // back to our running task...
00184         return newval;
00185     }
00186 }
00187 
00188 
00189 void WvTaskMan::get_stack(WvTask &task, size_t size)
00190 {
00191     if (setjmp(get_stack_return) == 0)
00192     {
00193         assert(magic_number == -WVTASK_MAGIC);
00194         assert(task.magic_number == WVTASK_MAGIC);
00195         
00196         // initial setup
00197         stack_target = &task;
00198         longjmp(stackmaster_task, size/1024 + (size%1024 > 0));
00199     }
00200     else
00201     {
00202         assert(magic_number == -WVTASK_MAGIC);
00203         assert(task.magic_number == WVTASK_MAGIC);
00204         
00205         // back from stackmaster - the task is now set up.
00206         return;
00207     }
00208 }
00209 
00210 
00211 void WvTaskMan::stackmaster()
00212 {
00213     // leave lots of room on the "main" stack before doing our magic
00214     alloca(1024*1024);
00215     
00216     _stackmaster();
00217 }
00218 
00219 
00220 void WvTaskMan::_stackmaster()
00221 {
00222     int val;
00223     
00224     Dprintf("stackmaster 1\n");
00225     
00226     for (;;)
00227     {
00228         assert(magic_number == -WVTASK_MAGIC);
00229         
00230         Dprintf("stackmaster 2\n");
00231         val = setjmp(stackmaster_task);
00232         if (val == 0)
00233         {
00234             assert(magic_number == -WVTASK_MAGIC);
00235             
00236             // just did setjmp; save stackmaster's current state (with
00237             // all current stack allocations) and go back to get_stack
00238             // (or the constructor, if that's what called us)
00239             Dprintf("stackmaster 3\n");
00240             longjmp(get_stack_return, 1);
00241         }
00242         else
00243         {
00244             assert(magic_number == -WVTASK_MAGIC);
00245             
00246             // set up a stack frame for the task
00247             do_task();
00248             
00249             assert(magic_number == -WVTASK_MAGIC);
00250             
00251             // allocate the stack area so we never use it again
00252             alloca(val * (size_t)1024);
00253         }
00254     }
00255 }
00256 
00257 
00258 void WvTaskMan::do_task()
00259 {
00260     assert(magic_number == -WVTASK_MAGIC);
00261     WvTask *task = stack_target;
00262     assert(task->magic_number == WVTASK_MAGIC);
00263         
00264     // back here from longjmp; someone wants stack space.
00265     Dprintf("stackmaster 4\n");
00266     if (setjmp(task->mystate) == 0)
00267     {
00268         // done the setjmp; that means the target task now has
00269         // a working jmp_buf all set up.  Leave space on the stack
00270         // for his data, then repeat the loop (so we can return
00271         // to get_stack(), and allocate more stack for someone later)
00272         Dprintf("stackmaster 5\n");
00273         return;
00274     }
00275     else
00276     {
00277         // someone did a run() on the task, which
00278         // means they're ready to make it go.  Do it.
00279         for (;;)
00280         {
00281             Dprintf("stackmaster 6\n");
00282             assert(magic_number == -WVTASK_MAGIC);
00283             assert(task->magic_number == WVTASK_MAGIC);
00284             
00285             if (task->func && task->running)
00286             {
00287                 task->func(task->userdata);
00288                 task->name = "DEAD";
00289                 task->running = false;
00290                 task->numrunning--;
00291             }
00292             Dprintf("stackmaster 7\n");
00293             yield();
00294         }
00295     }
00296 }

Generated on Sun Aug 25 12:42:25 2002 for WvStreams by doxygen1.2.15