WvStreams
wvwin32task.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  */
00006 #define WIN32_LEAN_AND_MEAN
00007 //#define NOMINMAX
00008 #ifndef _WIN32_WINNT
00009 #define _WIN32_WINNT 0x0400
00010 #endif
00011 #include <windows.h>
00012 #include <winbase.h>
00013 
00014 #include "wvwin32task.h"
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <assert.h>
00018 #include <malloc.h> // for alloca()
00019 #include <stdlib.h> // for alloca() on non-Linux platforms?
00020 
00021 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
00022 
00023 WvTaskMan *WvTaskMan::singleton = NULL;
00024 int WvTaskMan::links = 1; // never delete singleton
00025 
00026 int WvTaskMan::magic_number;
00027 WvTaskList WvTaskMan::free_tasks;
00028     
00029 WvTask *WvTaskMan::stack_target;
00030     
00031 WvTask *WvTaskMan::current_task;
00032 LPVOID WvTaskMan::toplevel;
00033 
00034 WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man)
00035 {
00036     stacksize = _stacksize;
00037     running = recycled = false;
00038     func = NULL;
00039     userdata = NULL;
00040     
00041     tid = ++taskcount;
00042     numtasks++;
00043     magic_number = WVTASK_MAGIC;
00044     stack_magic = NULL;
00045 
00046     mystate = CreateFiber(_stacksize, &MyFiberProc, this);
00047     assert(mystate);
00048 }
00049 
00050 
00051 WvTask::~WvTask()
00052 {
00053     numtasks--;
00054     if (running)
00055         numrunning--;
00056     magic_number = 42;
00057 }
00058 
00059 VOID CALLBACK WvTask::MyFiberProc(PVOID lpParameter)
00060 {
00061     WvTask *_this = (WvTask *) lpParameter;
00062     while (true)
00063     {
00064         if (_this->func && _this->running)
00065         {
00066             _this->func(_this->userdata);
00067 
00068             // the task's function terminated.
00069             _this->name = "DEAD";
00070             _this->running = false;
00071             _this->numrunning--;
00072         }
00073         _this->man.yield();
00074     }
00075 }
00076 
00077 void WvTask::start(WvStringParm _name, TaskFunc *_func, void *_userdata)
00078 {
00079     assert(!recycled);
00080     name = _name;
00081     func = _func;
00082     userdata = _userdata;
00083     running = true;
00084     numrunning++;
00085 }
00086 
00087 
00088 void WvTask::recycle()
00089 {
00090     assert(!running);
00091     
00092     if (!running && !recycled)
00093     {
00094         man.free_tasks.append(this, true);
00095         recycled = true;
00096     }
00097 }
00098 
00099 WvTaskMan *WvTaskMan::get()
00100 {
00101     if (!singleton)
00102         singleton = new WvTaskMan;
00103     links++;
00104     return singleton;
00105 }
00106 
00107 
00108 void WvTaskMan::unlink()
00109 {
00110     links--;
00111     if (links == 0)
00112     {
00113         delete singleton;
00114         singleton = NULL;
00115     }
00116 }
00117 
00118 WvTaskMan::WvTaskMan()
00119 {
00120     stack_target = NULL;
00121     current_task = NULL;
00122     magic_number = -WVTASK_MAGIC;
00123     
00124     toplevel = ::ConvertThreadToFiber(0);
00125     assert(toplevel);
00126 }
00127 
00128 
00129 WvTaskMan::~WvTaskMan()
00130 {    
00131     magic_number = -42;
00132 }
00133 
00134 
00135 WvTask *WvTaskMan::start(WvStringParm name, 
00136                          WvTask::TaskFunc *func, void *userdata,
00137                          size_t stacksize)
00138 {
00139     WvTask *t;
00140     
00141     WvTaskList::Iter i(free_tasks);
00142     for (i.rewind(); i.next(); )
00143     {
00144         if (i().stacksize >= stacksize)
00145         {
00146             t = &i();
00147             i.link->set_autofree(false);
00148             i.unlink();
00149             t->recycled = false;
00150             t->start(name, func, userdata);
00151             return t;
00152         }
00153     }    
00154     // if we get here, no matching task was found.
00155     t = new WvTask(*this, stacksize);
00156     t->start(name, func, userdata);
00157     return t;
00158 }
00159 
00160 
00161 int WvTaskMan::run(WvTask &task, int val)
00162 {
00163     assert(magic_number == -WVTASK_MAGIC);
00164     assert(task.magic_number == WVTASK_MAGIC);
00165     assert(!task.recycled);
00166     
00167     if (&task == current_task)
00168         return val; // that's easy!
00169         
00170     WvTask *old_task = current_task;
00171     current_task = &task;
00172     LPVOID *state;
00173     
00174     if (!old_task)
00175         state = &toplevel; // top-level call (not in an actual task yet)
00176     else
00177         state = &old_task->mystate;
00178     
00179     //fprintf(stderr, "taskman: switching from %p to %p\n", old_task, &task);
00180     ::SwitchToFiber(task.mystate);
00181     //fprintf(stderr, "taskman: back in %p\n", old_task);
00182     
00183     // someone did yield() (if toplevel) or run() on our task; exit
00184     current_task = old_task;
00185     int newval = 0;
00186     return newval;
00187 }
00188 
00189 
00190 int WvTaskMan::yield(int val)
00191 {
00192     if (!current_task)
00193         return 0; // weird...
00194     
00195     WvTask *task = current_task;
00196     //fprintf(stderr, "taskman: yielding from %p to toplevel\n", task);
00197     current_task = 0; // toplevel
00198     ::SwitchToFiber(toplevel);
00199     //fprintf(stderr, "taskman: return from yield in %p (%p)\n", current_task, task);
00200     assert(current_task == task);
00201 
00202     int newval = 0;
00203     return newval;
00204 }