/* * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * */ #define WIN32_LEAN_AND_MEAN //#define NOMINMAX #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #include #include #include "wvwin32task.h" #include #include #include #include // for alloca() #include // for alloca() on non-Linux platforms? int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning; WvTaskMan *WvTaskMan::singleton = NULL; int WvTaskMan::links = 1; // never delete singleton int WvTaskMan::magic_number; WvTaskList WvTaskMan::free_tasks; WvTask *WvTaskMan::stack_target; WvTask *WvTaskMan::current_task; LPVOID WvTaskMan::toplevel; WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man) { stacksize = _stacksize; running = recycled = false; func = NULL; userdata = NULL; tid = ++taskcount; numtasks++; magic_number = WVTASK_MAGIC; stack_magic = NULL; mystate = CreateFiber(_stacksize, &MyFiberProc, this); assert(mystate); } WvTask::~WvTask() { numtasks--; if (running) numrunning--; magic_number = 42; } VOID CALLBACK WvTask::MyFiberProc(PVOID lpParameter) { WvTask *_this = (WvTask *) lpParameter; while (true) { if (_this->func && _this->running) { _this->func(_this->userdata); // the task's function terminated. _this->name = "DEAD"; _this->running = false; _this->numrunning--; } _this->man.yield(); } } void WvTask::start(WvStringParm _name, TaskFunc *_func, void *_userdata) { assert(!recycled); name = _name; func = _func; userdata = _userdata; running = true; numrunning++; } void WvTask::recycle() { assert(!running); if (!running && !recycled) { man.free_tasks.append(this, true); recycled = true; } } WvTaskMan *WvTaskMan::get() { if (!singleton) singleton = new WvTaskMan; links++; return singleton; } void WvTaskMan::unlink() { links--; if (links == 0) { delete singleton; singleton = NULL; } } WvTaskMan::WvTaskMan() { stack_target = NULL; current_task = NULL; magic_number = -WVTASK_MAGIC; toplevel = ::ConvertThreadToFiber(0); assert(toplevel); } WvTaskMan::~WvTaskMan() { magic_number = -42; } WvTask *WvTaskMan::start(WvStringParm name, WvTask::TaskFunc *func, void *userdata, size_t stacksize) { WvTask *t; WvTaskList::Iter i(free_tasks); for (i.rewind(); i.next(); ) { if (i().stacksize >= stacksize) { t = &i(); i.link->set_autofree(false); i.unlink(); t->recycled = false; t->start(name, func, userdata); return t; } } // if we get here, no matching task was found. t = new WvTask(*this, stacksize); t->start(name, func, userdata); return t; } int WvTaskMan::run(WvTask &task, int val) { assert(magic_number == -WVTASK_MAGIC); assert(task.magic_number == WVTASK_MAGIC); assert(!task.recycled); if (&task == current_task) return val; // that's easy! WvTask *old_task = current_task; current_task = &task; LPVOID *state; if (!old_task) state = &toplevel; // top-level call (not in an actual task yet) else state = &old_task->mystate; //fprintf(stderr, "taskman: switching from %p to %p\n", old_task, &task); ::SwitchToFiber(task.mystate); //fprintf(stderr, "taskman: back in %p\n", old_task); // someone did yield() (if toplevel) or run() on our task; exit current_task = old_task; int newval = 0; return newval; } int WvTaskMan::yield(int val) { if (!current_task) return 0; // weird... WvTask *task = current_task; //fprintf(stderr, "taskman: yielding from %p to toplevel\n", task); current_task = 0; // toplevel ::SwitchToFiber(toplevel); //fprintf(stderr, "taskman: return from yield in %p (%p)\n", current_task, task); assert(current_task == task); int newval = 0; return newval; }