WvStreams
wvwin32task.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  */
6 #define WIN32_LEAN_AND_MEAN
7 //#define NOMINMAX
8 #ifndef _WIN32_WINNT
9 #define _WIN32_WINNT 0x0400
10 #endif
11 #include <windows.h>
12 #include <winbase.h>
13 
14 #include "wvwin32task.h"
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <assert.h>
18 #include <malloc.h> // for alloca()
19 #include <stdlib.h> // for alloca() on non-Linux platforms?
20 
21 int WvTask::taskcount, WvTask::numtasks, WvTask::numrunning;
22 
23 WvTaskMan *WvTaskMan::singleton = NULL;
24 int WvTaskMan::links = 1; // never delete singleton
25 
26 int WvTaskMan::magic_number;
27 WvTaskList WvTaskMan::free_tasks;
28 
29 WvTask *WvTaskMan::stack_target;
30 
31 WvTask *WvTaskMan::current_task;
32 LPVOID WvTaskMan::toplevel;
33 
34 WvTask::WvTask(WvTaskMan &_man, size_t _stacksize) : man(_man)
35 {
36  stacksize = _stacksize;
37  running = recycled = false;
38  func = NULL;
39  userdata = NULL;
40 
41  tid = ++taskcount;
42  numtasks++;
43  magic_number = WVTASK_MAGIC;
44  stack_magic = NULL;
45 
46  mystate = CreateFiber(_stacksize, &MyFiberProc, this);
47  assert(mystate);
48 }
49 
50 
51 WvTask::~WvTask()
52 {
53  numtasks--;
54  if (running)
55  numrunning--;
56  magic_number = 42;
57 }
58 
59 VOID CALLBACK WvTask::MyFiberProc(PVOID lpParameter)
60 {
61  WvTask *_this = (WvTask *) lpParameter;
62  while (true)
63  {
64  if (_this->func && _this->running)
65  {
66  _this->func(_this->userdata);
67 
68  // the task's function terminated.
69  _this->name = "DEAD";
70  _this->running = false;
71  _this->numrunning--;
72  }
73  _this->man.yield();
74  }
75 }
76 
77 void WvTask::start(WvStringParm _name, TaskFunc *_func, void *_userdata)
78 {
79  assert(!recycled);
80  name = _name;
81  func = _func;
82  userdata = _userdata;
83  running = true;
84  numrunning++;
85 }
86 
87 
88 void WvTask::recycle()
89 {
90  assert(!running);
91 
92  if (!running && !recycled)
93  {
94  man.free_tasks.append(this, true);
95  recycled = true;
96  }
97 }
98 
100 {
101  if (!singleton)
102  singleton = new WvTaskMan;
103  links++;
104  return singleton;
105 }
106 
107 
108 void WvTaskMan::unlink()
109 {
110  links--;
111  if (links == 0)
112  {
113  delete singleton;
114  singleton = NULL;
115  }
116 }
117 
118 WvTaskMan::WvTaskMan()
119 {
120  stack_target = NULL;
121  current_task = NULL;
122  magic_number = -WVTASK_MAGIC;
123 
124  toplevel = ::ConvertThreadToFiber(0);
125  assert(toplevel);
126 }
127 
128 
129 WvTaskMan::~WvTaskMan()
130 {
131  magic_number = -42;
132 }
133 
134 
135 WvTask *WvTaskMan::start(WvStringParm name,
136  WvTask::TaskFunc *func, void *userdata,
137  size_t stacksize)
138 {
139  WvTask *t;
140 
141  WvTaskList::Iter i(free_tasks);
142  for (i.rewind(); i.next(); )
143  {
144  if (i().stacksize >= stacksize)
145  {
146  t = &i();
147  i.link->set_autofree(false);
148  i.unlink();
149  t->recycled = false;
150  t->start(name, func, userdata);
151  return t;
152  }
153  }
154  // if we get here, no matching task was found.
155  t = new WvTask(*this, stacksize);
156  t->start(name, func, userdata);
157  return t;
158 }
159 
160 
161 int WvTaskMan::run(WvTask &task, int val)
162 {
163  assert(magic_number == -WVTASK_MAGIC);
164  assert(task.magic_number == WVTASK_MAGIC);
165  assert(!task.recycled);
166 
167  if (&task == current_task)
168  return val; // that's easy!
169 
170  WvTask *old_task = current_task;
171  current_task = &task;
172  LPVOID *state;
173 
174  if (!old_task)
175  state = &toplevel; // top-level call (not in an actual task yet)
176  else
177  state = &old_task->mystate;
178 
179  //fprintf(stderr, "taskman: switching from %p to %p\n", old_task, &task);
180  ::SwitchToFiber(task.mystate);
181  //fprintf(stderr, "taskman: back in %p\n", old_task);
182 
183  // someone did yield() (if toplevel) or run() on our task; exit
184  current_task = old_task;
185  int newval = 0;
186  return newval;
187 }
188 
189 
190 int WvTaskMan::yield(int val)
191 {
192  if (!current_task)
193  return 0; // weird...
194 
195  WvTask *task = current_task;
196  //fprintf(stderr, "taskman: yielding from %p to toplevel\n", task);
197  current_task = 0; // toplevel
198  ::SwitchToFiber(toplevel);
199  //fprintf(stderr, "taskman: return from yield in %p (%p)\n", current_task, task);
200  assert(current_task == task);
201 
202  int newval = 0;
203  return newval;
204 }
WvTaskMan
Provides co-operative multitasking support among WvTask instances.
Definition: wvtask.h:81
WvTask
Represents a single thread of control.
Definition: wvtask.h:34
WvTaskMan::get
static WvTaskMan * get()
get/dereference the singleton global WvTaskMan
Definition: wvtask.cc:139