WvStreams
|
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 }