Lely core libraries  2.2.5
fiber-win32.c
Go to the documentation of this file.
1 
24 #include "fiber.h"
25 
26 #if _WIN32
27 
28 #include <lely/libc/stddef.h>
29 #include <lely/util/fiber.h>
30 
31 #include <assert.h>
32 #include <errno.h>
33 #include <stdlib.h>
34 
35 #include <windows.h>
36 
37 struct fiber {
38  fiber_func_t *func;
39  void *arg;
40  int flags;
41  void *data;
42  fiber_t *from;
43  LPVOID lpFiber;
44  int errsv;
45  DWORD dwErrCode;
46 };
47 
48 #define FIBER_SIZE ALIGN(sizeof(fiber_t), _Alignof(max_align_t))
49 
50 #if LELY_NO_THREADS
51 static struct fiber_thrd {
52 #else
53 static _Thread_local struct fiber_thrd {
54 #endif
55  size_t refcnt;
56  fiber_t main;
57  fiber_t *curr;
58 } fiber_thrd;
59 
60 static _Noreturn void CALLBACK fiber_start(void *arg);
61 
62 static fiber_t *fiber_switch(fiber_t *fiber);
63 
64 int
65 fiber_thrd_init(int flags)
66 {
67  struct fiber_thrd *thr = &fiber_thrd;
68 
69  if (flags & ~(int)FIBER_SAVE_ALL) {
70  SetLastError(ERROR_INVALID_PARAMETER);
71  return -1;
72  }
73 
74  if (thr->refcnt++)
75  return 1;
76 
77  assert(!thr->main.lpFiber);
78  assert(!thr->main.from);
79  assert(!thr->curr);
80 
81  thr->main.flags = flags;
82  DWORD dwFlags = 0;
83  if (thr->main.flags & FIBER_SAVE_FENV)
84  dwFlags |= FIBER_FLAG_FLOAT_SWITCH;
85  thr->main.lpFiber = ConvertThreadToFiberEx(NULL, dwFlags);
86  if (!thr->main.lpFiber) {
87  thr->main.flags = 0;
88  thr->refcnt--;
89  return -1;
90  }
91 
92  thr->curr = &thr->main;
93 
94  return 0;
95 }
96 
97 void
98 fiber_thrd_fini(void)
99 {
100  struct fiber_thrd *thr = &fiber_thrd;
101  assert(thr->refcnt);
102 
103  if (!--thr->refcnt) {
104  assert(thr->curr == &thr->main);
105 
106  thr->curr = NULL;
107 
108  thr->main.from = NULL;
109  assert(thr->main.lpFiber);
110  ConvertFiberToThread();
111  thr->main.lpFiber = NULL;
112  thr->main.flags = 0;
113  }
114 }
115 
116 fiber_t *
117 fiber_create(fiber_func_t *func, void *arg, int flags, size_t data_size,
118  size_t stack_size)
119 {
120  if (flags & ~(int)FIBER_SAVE_ALL) {
121  SetLastError(ERROR_INVALID_PARAMETER);
122  return NULL;
123  }
124 
125  if (!stack_size)
126  stack_size = LELY_FIBER_STKSZ;
127  else if (stack_size < LELY_FIBER_MINSTKSZ)
128  stack_size = LELY_FIBER_MINSTKSZ;
129 
130  DWORD dwErrCode = 0;
131 
132  fiber_t *fiber = malloc(FIBER_SIZE + data_size);
133  if (!fiber) {
134  dwErrCode = GetLastError();
135  goto error_malloc_fiber;
136  }
137 
138  fiber->func = func;
139  fiber->arg = arg;
140  fiber->flags = flags;
141 
142  fiber->data = (char *)fiber + FIBER_SIZE;
143 
144  fiber->from = NULL;
145 
146  DWORD dwFlags = 0;
147  if (fiber->flags & FIBER_SAVE_FENV)
148  dwFlags |= FIBER_FLAG_FLOAT_SWITCH;
149  fiber->lpFiber = CreateFiberEx(
150  stack_size, 0, dwFlags, &fiber_start, fiber);
151  if (!fiber->lpFiber) {
152  dwErrCode = GetLastError();
153  goto error_CreateFiberEx;
154  }
155 
156  // Invoke fiber_start(). After setting up the environment it will return
157  // here.
159 
160  // Cppcheck gets confused by fiber_resume() and thinks we leak memory.
161  // cppcheck-suppress memleak
162  return fiber;
163 
164  DeleteFiber(fiber->lpFiber);
165 error_CreateFiberEx:
166  free(fiber);
167 error_malloc_fiber:
168  SetLastError(dwErrCode);
169  return NULL;
170 }
171 
172 void
174 {
175  if (fiber && fiber->data) {
176  DeleteFiber(fiber->lpFiber);
177  free(fiber);
178  }
179 }
180 
181 void *
182 fiber_data(const fiber_t *fiber)
183 {
184  assert(fiber_thrd.curr);
185 
186  return fiber ? fiber->data : fiber_thrd.curr->data;
187 }
188 
189 fiber_t *
191 {
192  if (!fiber)
193  fiber = &fiber_thrd.main;
194 
195  if (fiber == fiber_thrd.curr)
196  return fiber->from = fiber;
197 
198  return fiber_switch(fiber);
199 }
200 
201 fiber_t *
202 fiber_resume_with(fiber_t *fiber, fiber_func_t *func, void *arg)
203 {
204  if (!fiber)
205  fiber = &fiber_thrd.main;
206 
207  if (fiber == fiber_thrd.curr)
208  return fiber->from = func ? func(fiber, arg) : fiber;
209 
210  fiber->func = func;
211  fiber->arg = arg;
212 
213  return fiber_switch(fiber);
214 }
215 
216 static _Noreturn void CALLBACK
217 fiber_start(void *arg)
218 {
219  fiber_t *fiber = arg;
220  assert(fiber);
221 
222  // Copy the function to be executed to the stack for later reference.
223  fiber_func_t *func = fiber->func;
224  fiber->func = NULL;
225  arg = fiber->arg;
226  fiber->arg = NULL;
227 
228  // Resume fiber_create().
229  fiber = fiber_resume(fiber->from);
230 
231  // If the fiber is started with fiber_resume_with(), execute that
232  // function before starting the original function.
233  if (fiber->func) {
234  fiber_func_t *func = fiber->func;
235  fiber->func = NULL;
236  void *arg = fiber->arg;
237  fiber->arg = NULL;
238  fiber = func(fiber, arg);
239  }
240 
241  if (func)
242  fiber = func(fiber, arg);
243 
244  // If no valid fiber is returned, return to the main context.
245  if (!fiber)
246  fiber = &fiber_thrd.main;
247 
248  // The function has terminated, so return to the caller immediately.
249  for (;;)
251 }
252 
253 static fiber_t *
254 fiber_switch(fiber_t *fiber)
255 {
256  assert(fiber);
257  struct fiber_thrd *thr = &fiber_thrd;
258  fiber_t *curr = thr->curr;
259  assert(curr);
260  assert(fiber != curr);
261 
262  if (curr->flags & FIBER_SAVE_ERROR) {
263  curr->dwErrCode = GetLastError();
264  curr->errsv = errno;
265  }
266 
267  fiber->from = thr->curr;
268  thr->curr = fiber;
269  assert(fiber->lpFiber);
270  SwitchToFiber(fiber->lpFiber);
271  assert(curr == thr->curr);
272 
273  if (curr->flags & FIBER_SAVE_ERROR) {
274  errno = curr->errsv;
275  SetLastError(curr->dwErrCode);
276  }
277 
278  if (curr->func) {
279  fiber_func_t *func = curr->func;
280  curr->func = NULL;
281  void *arg = curr->arg;
282  curr->arg = NULL;
283  curr->from = func(curr->from, arg);
284  }
285 
286  return curr->from;
287 }
288 
289 #endif // _WIN32
#define _Thread_local
An object whose identifier is declared with the storage-class specifier _Thread_local has thread stor...
Definition: features.h:239
#define _Noreturn
A function declared with a _Noreturn function specifier SHALL not return to its caller.
Definition: features.h:214
This header file is part of the utilities library; it contains the fiber declarations.
void * fiber_data(const fiber_t *fiber)
Returns a pointer to the data region of the specified fiber, or of the calling fiber if fiber is NULL...
Definition: fiber-sjlj.c:314
fiber_t * fiber_resume_with(fiber_t *fiber, fiber_func_t *func, void *arg)
Suspends the calling fiber and resumes the specified fiber, optionally executing a function before re...
Definition: fiber-sjlj.c:334
int fiber_thrd_init(int flags)
Initializes the fiber associated with the calling thread.
Definition: fiber-sjlj.c:108
#define FIBER_SAVE_ERROR
A flag specifying a fiber to save and restore the error values (i.e., errno and GetLastError() on Win...
Definition: fiber.h:58
fiber_t * fiber_resume(fiber_t *fiber)
Equivalent to fiber_resume_with(fiber, NULL, NULL).
Definition: fiber-sjlj.c:322
void fiber_thrd_fini(void)
Finalizes the fiber associated with the calling thread.
Definition: fiber-sjlj.c:135
#define FIBER_SAVE_FENV
A flag specifying a fiber to save and restore the floating-point environment.
Definition: fiber.h:52
#define FIBER_SAVE_ALL
A combination of those flags in FIBER_SAVE_MASK, FIBER_SAVE_FENV and FIBER_SAVE_ERROR that are suppor...
Definition: fiber.h:65
fiber_t * fiber_func_t(fiber_t *fiber, void *arg)
The type of the function executed by a fiber.
Definition: fiber.h:96
void fiber_destroy(fiber_t *fiber)
Destroys the specified fiber.
Definition: fiber-sjlj.c:299
fiber_t * fiber_create(fiber_func_t *func, void *arg, int flags, size_t data_size, size_t stack_size)
Creates a new fiber, allocates a stack and sets up a calling environment to begin executing the speci...
Definition: fiber-sjlj.c:152
This is the internal header file of the fiber implementation.
#define LELY_FIBER_MINSTKSZ
The minimum size (in bytes) of a fiber stack frame.
Definition: fiber.h:33
#define LELY_FIBER_STKSZ
The default size (in bytes) of a fiber stack frame.
Definition: fiber.h:41
This header file is part of the C11 and POSIX compatibility library; it includes <stddef....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....