Line | Branch | Exec | Source |
---|---|---|---|
1 | /* GATE PROJECT LICENSE: | ||
2 | +----------------------------------------------------------------------------+ | ||
3 | | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> | | ||
4 | | All rights reserved. | | ||
5 | | | | ||
6 | | Redistribution and use in source and binary forms, with or without | | ||
7 | | modification, are permitted provided that the following conditions are met:| | ||
8 | | | | ||
9 | | 1. Redistributions of source code must retain the above copyright notice, | | ||
10 | | this list of conditions and the following disclaimer. | | ||
11 | | 2. Redistributions in binary form must reproduce the above copyright | | ||
12 | | notice, this list of conditions and the following disclaimer in the | | ||
13 | | documentation and/or other materials provided with the distribution. | | ||
14 | | | | ||
15 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"| | ||
16 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | ||
17 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ||
18 | | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | | ||
19 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | ||
20 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | ||
21 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | ||
22 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | ||
23 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | ||
24 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | | ||
25 | | THE POSSIBILITY OF SUCH DAMAGE. | | ||
26 | +----------------------------------------------------------------------------+ | ||
27 | */ | ||
28 | |||
29 | #include "gate/coroutines.h" | ||
30 | #include "gate/results.h" | ||
31 | #include "gate/threading.h" | ||
32 | #include "gate/debugging.h" | ||
33 | #include "gate/times.h" | ||
34 | #include "gate/atomics.h" | ||
35 | |||
36 | #if defined(GATE_SYS_WIN) && !defined(GATE_SYS_WIN16) | ||
37 | # if defined(GATE_WIN32_ANSI) | ||
38 | # define GATE_COROUTINES_THREADS 1 | ||
39 | # else | ||
40 | # define GATE_COROUTINES_WINAPI 1 | ||
41 | # endif | ||
42 | /*# define GATE_COROUTINES_NATIVE_STACKSWITCH*/ | ||
43 | #elif defined(GATE_SYS_POSIX) | ||
44 | # if defined(GATE_SYS_BEOS) | ||
45 | # define GATE_COROUTINES_THREADS 1 | ||
46 | # else | ||
47 | # define GATE_COROUTINES_POSIX 1 | ||
48 | # endif | ||
49 | #elif defined(GATE_SYS_EFI) || defined(GATE_SYS_DOS) | ||
50 | # define GATE_COROUTINES_NATIVE_STACKSWITCH 1 | ||
51 | #else | ||
52 | # define GATE_COROUTINES_NOIMPL | ||
53 | //# define GATE_COROUTINES_THREADS 1 | ||
54 | #endif | ||
55 | |||
56 | |||
57 | /**************************** | ||
58 | * GENERIC implementation * | ||
59 | ****************************/ | ||
60 | |||
61 | |||
62 | ✗ | gate_result_t gate_coroutine_sleep(gate_uint32_t milliseconds) | |
63 | { | ||
64 | gate_result_t ret; | ||
65 | gate_timecounter_t tc_start; | ||
66 | gate_timecounter_t tc_now; | ||
67 | ✗ | gate_int64_t max_diff_micro = (gate_int64_t)milliseconds * (gate_int64_t)1000; | |
68 | |||
69 | do | ||
70 | { | ||
71 | ✗ | ret = gate_timecounter_now(&tc_start); | |
72 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
73 | |||
74 | do | ||
75 | { | ||
76 | ✗ | ret = gate_coroutine_yield(); | |
77 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
78 | ✗ | ret = gate_timecounter_now(&tc_now); | |
79 | ✗ | GATE_BREAK_IF_FAILED(ret); | |
80 | ✗ | } while (gate_timecounter_diff(tc_now, tc_start) < max_diff_micro); | |
81 | |||
82 | } while (0); | ||
83 | |||
84 | ✗ | return ret; | |
85 | } | ||
86 | |||
87 | |||
88 | |||
89 | |||
90 | |||
91 | |||
92 | |||
93 | #if defined(GATE_COROUTINES_WINAPI) | ||
94 | |||
95 | #include "gate/platforms.h" | ||
96 | |||
97 | #if defined(GATE_SYS_WINCE) | ||
98 | |||
99 | typedef VOID(CALLBACK* win32_fiber_start_routine)(PVOID); | ||
100 | |||
101 | #define win32_has_fibers() false | ||
102 | |||
103 | LPVOID win32_create_fiber(DWORD dwStackSize, | ||
104 | win32_fiber_start_routine lpStartAddress, | ||
105 | LPVOID lpParameter) | ||
106 | { | ||
107 | //CreateFiber | ||
108 | return NULL; | ||
109 | } | ||
110 | |||
111 | VOID win32_switch_to_fiber(LPVOID lpFiber) | ||
112 | { | ||
113 | //SwitchToFiber | ||
114 | } | ||
115 | |||
116 | LPVOID win32_convert_thread_to_fiber(LPVOID lpParameter) | ||
117 | { | ||
118 | //ConvertThreadToFiber | ||
119 | return NULL; | ||
120 | } | ||
121 | |||
122 | VOID WINAPI win32_delete_fiber(LPVOID lpFiber) | ||
123 | { | ||
124 | //DeleteFiber | ||
125 | } | ||
126 | |||
127 | LPVOID win32_get_current_fiber(void) | ||
128 | { | ||
129 | //GetCurrentFiber | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | LPVOID WINAPI win32_get_fiber_data(void) | ||
134 | { | ||
135 | //GetFiberData | ||
136 | return NULL; | ||
137 | } | ||
138 | |||
139 | |||
140 | #else | ||
141 | |||
142 | #define win32_has_fibers() (gate_win32_fibers() != NULL) | ||
143 | |||
144 | # define win32_create_fiber gate_win32_fibers()->Win32CreateFiber | ||
145 | # define win32_switch_to_fiber gate_win32_fibers()->Win32SwitchToFiber | ||
146 | # define win32_convert_thread_to_fiber gate_win32_fibers()->Win32ConvertThreadToFiber | ||
147 | # define win32_delete_fiber gate_win32_fibers()->Win32DeleteFiber | ||
148 | # define win32_get_current_fiber gate_win32_fibers()->Win32GetCurrentFiber | ||
149 | # define win32_get_fiber_data gate_win32_fibers()->Win32GetFiberData | ||
150 | # define win32_is_thread_a_fiber gate_win32_fibers()->Win32IsThreadAFiber | ||
151 | |||
152 | #endif | ||
153 | |||
154 | #define GATE_WIN32_COROUTINE_STATE_NONE 0 | ||
155 | #define GATE_WIN32_COROUTINE_STATE_RUNNING 1 | ||
156 | #define GATE_WIN32_COROUTINE_STATE_COMPLETED 2 | ||
157 | |||
158 | typedef struct gate_win32_coroutine_starter_class | ||
159 | { | ||
160 | gate_coroutine_function_t function; | ||
161 | void* param; | ||
162 | } gate_win32_coroutine_starter_t; | ||
163 | |||
164 | typedef struct gate_win32_coroutine_context_class | ||
165 | { | ||
166 | LPVOID address; | ||
167 | DWORD state; | ||
168 | gate_result_t result; | ||
169 | gate_win32_coroutine_starter_t starter; | ||
170 | } gate_win32_coroutine_context_t; | ||
171 | |||
172 | typedef struct gate_win32_coroutine_session_class | ||
173 | { | ||
174 | LPVOID master; | ||
175 | gate_index_t current; | ||
176 | gate_index_t try_next; | ||
177 | gate_win32_coroutine_context_t contexts[256]; | ||
178 | } gate_win32_coroutine_session_t; | ||
179 | |||
180 | #ifndef TLS_OUT_OF_INDEXES | ||
181 | #define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) | ||
182 | #endif | ||
183 | |||
184 | typedef struct gate_win32_thread_fiber_mapping_class | ||
185 | { | ||
186 | gate_win32_coroutine_session_t* session; | ||
187 | DWORD thread_id; | ||
188 | } gate_win32_thread_fiber_mapping_t; | ||
189 | |||
190 | |||
191 | static gate_win32_coroutine_session_t* gate_win32_coroutine_get_session() | ||
192 | { | ||
193 | if (win32_has_fibers()) | ||
194 | { | ||
195 | gate_win32_coroutine_session_t* ret = (gate_win32_coroutine_session_t*)win32_get_fiber_data(); | ||
196 | return ret; | ||
197 | } | ||
198 | return NULL; | ||
199 | } | ||
200 | |||
201 | static gate_index_t gate_win32_coroutine_get_context_index(gate_win32_coroutine_session_t* session) | ||
202 | { | ||
203 | gate_index_t ret = -1; | ||
204 | gate_size_t index = 0; | ||
205 | gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]); | ||
206 | PVOID address; | ||
207 | if (!win32_has_fibers()) | ||
208 | { | ||
209 | return ret; | ||
210 | } | ||
211 | address = win32_get_current_fiber(); | ||
212 | if (address) | ||
213 | { | ||
214 | for (index = 0; index != count; ++index) | ||
215 | { | ||
216 | if (session->contexts[index].address == address) | ||
217 | { | ||
218 | ret = (gate_index_t)index; | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | static gate_win32_coroutine_context_t* gate_win32_coroutine_get_context(gate_win32_coroutine_session_t* session) | ||
227 | { | ||
228 | gate_index_t index = gate_win32_coroutine_get_context_index(session); | ||
229 | if (index < 0) | ||
230 | { | ||
231 | return NULL; | ||
232 | } | ||
233 | return &session->contexts[index]; | ||
234 | } | ||
235 | |||
236 | |||
237 | static void gate_win32_coroutine_release_context(gate_win32_coroutine_context_t* context) | ||
238 | { | ||
239 | if (context->address != NULL) | ||
240 | { | ||
241 | win32_delete_fiber(context->address); | ||
242 | } | ||
243 | gate_mem_clear(context, sizeof(gate_win32_coroutine_context_t)); | ||
244 | } | ||
245 | |||
246 | static gate_index_t gate_win32_coroutine_get_next_index(gate_win32_coroutine_session_t* session, gate_index_t try_start_at) | ||
247 | { | ||
248 | gate_index_t ret = -1; | ||
249 | gate_size_t index = 0; | ||
250 | gate_size_t test_index = 0; | ||
251 | gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]); | ||
252 | gate_win32_coroutine_context_t* context; | ||
253 | for (index = 0; index != count; ++index) | ||
254 | { | ||
255 | test_index = (((gate_size_t)try_start_at) + index) % count; | ||
256 | context = &session->contexts[test_index]; | ||
257 | if (GATE_WIN32_COROUTINE_STATE_NONE == context->state) | ||
258 | { | ||
259 | /* inactive context -> clean up if previous fiber was present */ | ||
260 | gate_win32_coroutine_release_context(context); | ||
261 | } | ||
262 | else if (GATE_WIN32_COROUTINE_STATE_RUNNING == context->state) | ||
263 | { | ||
264 | /* active context found */ | ||
265 | ret = (gate_index_t)test_index; | ||
266 | break; | ||
267 | } | ||
268 | } | ||
269 | return ret; | ||
270 | } | ||
271 | static gate_index_t gate_win32_coroutine_get_free_index(gate_win32_coroutine_session_t* session) | ||
272 | { | ||
273 | gate_index_t ret = -1; | ||
274 | gate_size_t index = 0; | ||
275 | gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]); | ||
276 | gate_win32_coroutine_context_t* context; | ||
277 | for (index = 0; index != count; ++index) | ||
278 | { | ||
279 | context = &session->contexts[index]; | ||
280 | if (context->state == GATE_WIN32_COROUTINE_STATE_NONE) | ||
281 | { | ||
282 | gate_win32_coroutine_release_context(context); | ||
283 | ret = (gate_index_t)index; | ||
284 | break; | ||
285 | } | ||
286 | } | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | |||
291 | #ifndef CALLBACK | ||
292 | #define CALLBACK __stdcall | ||
293 | #endif | ||
294 | |||
295 | |||
296 | static void CALLBACK gate_win32_coroutine_dispatcher(LPVOID lpFiberParameter) | ||
297 | { | ||
298 | gate_win32_coroutine_session_t* session = gate_win32_coroutine_get_session(); | ||
299 | gate_win32_coroutine_context_t* ptr_context = gate_win32_coroutine_get_context(session); | ||
300 | gate_win32_coroutine_context_t* context; | ||
301 | gate_index_t index = gate_win32_coroutine_get_context_index(session); | ||
302 | gate_result_t co_func_result; | ||
303 | |||
304 | GATE_UNUSED_ARG(lpFiberParameter); | ||
305 | |||
306 | if (index >= 0) | ||
307 | { | ||
308 | context = &session->contexts[index]; | ||
309 | |||
310 | if (context != ptr_context) | ||
311 | { | ||
312 | GATE_DEBUG_TRACE("Invalid coroutine context"); | ||
313 | } | ||
314 | |||
315 | if (context->starter.function) | ||
316 | { | ||
317 | context->state = GATE_WIN32_COROUTINE_STATE_RUNNING; | ||
318 | co_func_result = context->starter.function(context->starter.param); | ||
319 | context->result = co_func_result; | ||
320 | } | ||
321 | context->state = GATE_WIN32_COROUTINE_STATE_COMPLETED; | ||
322 | win32_switch_to_fiber(session->master); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | #ifndef FIBER_FLAG_FLOAT_SWITCH | ||
327 | #define FIBER_FLAG_FLOAT_SWITCH 0x1 // context switch floating point | ||
328 | #endif | ||
329 | |||
330 | #ifndef WINAPI | ||
331 | #define WINAPI __stdcall | ||
332 | #endif | ||
333 | |||
334 | static gate_result_t gate_win32_coroutine_activate_context(gate_win32_coroutine_session_t* session, gate_index_t ctx_index) | ||
335 | { | ||
336 | gate_index_t const context_max = sizeof(session->contexts) / sizeof(session->contexts[0]); | ||
337 | if ((ctx_index < 0) || (ctx_index >= context_max)) | ||
338 | { | ||
339 | return GATE_RESULT_INVALIDARG; | ||
340 | } | ||
341 | if (session->contexts[ctx_index].state != GATE_WIN32_COROUTINE_STATE_RUNNING) | ||
342 | { | ||
343 | return GATE_RESULT_INVALIDARG; | ||
344 | } | ||
345 | session->current = ctx_index; | ||
346 | session->try_next = ctx_index + 1; | ||
347 | win32_switch_to_fiber(session->contexts[ctx_index].address); | ||
348 | return GATE_RESULT_OK; | ||
349 | } | ||
350 | |||
351 | static DWORD WINAPI gate_coroutine_scheduler(LPVOID lpThreadParameter) | ||
352 | { | ||
353 | gate_result_t ret = GATE_RESULT_OK; | ||
354 | gate_win32_coroutine_session_t session = GATE_INIT_EMPTY; | ||
355 | gate_index_t next_index; | ||
356 | gate_win32_coroutine_starter_t const* ptr_starter = (gate_win32_coroutine_starter_t const*)lpThreadParameter; | ||
357 | |||
358 | do | ||
359 | { | ||
360 | gate_mem_clear(&session, sizeof(session)); | ||
361 | |||
362 | #if defined(GATE_SYS_WINSTORE) | ||
363 | session.master = ConvertThreadToFiberEx((LPVOID)&session, FIBER_FLAG_FLOAT_SWITCH); | ||
364 | #else | ||
365 | session.master = win32_convert_thread_to_fiber((LPVOID)&session); | ||
366 | #endif | ||
367 | if (NULL == session.master) | ||
368 | { | ||
369 | ret = GATE_RESULT_FAILED; | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | ret = gate_coroutine_create(ptr_starter->function, ptr_starter->param, NULL); | ||
374 | session.try_next = 0; | ||
375 | |||
376 | while (GATE_SUCCEEDED(ret)) | ||
377 | { | ||
378 | next_index = gate_win32_coroutine_get_next_index(&session, session.try_next); | ||
379 | if (next_index < 0) | ||
380 | { | ||
381 | /* all coroutines have exited */ | ||
382 | break; | ||
383 | } | ||
384 | gate_win32_coroutine_activate_context(&session, next_index); | ||
385 | } | ||
386 | |||
387 | } while (0); | ||
388 | |||
389 | return ret; | ||
390 | } | ||
391 | |||
392 | gate_bool_t gate_coroutine_supported() | ||
393 | { | ||
394 | gate_win32_fibers_t* fibers = gate_win32_fibers(); | ||
395 | return fibers != NULL; | ||
396 | } | ||
397 | |||
398 | gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param) | ||
399 | { | ||
400 | gate_win32_coroutine_starter_t starter; | ||
401 | HANDLE thread; | ||
402 | DWORD exit_code = (DWORD)GATE_RESULT_FAILED; | ||
403 | |||
404 | starter.function = func; | ||
405 | starter.param = param; | ||
406 | thread = gate_win32_createthread(&gate_coroutine_scheduler, (LPVOID)&starter, FALSE, NULL); | ||
407 | |||
408 | if (NULL == thread) | ||
409 | { | ||
410 | return GATE_RESULT_OUTOFRESOURCES; | ||
411 | } | ||
412 | else | ||
413 | { | ||
414 | WaitForSingleObject(thread, INFINITE); | ||
415 | GetExitCodeThread(thread, &exit_code); | ||
416 | gate_win32_closehandle(thread); | ||
417 | return (gate_result_t)exit_code; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | gate_bool_t gate_coroutine_enabled() | ||
422 | { | ||
423 | return NULL != gate_win32_coroutine_get_session(); | ||
424 | } | ||
425 | gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id) | ||
426 | { | ||
427 | gate_win32_coroutine_session_t* session = gate_win32_coroutine_get_session(); | ||
428 | if (session) | ||
429 | { | ||
430 | gate_index_t index = gate_win32_coroutine_get_context_index(session); | ||
431 | if (index >= 0) | ||
432 | { | ||
433 | if (ptr_id) | ||
434 | { | ||
435 | *ptr_id = (gate_coroutine_id_t)index; | ||
436 | } | ||
437 | return GATE_RESULT_OK; | ||
438 | } | ||
439 | } | ||
440 | return GATE_RESULT_NOTAVAILABLE; | ||
441 | } | ||
442 | gate_result_t gate_coroutine_yield() | ||
443 | { | ||
444 | gate_win32_coroutine_session_t* session = gate_win32_coroutine_get_session(); | ||
445 | if (session) | ||
446 | { | ||
447 | win32_switch_to_fiber(session->master); | ||
448 | return GATE_RESULT_OK; | ||
449 | } | ||
450 | return GATE_RESULT_NOTAVAILABLE; | ||
451 | } | ||
452 | gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id) | ||
453 | { | ||
454 | gate_win32_coroutine_starter_t starter; | ||
455 | gate_result_t ret = GATE_RESULT_FAILED; | ||
456 | gate_win32_coroutine_session_t* session = gate_win32_coroutine_get_session(); | ||
457 | gate_win32_coroutine_context_t* context; | ||
458 | gate_index_t index; | ||
459 | LPVOID fiber_address; | ||
460 | |||
461 | do | ||
462 | { | ||
463 | if (session == NULL) | ||
464 | { | ||
465 | ret = GATE_RESULT_INVALIDSTATE; | ||
466 | break; | ||
467 | } | ||
468 | starter.function = func; | ||
469 | starter.param = param; | ||
470 | index = gate_win32_coroutine_get_free_index(session); | ||
471 | if (index < 0) | ||
472 | { | ||
473 | ret = GATE_RESULT_OUTOFRESOURCES; | ||
474 | break; | ||
475 | } | ||
476 | context = &session->contexts[index]; | ||
477 | gate_mem_copy(&context->starter, &starter, sizeof(gate_win32_coroutine_starter_t)); | ||
478 | #if defined(GATE_SYS_WINSTORE) | ||
479 | fiber_address = CreateFiberEx(0, 0, FIBER_FLAG_FLOAT_SWITCH, &gate_win32_coroutine_dispatcher, session); | ||
480 | #else | ||
481 | fiber_address = win32_create_fiber(0, &gate_win32_coroutine_dispatcher, session); | ||
482 | #endif | ||
483 | if (fiber_address == NULL) | ||
484 | { | ||
485 | ret = GATE_RESULT_FAILED; | ||
486 | context->state = GATE_WIN32_COROUTINE_STATE_NONE; | ||
487 | break; | ||
488 | } | ||
489 | else | ||
490 | { | ||
491 | context->address = fiber_address; | ||
492 | context->state = GATE_WIN32_COROUTINE_STATE_RUNNING; | ||
493 | if (ptr_id) | ||
494 | { | ||
495 | *ptr_id = index; | ||
496 | } | ||
497 | ret = GATE_RESULT_OK; | ||
498 | } | ||
499 | } while (0); | ||
500 | |||
501 | return ret; | ||
502 | } | ||
503 | gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id) | ||
504 | { | ||
505 | gate_result_t ret = GATE_RESULT_FAILED; | ||
506 | gate_win32_coroutine_session_t* session = gate_win32_coroutine_get_session(); | ||
507 | gate_index_t index = (gate_index_t)id; | ||
508 | |||
509 | if (session == NULL) | ||
510 | { | ||
511 | return GATE_RESULT_INVALIDSTATE; | ||
512 | } | ||
513 | |||
514 | ret = gate_win32_coroutine_activate_context(session, index); | ||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result) | ||
519 | { | ||
520 | gate_result_t ret = GATE_RESULT_FAILED; | ||
521 | gate_index_t index = (gate_index_t)id; | ||
522 | gate_win32_coroutine_session_t* session = gate_win32_coroutine_get_session(); | ||
523 | gate_win32_coroutine_context_t* ptr_context; | ||
524 | LPVOID co_address; | ||
525 | gate_result_t result; | ||
526 | |||
527 | do | ||
528 | { | ||
529 | if (session == NULL) | ||
530 | { | ||
531 | break; | ||
532 | } | ||
533 | if ((index < 0) || (index >= sizeof(session->contexts) / sizeof(session->contexts[0]))) | ||
534 | { | ||
535 | ret = GATE_RESULT_INVALIDARG; | ||
536 | break; | ||
537 | } | ||
538 | ptr_context = &session->contexts[index]; | ||
539 | co_address = ptr_context->address; | ||
540 | |||
541 | if (co_address == NULL) | ||
542 | { | ||
543 | ret = GATE_RESULT_INVALIDSTATE; | ||
544 | break; | ||
545 | } | ||
546 | |||
547 | while (ptr_context->state == GATE_WIN32_COROUTINE_STATE_RUNNING) | ||
548 | { | ||
549 | result = gate_coroutine_switch_to(id); | ||
550 | GATE_BREAK_IF_FAILED(result); | ||
551 | } | ||
552 | |||
553 | if ((co_address == ptr_context->address) || (ptr_context->state != GATE_WIN32_COROUTINE_STATE_COMPLETED)) | ||
554 | { | ||
555 | ret = GATE_RESULT_INVALIDSTATE; | ||
556 | break; | ||
557 | } | ||
558 | |||
559 | if (ptr_coroutine_result) | ||
560 | { | ||
561 | *ptr_coroutine_result = ptr_context->result; | ||
562 | } | ||
563 | |||
564 | gate_mem_clear(ptr_context, sizeof(gate_win32_coroutine_context_t)); | ||
565 | ret = GATE_RESULT_OK; | ||
566 | |||
567 | } while (0); | ||
568 | return ret; | ||
569 | } | ||
570 | |||
571 | |||
572 | #endif | ||
573 | |||
574 | #if defined(GATE_COROUTINES_POSIX) | ||
575 | |||
576 | #include "gate/platform/posix/posixucontext.h" | ||
577 | |||
578 | |||
579 | #define GATE_POSIX_CO_STATE_UNINIT 0 | ||
580 | #define GATE_POSIX_CO_STATE_RUNNING 1 | ||
581 | #define GATE_POSIX_CO_STATE_COMPLETED 2 | ||
582 | |||
583 | |||
584 | #define GATE_POSIX_CO_DEFAULT_STACK_SIZE (1024 * 256) | ||
585 | |||
586 | |||
587 | typedef struct gate_posix_co_session_class gate_posix_co_session_t; | ||
588 | |||
589 | typedef struct gate_posix_co_context_class | ||
590 | { | ||
591 | gate_posix_co_session_t volatile* session; | ||
592 | gate_posix_uctx_t volatile uctx; | ||
593 | void* stack; | ||
594 | unsigned state; | ||
595 | gate_coroutine_function_t func; | ||
596 | void* param; | ||
597 | gate_result_t result; | ||
598 | } gate_posix_co_context_t; | ||
599 | |||
600 | struct gate_posix_co_session_class | ||
601 | { | ||
602 | gate_posix_uctx_t volatile master_ctx; | ||
603 | |||
604 | gate_posix_co_context_t volatile contexts[64]; | ||
605 | gate_index_t volatile current_context; | ||
606 | |||
607 | }; | ||
608 | |||
609 | typedef union gate_posix_co_entry_args | ||
610 | { | ||
611 | int items[4]; | ||
612 | gate_posix_co_context_t volatile* ptr_ctx; | ||
613 | } gate_posix_co_entry_args_t; | ||
614 | |||
615 | static gate_thread_storage_t global_posix_co_threadstore_key = GATE_INIT_EMPTY; | ||
616 | static gate_atomic_flag_t global_posix_co_threadstore_key_init = GATE_ATOMIC_FLAG_INIT; | ||
617 | |||
618 | 57 | static gate_result_t gate_posix_co_init() | |
619 | { | ||
620 | 57 | gate_result_t ret = GATE_RESULT_FAILED; | |
621 | |||
622 | 57 | gate_bool_t was_init = gate_atomic_flag_set(&global_posix_co_threadstore_key_init); | |
623 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 55 times.
|
57 | if (!was_init) |
624 | { | ||
625 | 2 | ret = gate_posix_uctx_init(); | |
626 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (GATE_FAILED(ret)) |
627 | { | ||
628 | ✗ | gate_atomic_flag_clear(&global_posix_co_threadstore_key_init); | |
629 | ✗ | ret = GATE_RESULT_NOTSUPPORTED; | |
630 | } | ||
631 | else | ||
632 | { | ||
633 | 2 | ret = gate_thread_storage_alloc(&global_posix_co_threadstore_key); | |
634 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (GATE_FAILED(ret)) |
635 | { | ||
636 | ✗ | gate_atomic_flag_clear(&global_posix_co_threadstore_key_init); | |
637 | } | ||
638 | } | ||
639 | |||
640 | } | ||
641 | else | ||
642 | { | ||
643 | 55 | ret = GATE_RESULT_OK; | |
644 | } | ||
645 | 57 | return ret; | |
646 | } | ||
647 | |||
648 | 2 | gate_bool_t gate_coroutine_supported() | |
649 | { | ||
650 | 2 | gate_result_t result = gate_posix_co_init(); | |
651 | 2 | return GATE_SUCCEEDED(result); | |
652 | } | ||
653 | |||
654 | 15 | static gate_posix_co_session_t volatile* gate_posix_co_session_register(gate_posix_co_session_t volatile* session) | |
655 | { | ||
656 | gate_result_t result; | ||
657 | 15 | void* previous_value = NULL; | |
658 | 15 | result = gate_thread_storage_get(&global_posix_co_threadstore_key, &previous_value); | |
659 | 15 | result = gate_thread_storage_set(&global_posix_co_threadstore_key, (void*)session); | |
660 | 15 | return (gate_posix_co_session_t volatile*)previous_value; | |
661 | } | ||
662 | |||
663 | 9 | static void gate_posix_co_dispatcher(int a0, int a1, int a2, int a3) | |
664 | { | ||
665 | gate_posix_co_entry_args_t args; | ||
666 | gate_posix_co_context_t volatile* ptr_ctx; | ||
667 | gate_posix_co_session_t volatile* old_session; | ||
668 | |||
669 | 9 | args.items[0] = a0; | |
670 | 9 | args.items[1] = a1; | |
671 | 9 | args.items[2] = a2; | |
672 | 9 | args.items[3] = a3; | |
673 | |||
674 | 9 | ptr_ctx = args.ptr_ctx; | |
675 | |||
676 | 9 | old_session = gate_posix_co_session_register(ptr_ctx->session); | |
677 | |||
678 | 9 | ptr_ctx->result = ptr_ctx->func(ptr_ctx->param); | |
679 | 9 | ptr_ctx->state = GATE_POSIX_CO_STATE_COMPLETED; | |
680 | |||
681 | 9 | gate_posix_uctx_swap((void*)&ptr_ctx->uctx, (void*)&ptr_ctx->session->master_ctx); | |
682 | ✗ | } | |
683 | |||
684 | 46 | static gate_posix_co_context_t volatile* gate_posix_co_get_context() | |
685 | { | ||
686 | gate_result_t result; | ||
687 | 46 | void* data = NULL; | |
688 | gate_posix_co_session_t volatile* volatile ptr_session; | ||
689 | gate_posix_co_context_t volatile* ptr_ctx; | ||
690 | gate_uintptr_t local_address; | ||
691 | gate_uintptr_t stack_address_begin; | ||
692 | gate_uintptr_t stack_address_end; | ||
693 | gate_size_t ctx_count; | ||
694 | gate_size_t ndx; | ||
695 | |||
696 | 46 | result = gate_posix_co_init(); | |
697 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
|
46 | if (GATE_FAILED(result)) |
698 | { | ||
699 | ✗ | return NULL; | |
700 | } | ||
701 | |||
702 | 46 | result = gate_thread_storage_get(&global_posix_co_threadstore_key, &data); | |
703 |
3/4✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 41 times.
|
46 | if (GATE_FAILED(result) || (data == NULL)) |
704 | { | ||
705 | 5 | return NULL; | |
706 | } | ||
707 | 41 | ptr_session = (gate_posix_co_session_t volatile*)data; | |
708 | 41 | local_address = (gate_uintptr_t)&ptr_session; | |
709 | 41 | ctx_count = sizeof(ptr_session->contexts) / sizeof(ptr_session->contexts[0]); | |
710 |
1/2✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
|
55 | for (ndx = 0; ndx != ctx_count; ++ndx) |
711 | { | ||
712 | 55 | ptr_ctx = &ptr_session->contexts[ndx]; | |
713 | 55 | stack_address_begin = (gate_uintptr_t)ptr_ctx->stack; | |
714 | 55 | stack_address_end = stack_address_begin + GATE_POSIX_CO_DEFAULT_STACK_SIZE; | |
715 |
4/4✓ Branch 0 taken 51 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 41 times.
✓ Branch 3 taken 10 times.
|
55 | if ((local_address >= stack_address_begin) && (local_address <= stack_address_end)) |
716 | { | ||
717 | 41 | return ptr_ctx; | |
718 | } | ||
719 | } | ||
720 | ✗ | return NULL; | |
721 | } | ||
722 | |||
723 | 9 | static gate_result_t gate_posix_co_context_create(gate_posix_co_context_t volatile* ctx, | |
724 | gate_posix_co_session_t volatile* session, | ||
725 | gate_coroutine_function_t func, void* param) | ||
726 | { | ||
727 | 9 | void* stack_space = gate_mem_alloc(GATE_POSIX_CO_DEFAULT_STACK_SIZE); | |
728 | gate_posix_co_entry_args_t entry_args; | ||
729 | int* p; | ||
730 | |||
731 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (stack_space == NULL) |
732 | { | ||
733 | ✗ | return GATE_RESULT_OUTOFMEMORY; | |
734 | } | ||
735 | 9 | gate_mem_clear((void*)ctx, sizeof(gate_posix_co_context_t)); | |
736 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if (0 != gate_posix_uctx_get((void*)&ctx->uctx)) |
737 | { | ||
738 | ✗ | gate_mem_dealloc(stack_space); | |
739 | ✗ | return GATE_RESULT_OUTOFRESOURCES; | |
740 | } | ||
741 | 9 | gate_posix_uctx_setup((void*)&ctx->uctx, stack_space, GATE_POSIX_CO_DEFAULT_STACK_SIZE); | |
742 | 9 | ctx->stack = stack_space; | |
743 | 9 | ctx->session = session; | |
744 | 9 | ctx->state = GATE_POSIX_CO_STATE_RUNNING; | |
745 | 9 | ctx->func = func; | |
746 | 9 | ctx->param = param; | |
747 | 9 | ctx->result = GATE_RESULT_UNKNOWNERROR; | |
748 | |||
749 | 9 | entry_args.ptr_ctx = ctx; | |
750 | 9 | p = &entry_args.items[0]; | |
751 | 9 | gate_posix_uctx_make((void*)&ctx->uctx, &gate_posix_co_dispatcher, p[0], p[1], p[2], p[3]); | |
752 | 9 | return GATE_RESULT_OK; | |
753 | } | ||
754 | |||
755 | 38 | static void gate_posix_co_swap_context(gate_posix_co_session_t volatile* session, | |
756 | gate_index_t new_context_index, | ||
757 | void volatile* old_context) | ||
758 | { | ||
759 | 38 | session->current_context = new_context_index; | |
760 | 38 | gate_posix_uctx_swap((void*)old_context, (void*)&session->contexts[new_context_index].uctx); | |
761 | 38 | } | |
762 | |||
763 | 196 | static gate_result_t gate_posix_co_context_destroy(gate_posix_co_context_t volatile* ctx) | |
764 | { | ||
765 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 187 times.
|
196 | if (ctx->stack != NULL) |
766 | { | ||
767 | 9 | gate_mem_dealloc(ctx->stack); | |
768 | } | ||
769 | 196 | gate_mem_clear((void*)ctx, sizeof(gate_posix_co_context_t)); | |
770 | 196 | return GATE_RESULT_OK; | |
771 | } | ||
772 | |||
773 | 29 | static gate_index_t gate_posix_co_get_context_index(gate_posix_co_context_t volatile* ptr_ctx) | |
774 | { | ||
775 | gate_size_t ndx; | ||
776 | 29 | gate_posix_co_session_t volatile* session = ptr_ctx->session; | |
777 | 29 | gate_size_t cnt = sizeof(session->contexts) / sizeof(session->contexts[0]); | |
778 | |||
779 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | for (ndx = 0; ndx != cnt; ++ndx) |
780 | { | ||
781 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 10 times.
|
39 | if (&session->contexts[ndx] == ptr_ctx) |
782 | { | ||
783 | 29 | return (gate_index_t)ndx; | |
784 | } | ||
785 | } | ||
786 | ✗ | return -1; | |
787 | } | ||
788 | |||
789 | 6 | static gate_index_t gate_posix_co_get_free_context(gate_posix_co_session_t volatile* session) | |
790 | { | ||
791 | gate_size_t ndx; | ||
792 | 6 | gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]); | |
793 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13 | for (ndx = 0; ndx != count; ++ndx) |
794 | { | ||
795 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 7 times.
|
13 | if (session->contexts[ndx].state == GATE_POSIX_CO_STATE_UNINIT) |
796 | { | ||
797 | 6 | return ndx; | |
798 | } | ||
799 | } | ||
800 | ✗ | return -1; | |
801 | } | ||
802 | |||
803 | |||
804 | 3 | gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param) | |
805 | { | ||
806 | 3 | gate_result_t ret = GATE_RESULT_FAILED; | |
807 | 3 | gate_posix_co_session_t volatile session = GATE_INIT_EMPTY; | |
808 | 3 | gate_posix_co_session_t volatile* ptr_old_session = NULL; | |
809 | gate_index_t ndx; | ||
810 | gate_index_t cnt; | ||
811 | gate_index_t new_index; | ||
812 | gate_index_t next_index; | ||
813 | |||
814 | 3 | ret = gate_posix_co_init(); | |
815 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (GATE_FAILED(ret)) |
816 | { | ||
817 | ✗ | return ret; | |
818 | } | ||
819 | |||
820 | do | ||
821 | { | ||
822 | 3 | gate_mem_clear((void*)&session, sizeof(session)); | |
823 | 3 | ptr_old_session = gate_posix_co_session_register(&session); | |
824 | |||
825 | 3 | ret = gate_posix_co_context_create(&session.contexts[0], &session, func, param); | |
826 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | GATE_BREAK_IF_FAILED(ret); |
827 | |||
828 | 3 | cnt = sizeof(session.contexts) / sizeof(session.contexts[0]); | |
829 | 3 | session.current_context = -1; | |
830 | for (;;) | ||
831 | { | ||
832 | 33 | next_index = -1; | |
833 |
2/2✓ Branch 0 taken 1093 times.
✓ Branch 1 taken 3 times.
|
1096 | for (ndx = 0; ndx != cnt; ++ndx) |
834 | { | ||
835 | 1093 | new_index = (gate_index_t)((gate_size_t)(ndx + session.current_context + 1) % (gate_size_t)cnt); | |
836 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1060 times.
|
1093 | if (session.contexts[new_index].state == GATE_POSIX_CO_STATE_RUNNING) |
837 | { | ||
838 | 33 | next_index = new_index; | |
839 | 33 | break; | |
840 | } | ||
841 | } | ||
842 | |||
843 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33 times.
|
36 | if (next_index < 0) |
844 | { | ||
845 | 3 | break; | |
846 | } | ||
847 | 33 | gate_posix_co_swap_context(&session, next_index, &session.master_ctx); | |
848 | } | ||
849 |
2/2✓ Branch 0 taken 192 times.
✓ Branch 1 taken 3 times.
|
195 | for (ndx = 0; ndx != cnt; ++ndx) |
850 | { | ||
851 | 192 | gate_posix_co_context_destroy(&session.contexts[ndx]); | |
852 | } | ||
853 | } while (0); | ||
854 | |||
855 | 3 | gate_posix_co_session_register(ptr_old_session); | |
856 | |||
857 | 3 | return ret; | |
858 | } | ||
859 | |||
860 | 6 | gate_bool_t gate_coroutine_enabled() | |
861 | { | ||
862 | 6 | gate_result_t result = gate_posix_co_init(); | |
863 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (GATE_FAILED(result)) |
864 | { | ||
865 | ✗ | return false; | |
866 | } | ||
867 | 6 | return gate_posix_co_get_context() != NULL; | |
868 | } | ||
869 | |||
870 | 1 | gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id) | |
871 | { | ||
872 | 1 | gate_posix_co_context_t volatile* ptr_ctx = gate_posix_co_get_context(); | |
873 | gate_index_t index; | ||
874 | |||
875 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (NULL == ptr_ctx) |
876 | { | ||
877 | ✗ | return GATE_RESULT_INVALIDSTATE; | |
878 | } | ||
879 | 1 | index = gate_posix_co_get_context_index(ptr_ctx); | |
880 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (index < 0) |
881 | { | ||
882 | ✗ | return GATE_RESULT_INVALIDSTATE; | |
883 | } | ||
884 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (ptr_id) |
885 | { | ||
886 | 1 | *ptr_id = (gate_coroutine_id_t)index; | |
887 | } | ||
888 | 1 | return GATE_RESULT_OK; | |
889 | } | ||
890 | |||
891 | 24 | gate_result_t gate_coroutine_yield() | |
892 | { | ||
893 | 24 | gate_result_t ret = GATE_RESULT_FAILED; | |
894 | 24 | gate_posix_co_context_t volatile* ptr_ctx = gate_posix_co_get_context(); | |
895 | gate_index_t current_index; | ||
896 | do | ||
897 | { | ||
898 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (ptr_ctx == NULL) |
899 | { | ||
900 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
901 | ✗ | break; | |
902 | } | ||
903 | 24 | current_index = gate_posix_co_get_context_index(ptr_ctx); | |
904 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (current_index < 0) |
905 | { | ||
906 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
907 | ✗ | break; | |
908 | } | ||
909 | 24 | gate_posix_uctx_swap((void*)&ptr_ctx->session->contexts[current_index].uctx, | |
910 | 24 | (void*)&ptr_ctx->session->master_ctx); | |
911 | 24 | ret = GATE_RESULT_OK; | |
912 | } while (0); | ||
913 | 24 | return ret; | |
914 | } | ||
915 | |||
916 | 6 | gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id) | |
917 | { | ||
918 | 6 | gate_result_t ret = GATE_RESULT_FAILED; | |
919 | 6 | gate_posix_co_session_t volatile* session = NULL; | |
920 | 6 | gate_posix_co_context_t volatile* current_context = gate_posix_co_get_context(); | |
921 | 6 | gate_posix_co_context_t volatile* new_context = NULL; | |
922 | 6 | gate_index_t index = -1; | |
923 | |||
924 | do | ||
925 | { | ||
926 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (current_context == NULL) |
927 | { | ||
928 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
929 | } | ||
930 | 6 | session = current_context->session; | |
931 | |||
932 | 6 | index = gate_posix_co_get_free_context(session); | |
933 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (index < 0) |
934 | { | ||
935 | ✗ | ret = GATE_RESULT_OUTOFRESOURCES; | |
936 | ✗ | break; | |
937 | } | ||
938 | |||
939 | 6 | new_context = &session->contexts[index]; | |
940 | 6 | gate_posix_co_context_create(new_context, session, func, param); | |
941 | |||
942 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (ptr_id) |
943 | { | ||
944 | 6 | *ptr_id = (gate_coroutine_id_t)index; | |
945 | } | ||
946 | 6 | ret = GATE_RESULT_OK; | |
947 | } while (0); | ||
948 | |||
949 | 6 | return ret; | |
950 | } | ||
951 | |||
952 | 5 | gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id) | |
953 | { | ||
954 | 5 | gate_result_t ret = GATE_RESULT_FAILED; | |
955 | 5 | gate_index_t next_index = (gate_index_t)id; | |
956 | 5 | gate_posix_co_context_t volatile* cur_context = gate_posix_co_get_context(); | |
957 | gate_posix_co_session_t volatile* session; | ||
958 | |||
959 | do | ||
960 | { | ||
961 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (cur_context == NULL) |
962 | { | ||
963 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
964 | ✗ | break; | |
965 | } | ||
966 | 5 | session = cur_context->session; | |
967 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
|
5 | if ((next_index < 0) || (next_index >= sizeof(session->contexts) / sizeof(session->contexts[0]))) |
968 | { | ||
969 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
970 | ✗ | break; | |
971 | } | ||
972 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (session->contexts[next_index].state != GATE_POSIX_CO_STATE_RUNNING) |
973 | { | ||
974 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
975 | ✗ | break; | |
976 | } | ||
977 | |||
978 | 5 | gate_posix_co_swap_context(session, next_index, &cur_context->uctx); | |
979 | 5 | ret = GATE_RESULT_OK; | |
980 | } while (0); | ||
981 | 5 | return ret; | |
982 | } | ||
983 | |||
984 | 4 | gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result) | |
985 | { | ||
986 | 4 | gate_result_t ret = GATE_RESULT_FAILED; | |
987 | 4 | gate_index_t index = (gate_index_t)id; | |
988 | 4 | gate_posix_co_context_t volatile* cur_context = gate_posix_co_get_context(); | |
989 | gate_posix_co_session_t volatile* session; | ||
990 | gate_index_t cur_index; | ||
991 | do | ||
992 | { | ||
993 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (cur_context == NULL) |
994 | { | ||
995 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
996 | ✗ | break; | |
997 | } | ||
998 | 4 | session = cur_context->session; | |
999 | 4 | cur_index = gate_posix_co_get_context_index(cur_context); | |
1000 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if ((index < 0) || (index >= sizeof(session->contexts) / sizeof(session->contexts[0]))) |
1001 | { | ||
1002 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
1003 | ✗ | break; | |
1004 | } | ||
1005 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (index == cur_index) |
1006 | { | ||
1007 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
1008 | ✗ | break; | |
1009 | } | ||
1010 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 4 times.
|
18 | while (session->contexts[index].state == GATE_POSIX_CO_STATE_RUNNING) |
1011 | { | ||
1012 | 14 | ret = gate_coroutine_yield(); | |
1013 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | GATE_BREAK_IF_FAILED(ret); |
1014 | } | ||
1015 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (session->contexts[index].state != GATE_POSIX_CO_STATE_COMPLETED) |
1016 | { | ||
1017 | ✗ | ret = GATE_RESULT_INVALIDSTATE; | |
1018 | ✗ | break; | |
1019 | } | ||
1020 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (ptr_coroutine_result) |
1021 | { | ||
1022 | 4 | *ptr_coroutine_result = session->contexts[index].result; | |
1023 | } | ||
1024 | 4 | gate_posix_co_context_destroy(&session->contexts[index]); | |
1025 | 4 | ret = GATE_RESULT_OK; | |
1026 | } while (0); | ||
1027 | 4 | return ret; | |
1028 | } | ||
1029 | |||
1030 | #endif /* GATE_COROUTINES_POSIX */ | ||
1031 | |||
1032 | |||
1033 | #if defined(GATE_COROUTINES_THREADS) | ||
1034 | |||
1035 | #include "gate/synchronization.h" | ||
1036 | |||
1037 | |||
1038 | #define GATE_THCO_STATE_NONE 0 | ||
1039 | #define GATE_THCO_STATE_RUNNING 1 | ||
1040 | #define GATE_THCO_STATE_COMPLETED 2 | ||
1041 | |||
1042 | typedef struct gate_thco_session_class gate_thco_session_t; | ||
1043 | |||
1044 | static gate_atomic_flag_t gate_thco_initialized = GATE_ATOMIC_FLAG_INIT; | ||
1045 | static gate_thread_storage_t gate_thco_context_link = GATE_INIT_EMPTY; | ||
1046 | |||
1047 | static gate_result_t gate_thco_init() | ||
1048 | { | ||
1049 | gate_result_t ret = GATE_RESULT_OK; | ||
1050 | gate_bool_t was_initialized = gate_atomic_flag_set(&gate_thco_initialized); | ||
1051 | if (!was_initialized) | ||
1052 | { | ||
1053 | ret = gate_thread_storage_alloc(&gate_thco_context_link); | ||
1054 | if (GATE_FAILED(ret)) | ||
1055 | { | ||
1056 | gate_atomic_flag_clear(&gate_thco_initialized); | ||
1057 | return ret; | ||
1058 | } | ||
1059 | } | ||
1060 | return ret; | ||
1061 | } | ||
1062 | |||
1063 | typedef struct gate_thco_context_class | ||
1064 | { | ||
1065 | gate_thco_session_t volatile* session; | ||
1066 | gate_thread_t thread_handle; | ||
1067 | gate_syncevent_t run_event; | ||
1068 | gate_uint32_t state; | ||
1069 | gate_coroutine_function_t func; | ||
1070 | void* param; | ||
1071 | gate_result_t result; | ||
1072 | |||
1073 | } gate_thco_context_t; | ||
1074 | |||
1075 | struct gate_thco_session_class | ||
1076 | { | ||
1077 | gate_syncevent_t sched_event; | ||
1078 | gate_thco_context_t contexts[128]; | ||
1079 | gate_size_t contexts_used; | ||
1080 | gate_index_t active_context; | ||
1081 | }; | ||
1082 | |||
1083 | static gate_index_t gate_thco_get_index(gate_thco_context_t volatile const* ctx) | ||
1084 | { | ||
1085 | gate_size_t ndx; | ||
1086 | gate_thco_session_t volatile* session = ctx->session; | ||
1087 | for (ndx = 0; ndx != session->contexts_used; ++ndx) | ||
1088 | { | ||
1089 | if (ctx == &session->contexts[ndx]) | ||
1090 | { | ||
1091 | return (gate_index_t)ndx; | ||
1092 | } | ||
1093 | } | ||
1094 | return -1; | ||
1095 | } | ||
1096 | |||
1097 | static gate_result_t gate_thco_context_dispatcher(void* param) | ||
1098 | { | ||
1099 | gate_thco_context_t volatile* ctx = (gate_thco_context_t volatile*)param; | ||
1100 | gate_result_t result = GATE_RESULT_FAILED; | ||
1101 | do | ||
1102 | { | ||
1103 | if (!ctx) | ||
1104 | { | ||
1105 | result = GATE_RESULT_INVALIDARG; | ||
1106 | break; | ||
1107 | } | ||
1108 | |||
1109 | result = gate_thread_storage_set(&gate_thco_context_link, param); | ||
1110 | if (GATE_SUCCEEDED(result)) | ||
1111 | { | ||
1112 | result = gate_syncevent_wait((gate_syncevent_t*)&ctx->run_event); | ||
1113 | if (GATE_SUCCEEDED(result)) | ||
1114 | { | ||
1115 | if (!ctx->func) | ||
1116 | { | ||
1117 | ctx->result = GATE_RESULT_INVALIDARG; | ||
1118 | } | ||
1119 | else | ||
1120 | { | ||
1121 | ctx->result = ctx->func(ctx->param); | ||
1122 | } | ||
1123 | ctx->state = GATE_THCO_STATE_COMPLETED; | ||
1124 | } | ||
1125 | gate_thread_storage_set(&gate_thco_context_link, NULL); | ||
1126 | } | ||
1127 | gate_syncevent_set((gate_syncevent_t*)&ctx->session->sched_event); | ||
1128 | } while (0); | ||
1129 | return result; | ||
1130 | } | ||
1131 | |||
1132 | static gate_result_t gate_thco_context_destroy(gate_thco_context_t volatile* ctx) | ||
1133 | { | ||
1134 | gate_result_t ret = GATE_RESULT_OK; | ||
1135 | gate_thread_join((gate_thread_t*)&ctx->thread_handle, NULL); | ||
1136 | gate_syncevent_destroy((gate_syncevent_t*)&ctx->run_event); | ||
1137 | gate_mem_clear((void*)ctx, sizeof(gate_thco_context_t)); | ||
1138 | return ret; | ||
1139 | } | ||
1140 | |||
1141 | static gate_result_t gate_thco_context_init(gate_thco_session_t volatile* session, | ||
1142 | gate_thco_context_t volatile* ctx, | ||
1143 | gate_coroutine_function_t func, void* param) | ||
1144 | { | ||
1145 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1146 | |||
1147 | do | ||
1148 | { | ||
1149 | gate_mem_clear((void*)ctx, sizeof(gate_thco_context_t)); | ||
1150 | ctx->session = session; | ||
1151 | ctx->func = func; | ||
1152 | ctx->param = param; | ||
1153 | ctx->result = GATE_RESULT_FAILED; | ||
1154 | |||
1155 | ret = gate_syncevent_create((gate_syncevent_t*)&ctx->run_event, false); | ||
1156 | GATE_BREAK_IF_FAILED(ret); | ||
1157 | |||
1158 | gate_syncevent_reset((gate_syncevent_t*)&ctx->run_event); | ||
1159 | |||
1160 | ctx->state = GATE_THCO_STATE_RUNNING; | ||
1161 | ret = gate_thread_start_code(&gate_thco_context_dispatcher, (void*)ctx, (gate_thread_t*)&ctx->thread_handle, NULL); | ||
1162 | if (GATE_FAILED(ret)) | ||
1163 | { | ||
1164 | gate_syncevent_destroy((gate_syncevent_t*)&ctx->run_event); | ||
1165 | break; | ||
1166 | } | ||
1167 | |||
1168 | ret = GATE_RESULT_OK; | ||
1169 | } while (0); | ||
1170 | |||
1171 | if (GATE_FAILED(ret)) | ||
1172 | { | ||
1173 | gate_mem_clear((void*)ctx, sizeof(gate_thco_context_t)); | ||
1174 | } | ||
1175 | return ret; | ||
1176 | } | ||
1177 | |||
1178 | |||
1179 | |||
1180 | static gate_thco_context_t volatile* gate_thco_context_new(gate_thco_session_t volatile* session, | ||
1181 | gate_coroutine_function_t func, void* param) | ||
1182 | { | ||
1183 | gate_thco_context_t volatile* ret = NULL; | ||
1184 | gate_size_t index; | ||
1185 | gate_thco_context_t volatile* ctx = NULL; | ||
1186 | gate_result_t result; | ||
1187 | |||
1188 | do | ||
1189 | { | ||
1190 | for (index = 0; index != session->contexts_used; ++index) | ||
1191 | { | ||
1192 | ctx = &session->contexts[index]; | ||
1193 | if (ctx->state == GATE_THCO_STATE_NONE) | ||
1194 | { | ||
1195 | ret = ctx; | ||
1196 | break; | ||
1197 | } | ||
1198 | } | ||
1199 | if (ret == NULL) | ||
1200 | { | ||
1201 | if (session->contexts_used >= sizeof(session->contexts) / sizeof(session->contexts[0])) | ||
1202 | { | ||
1203 | break; | ||
1204 | } | ||
1205 | ret = &session->contexts[session->contexts_used]; | ||
1206 | ++session->contexts_used; | ||
1207 | } | ||
1208 | |||
1209 | result = gate_thco_context_init(session, ret, func, param); | ||
1210 | if (GATE_FAILED(result)) | ||
1211 | { | ||
1212 | ret = NULL; | ||
1213 | } | ||
1214 | } while (0); | ||
1215 | |||
1216 | return ret; | ||
1217 | } | ||
1218 | |||
1219 | |||
1220 | gate_bool_t gate_coroutine_supported() | ||
1221 | { | ||
1222 | return true; | ||
1223 | } | ||
1224 | gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param) | ||
1225 | { | ||
1226 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1227 | gate_thco_session_t volatile session; | ||
1228 | gate_thco_context_t volatile* ptr_ctx; | ||
1229 | gate_bool_t running = true; | ||
1230 | gate_size_t index; | ||
1231 | gate_size_t cnt; | ||
1232 | |||
1233 | do | ||
1234 | { | ||
1235 | gate_mem_clear((void*)&session, sizeof(session)); | ||
1236 | |||
1237 | ret = gate_thco_init(); | ||
1238 | GATE_BREAK_IF_FAILED(ret); | ||
1239 | |||
1240 | ret = gate_syncevent_create((gate_syncevent_t*)&session.sched_event, false); | ||
1241 | GATE_BREAK_IF_FAILED(ret); | ||
1242 | |||
1243 | ptr_ctx = gate_thco_context_new(&session, func, param); | ||
1244 | if (NULL == ptr_ctx) | ||
1245 | { | ||
1246 | ret = GATE_RESULT_OUTOFRESOURCES; | ||
1247 | break; | ||
1248 | } | ||
1249 | |||
1250 | session.active_context = gate_thco_get_index(ptr_ctx); | ||
1251 | if (session.active_context < 0) | ||
1252 | { | ||
1253 | ret = GATE_RESULT_FAILED; | ||
1254 | } | ||
1255 | else | ||
1256 | { | ||
1257 | do | ||
1258 | { | ||
1259 | ptr_ctx = &session.contexts[session.active_context]; | ||
1260 | |||
1261 | ret = gate_syncevent_reset((gate_syncevent_t*)&session.sched_event); | ||
1262 | GATE_BREAK_IF_FAILED(ret); | ||
1263 | ret = gate_syncevent_set((gate_syncevent_t*)&ptr_ctx->run_event); | ||
1264 | GATE_BREAK_IF_FAILED(ret); | ||
1265 | ret = gate_syncevent_wait((gate_syncevent_t*)&session.sched_event); | ||
1266 | GATE_BREAK_IF_FAILED(ret); | ||
1267 | |||
1268 | running = false; | ||
1269 | for (cnt = 0; cnt != session.contexts_used; ++cnt) | ||
1270 | { | ||
1271 | index = ((gate_size_t)(session.active_context + 1 + (gate_index_t)cnt)) % session.contexts_used; | ||
1272 | |||
1273 | if (session.contexts[index].state == GATE_THCO_STATE_RUNNING) | ||
1274 | { | ||
1275 | running = true; | ||
1276 | session.active_context = (gate_index_t)index; | ||
1277 | break; | ||
1278 | } | ||
1279 | } | ||
1280 | } while (running && (session.contexts_used > 0)); | ||
1281 | } | ||
1282 | |||
1283 | for (cnt = 0; cnt != session.contexts_used; ++cnt) | ||
1284 | { | ||
1285 | ptr_ctx = &session.contexts[cnt]; | ||
1286 | if (ptr_ctx->state == GATE_THCO_STATE_COMPLETED) | ||
1287 | { | ||
1288 | gate_thco_context_destroy(ptr_ctx); | ||
1289 | } | ||
1290 | } | ||
1291 | |||
1292 | } while (0); | ||
1293 | |||
1294 | gate_syncevent_destroy((gate_syncevent_t*)&session.sched_event); | ||
1295 | |||
1296 | return ret; | ||
1297 | } | ||
1298 | gate_bool_t gate_coroutine_enabled() | ||
1299 | { | ||
1300 | void* data = NULL; | ||
1301 | gate_result_t result = gate_thread_storage_get(&gate_thco_context_link, &data); | ||
1302 | if (GATE_SUCCEEDED(result)) | ||
1303 | { | ||
1304 | return (data != NULL); | ||
1305 | } | ||
1306 | else | ||
1307 | { | ||
1308 | return false; | ||
1309 | } | ||
1310 | } | ||
1311 | gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id) | ||
1312 | { | ||
1313 | void* data = NULL; | ||
1314 | gate_result_t result = gate_thread_storage_get(&gate_thco_context_link, &data); | ||
1315 | if (GATE_SUCCEEDED(result)) | ||
1316 | { | ||
1317 | if (data == NULL) | ||
1318 | { | ||
1319 | result = GATE_RESULT_INVALIDSTATE; | ||
1320 | } | ||
1321 | else | ||
1322 | { | ||
1323 | if (ptr_id) | ||
1324 | { | ||
1325 | *ptr_id = (gate_coroutine_id_t)data; | ||
1326 | } | ||
1327 | } | ||
1328 | } | ||
1329 | return result; | ||
1330 | } | ||
1331 | gate_result_t gate_coroutine_yield() | ||
1332 | { | ||
1333 | gate_result_t result; | ||
1334 | gate_coroutine_id_t current_id; | ||
1335 | gate_thco_context_t volatile* ptr_current_ctx; | ||
1336 | do | ||
1337 | { | ||
1338 | result = gate_coroutine_get_current(¤t_id); | ||
1339 | GATE_BREAK_IF_FAILED(result); | ||
1340 | ptr_current_ctx = (gate_thco_context_t volatile*)current_id; | ||
1341 | |||
1342 | result = gate_syncevent_reset((gate_syncevent_t*)&ptr_current_ctx->run_event); | ||
1343 | GATE_BREAK_IF_FAILED(result); | ||
1344 | result = gate_syncevent_set((gate_syncevent_t*)&ptr_current_ctx->session->sched_event); | ||
1345 | GATE_BREAK_IF_FAILED(result); | ||
1346 | gate_syncevent_wait((gate_syncevent_t*)&ptr_current_ctx->run_event); | ||
1347 | } while (0); | ||
1348 | |||
1349 | return result; | ||
1350 | } | ||
1351 | gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id) | ||
1352 | { | ||
1353 | gate_result_t ret; | ||
1354 | gate_thco_context_t volatile* current_ctx; | ||
1355 | gate_coroutine_id_t id; | ||
1356 | gate_thco_context_t volatile* new_ctx; | ||
1357 | |||
1358 | do | ||
1359 | { | ||
1360 | ret = gate_coroutine_get_current(&id); | ||
1361 | GATE_BREAK_IF_FAILED(ret); | ||
1362 | |||
1363 | current_ctx = (gate_thco_context_t*)id; | ||
1364 | new_ctx = gate_thco_context_new(current_ctx->session, func, param); | ||
1365 | if (NULL == new_ctx) | ||
1366 | { | ||
1367 | ret = GATE_RESULT_OUTOFRESOURCES; | ||
1368 | break; | ||
1369 | } | ||
1370 | |||
1371 | if (ptr_id != NULL) | ||
1372 | { | ||
1373 | *ptr_id = (gate_coroutine_id_t)new_ctx; | ||
1374 | } | ||
1375 | } while (0); | ||
1376 | |||
1377 | return ret; | ||
1378 | } | ||
1379 | gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id) | ||
1380 | { | ||
1381 | gate_result_t result; | ||
1382 | gate_coroutine_id_t current_id; | ||
1383 | gate_thco_context_t volatile* ptr_current_ctx; | ||
1384 | gate_thco_context_t volatile* ptr_next_ctx; | ||
1385 | gate_index_t next_index; | ||
1386 | |||
1387 | do | ||
1388 | { | ||
1389 | result = gate_coroutine_get_current(¤t_id); | ||
1390 | GATE_BREAK_IF_FAILED(result); | ||
1391 | ptr_current_ctx = (gate_thco_context_t volatile*)current_id; | ||
1392 | ptr_next_ctx = (gate_thco_context_t volatile*)id; | ||
1393 | |||
1394 | if (ptr_current_ctx->session != ptr_next_ctx->session) | ||
1395 | { | ||
1396 | result = GATE_RESULT_INVALIDARG; | ||
1397 | break; | ||
1398 | } | ||
1399 | |||
1400 | next_index = gate_thco_get_index(ptr_next_ctx); | ||
1401 | if (next_index < 0) | ||
1402 | { | ||
1403 | result = GATE_RESULT_INVALIDARG; | ||
1404 | break; | ||
1405 | } | ||
1406 | |||
1407 | if (ptr_next_ctx->state != GATE_THCO_STATE_RUNNING) | ||
1408 | { | ||
1409 | result = GATE_RESULT_INVALIDSTATE; | ||
1410 | break; | ||
1411 | } | ||
1412 | |||
1413 | result = gate_syncevent_reset((gate_syncevent_t*)&ptr_current_ctx->run_event); | ||
1414 | GATE_BREAK_IF_FAILED(result); | ||
1415 | ptr_next_ctx->session->active_context = next_index; | ||
1416 | result = gate_syncevent_set((gate_syncevent_t*)&ptr_next_ctx->run_event); | ||
1417 | GATE_BREAK_IF_FAILED(result); | ||
1418 | gate_syncevent_wait((gate_syncevent_t*)&ptr_current_ctx->run_event); | ||
1419 | } while (0); | ||
1420 | |||
1421 | return result; | ||
1422 | } | ||
1423 | gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result) | ||
1424 | { | ||
1425 | gate_result_t ret; | ||
1426 | gate_coroutine_id_t current_id; | ||
1427 | gate_thco_context_t volatile* ptr_current_ctx; | ||
1428 | gate_thco_context_t volatile* ptr_next_ctx; | ||
1429 | |||
1430 | do | ||
1431 | { | ||
1432 | ret = gate_coroutine_get_current(¤t_id); | ||
1433 | GATE_BREAK_IF_FAILED(ret); | ||
1434 | |||
1435 | ptr_current_ctx = (gate_thco_context_t volatile*)current_id; | ||
1436 | ptr_next_ctx = (gate_thco_context_t volatile*)id; | ||
1437 | if (ptr_current_ctx->session != ptr_next_ctx->session) | ||
1438 | { | ||
1439 | ret = GATE_RESULT_INVALIDARG; | ||
1440 | break; | ||
1441 | } | ||
1442 | |||
1443 | while (GATE_SUCCEEDED(ret) && (ptr_next_ctx->state == GATE_THCO_STATE_RUNNING)) | ||
1444 | { | ||
1445 | ret = gate_coroutine_switch_to(id); | ||
1446 | } | ||
1447 | GATE_BREAK_IF_FAILED(ret); | ||
1448 | |||
1449 | if (ptr_next_ctx->state == GATE_THCO_STATE_COMPLETED) | ||
1450 | { | ||
1451 | if (ptr_coroutine_result) | ||
1452 | { | ||
1453 | *ptr_coroutine_result = ptr_next_ctx->result; | ||
1454 | } | ||
1455 | gate_thco_context_destroy(ptr_next_ctx); | ||
1456 | ptr_next_ctx->state = GATE_THCO_STATE_NONE; | ||
1457 | ret = GATE_RESULT_OK; | ||
1458 | } | ||
1459 | else | ||
1460 | { | ||
1461 | ret = GATE_RESULT_INVALIDSTATE; | ||
1462 | } | ||
1463 | } while (0); | ||
1464 | |||
1465 | return ret; | ||
1466 | } | ||
1467 | |||
1468 | #endif /* GATE_COROUTINES_THREADS */ | ||
1469 | |||
1470 | |||
1471 | #if defined(GATE_COROUTINES_NATIVE_STACKSWITCH) | ||
1472 | |||
1473 | #include "gate/callstacks.h" | ||
1474 | |||
1475 | /* STAck based COroutines = STACO */ | ||
1476 | |||
1477 | #define GATE_STACO_DEFAULT_SIZE (1024 * 1024) | ||
1478 | |||
1479 | #define GATE_STACO_STATE_UNINIT 0 | ||
1480 | #define GATE_STACO_STATE_RUNNING 1 | ||
1481 | #define GATE_STACO_STATE_COMPLETED 2 | ||
1482 | |||
1483 | typedef struct gate_staco_session_class gate_staco_session_t; | ||
1484 | |||
1485 | typedef struct gate_staco_context_class | ||
1486 | { | ||
1487 | gate_callstack_context_t stack_ctx; | ||
1488 | void* stack_ptr; | ||
1489 | gate_size_t stack_size; | ||
1490 | gate_staco_session_t volatile* session; | ||
1491 | unsigned state; | ||
1492 | gate_coroutine_function_t func; | ||
1493 | void* param; | ||
1494 | gate_result_t result; | ||
1495 | } gate_staco_context_t; | ||
1496 | |||
1497 | struct gate_staco_session_class | ||
1498 | { | ||
1499 | gate_callstack_context_t volatile scheduler_stack_ctx; | ||
1500 | |||
1501 | gate_staco_context_t volatile contexts[32]; | ||
1502 | gate_index_t volatile current_context; | ||
1503 | gate_index_t volatile next_context; | ||
1504 | }; | ||
1505 | |||
1506 | static gate_staco_session_t* global_staco_session = NULL; | ||
1507 | |||
1508 | static gate_staco_session_t* gate_staco_session_get() | ||
1509 | { | ||
1510 | return global_staco_session; | ||
1511 | } | ||
1512 | |||
1513 | |||
1514 | gate_bool_t gate_coroutine_supported() | ||
1515 | { | ||
1516 | return true; | ||
1517 | } | ||
1518 | |||
1519 | struct gate_staco_entry_param | ||
1520 | { | ||
1521 | gate_staco_context_t volatile* target_context; | ||
1522 | gate_callstack_context_t volatile* creator_context; | ||
1523 | gate_callstack_context_t volatile* exit_context; | ||
1524 | }; | ||
1525 | |||
1526 | static gate_result_t gate_staco_switch_context(gate_callstack_context_t volatile* current_to_save, | ||
1527 | gate_callstack_context_t volatile* next_to_load) | ||
1528 | { | ||
1529 | return gate_callstack_switch((gate_callstack_context_t*)current_to_save, (gate_callstack_context_t*)next_to_load); | ||
1530 | } | ||
1531 | |||
1532 | static gate_result_t gate_staco_entry(void* param) | ||
1533 | { | ||
1534 | struct gate_staco_entry_param* entry_param = (struct gate_staco_entry_param*)param; | ||
1535 | |||
1536 | gate_staco_context_t volatile* local_context = entry_param->target_context; | ||
1537 | gate_callstack_context_t volatile* ptr_creator_context = entry_param->creator_context; | ||
1538 | gate_callstack_context_t volatile* ptr_exit_context = entry_param->exit_context; | ||
1539 | |||
1540 | local_context->state = GATE_STACO_STATE_RUNNING; | ||
1541 | |||
1542 | gate_staco_switch_context(&local_context->stack_ctx, ptr_creator_context); | ||
1543 | |||
1544 | local_context->result = local_context->func(local_context->param); | ||
1545 | local_context->state = GATE_STACO_STATE_COMPLETED; | ||
1546 | |||
1547 | gate_staco_switch_context(&local_context->stack_ctx, ptr_exit_context); | ||
1548 | return GATE_RESULT_OK; | ||
1549 | } | ||
1550 | |||
1551 | static gate_index_t gate_staco_get_free_context_index(gate_staco_session_t* session) | ||
1552 | { | ||
1553 | gate_size_t ndx; | ||
1554 | gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]); | ||
1555 | for (ndx = 0; ndx != count; ++ndx) | ||
1556 | { | ||
1557 | if (session->contexts[ndx].state == GATE_STACO_STATE_UNINIT) | ||
1558 | { | ||
1559 | return (gate_index_t)ndx; | ||
1560 | } | ||
1561 | } | ||
1562 | return -1; | ||
1563 | } | ||
1564 | |||
1565 | static gate_result_t gate_staco_create_context(gate_staco_session_t* session, gate_index_t index, gate_coroutine_function_t func, void* func_param) | ||
1566 | { | ||
1567 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1568 | struct gate_staco_entry_param entry_param; | ||
1569 | gate_callstack_context_t volatile creator_ctx = GATE_INIT_EMPTY; | ||
1570 | gate_staco_context_t volatile* ptr_ctx; | ||
1571 | gate_result_t retval_dummy = GATE_RESULT_FAILED; | ||
1572 | |||
1573 | do | ||
1574 | { | ||
1575 | ptr_ctx = &session->contexts[index]; | ||
1576 | ptr_ctx->func = func; | ||
1577 | ptr_ctx->param = func_param; | ||
1578 | ptr_ctx->result = GATE_RESULT_UNKNOWNERROR; | ||
1579 | ptr_ctx->session = session; | ||
1580 | ptr_ctx->stack_size = GATE_STACO_DEFAULT_SIZE; | ||
1581 | ret = gate_callstack_create((gate_size_t*)&ptr_ctx->stack_size, (void**)&ptr_ctx->stack_ptr); | ||
1582 | GATE_BREAK_IF_FAILED(ret); | ||
1583 | |||
1584 | entry_param.target_context = ptr_ctx; | ||
1585 | entry_param.creator_context = &creator_ctx; | ||
1586 | entry_param.exit_context = &session->scheduler_stack_ctx; | ||
1587 | ret = gate_callstack_run(ptr_ctx->stack_ptr, ptr_ctx->stack_size, | ||
1588 | (gate_callstack_context_t*)&creator_ctx, | ||
1589 | &gate_staco_entry, &entry_param, | ||
1590 | &retval_dummy); | ||
1591 | |||
1592 | if (GATE_FAILED(ret)) | ||
1593 | { | ||
1594 | gate_callstack_destroy(ptr_ctx->stack_ptr, ptr_ctx->stack_size); | ||
1595 | } | ||
1596 | } while (0); | ||
1597 | |||
1598 | return ret; | ||
1599 | } | ||
1600 | |||
1601 | static void gate_staco_destroy_context(gate_staco_context_t volatile* ctx) | ||
1602 | { | ||
1603 | if (ctx->stack_ptr) | ||
1604 | { | ||
1605 | gate_callstack_destroy(ctx->stack_ptr, ctx->stack_size); | ||
1606 | } | ||
1607 | gate_mem_clear((void*)ctx, sizeof(gate_staco_context_t)); | ||
1608 | } | ||
1609 | |||
1610 | static gate_result_t gate_staco_scheduler(gate_staco_session_t* session, gate_coroutine_function_t first_func, void* first_param) | ||
1611 | { | ||
1612 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1613 | gate_staco_context_t volatile* ptr_next_context; | ||
1614 | gate_size_t ndx; | ||
1615 | gate_size_t const count = sizeof(session->contexts) / sizeof(session->contexts[0]); | ||
1616 | gate_size_t check_index; | ||
1617 | gate_index_t next_context_index; | ||
1618 | |||
1619 | do | ||
1620 | { | ||
1621 | gate_mem_clear(session, sizeof(gate_staco_session_t)); | ||
1622 | |||
1623 | ret = gate_staco_create_context(session, 0, first_func, first_param); | ||
1624 | GATE_BREAK_IF_FAILED(ret); | ||
1625 | |||
1626 | session->current_context = 0; | ||
1627 | |||
1628 | for (;;) | ||
1629 | { | ||
1630 | next_context_index = -1; | ||
1631 | |||
1632 | if ((session->next_context >= 0) && (session->next_context < (gate_index_t)count)) | ||
1633 | { | ||
1634 | if (session->contexts[session->next_context].state == GATE_STACO_STATE_RUNNING) | ||
1635 | { | ||
1636 | next_context_index = session->next_context; | ||
1637 | } | ||
1638 | session->next_context = -1; | ||
1639 | } | ||
1640 | |||
1641 | if (next_context_index == -1) | ||
1642 | { | ||
1643 | for (ndx = 0; ndx != count; ++ndx) | ||
1644 | { | ||
1645 | check_index = (gate_size_t)(session->current_context + 1 + ndx) % count; | ||
1646 | if (session->contexts[check_index].state == GATE_STACO_STATE_RUNNING) | ||
1647 | { | ||
1648 | next_context_index = check_index; | ||
1649 | break; | ||
1650 | } | ||
1651 | } | ||
1652 | } | ||
1653 | |||
1654 | if (next_context_index < 0) | ||
1655 | { | ||
1656 | break; | ||
1657 | } | ||
1658 | |||
1659 | ptr_next_context = &session->contexts[next_context_index]; | ||
1660 | session->current_context = next_context_index; | ||
1661 | ret = gate_staco_switch_context(&session->scheduler_stack_ctx, &ptr_next_context->stack_ctx); | ||
1662 | GATE_BREAK_IF_FAILED(ret); | ||
1663 | |||
1664 | } | ||
1665 | |||
1666 | } while (0); | ||
1667 | return ret; | ||
1668 | } | ||
1669 | |||
1670 | static gate_result_t gate_staco_start_session(gate_coroutine_function_t func, void* param) | ||
1671 | { | ||
1672 | gate_result_t ret; | ||
1673 | gate_staco_session_t session; | ||
1674 | |||
1675 | gate_mem_clear(&session, sizeof(session)); | ||
1676 | global_staco_session = &session; | ||
1677 | ret = gate_staco_scheduler(&session, func, param); | ||
1678 | global_staco_session = NULL; | ||
1679 | return ret; | ||
1680 | } | ||
1681 | |||
1682 | static gate_result_t gate_staco_switch_to_scheduler() | ||
1683 | { | ||
1684 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1685 | gate_staco_session_t* session; | ||
1686 | gate_index_t current_context_index; | ||
1687 | do | ||
1688 | { | ||
1689 | session = gate_staco_session_get(); | ||
1690 | if (!session) | ||
1691 | { | ||
1692 | ret = GATE_RESULT_INVALIDSTATE; | ||
1693 | break; | ||
1694 | } | ||
1695 | current_context_index = session->current_context; | ||
1696 | if ((current_context_index < 0) || (current_context_index >= sizeof(session->contexts) / sizeof(session->contexts[0]))) | ||
1697 | { | ||
1698 | ret = GATE_RESULT_INVALIDSTATE; | ||
1699 | break; | ||
1700 | } | ||
1701 | ret = gate_staco_switch_context(&session->contexts[current_context_index].stack_ctx, &session->scheduler_stack_ctx); | ||
1702 | } while (0); | ||
1703 | |||
1704 | return ret; | ||
1705 | } | ||
1706 | |||
1707 | gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param) | ||
1708 | { | ||
1709 | gate_result_t ret; | ||
1710 | gate_coroutine_id_t id = 0; | ||
1711 | gate_staco_session_t* session = gate_staco_session_get(); | ||
1712 | |||
1713 | if (session == NULL) | ||
1714 | { | ||
1715 | /* no session registered, register new one */ | ||
1716 | ret = gate_staco_start_session(func, param); | ||
1717 | } | ||
1718 | else | ||
1719 | { | ||
1720 | /* session registered, just add a new coroutine and wait for it */ | ||
1721 | ret = gate_coroutine_create(func, param, &id); | ||
1722 | if (GATE_SUCCEEDED(ret)) | ||
1723 | { | ||
1724 | ret = gate_coroutine_await(id, NULL); | ||
1725 | } | ||
1726 | } | ||
1727 | return ret; | ||
1728 | } | ||
1729 | |||
1730 | gate_bool_t gate_coroutine_enabled() | ||
1731 | { | ||
1732 | return gate_staco_session_get() != NULL; | ||
1733 | } | ||
1734 | gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id) | ||
1735 | { | ||
1736 | gate_result_t ret = GATE_RESULT_INVALIDSTATE; | ||
1737 | gate_staco_session_t* session = gate_staco_session_get(); | ||
1738 | do | ||
1739 | { | ||
1740 | if (!session) | ||
1741 | { | ||
1742 | break; | ||
1743 | } | ||
1744 | |||
1745 | if (ptr_id) | ||
1746 | { | ||
1747 | *ptr_id = session->current_context; | ||
1748 | } | ||
1749 | ret = GATE_RESULT_OK; | ||
1750 | } while (0); | ||
1751 | return ret; | ||
1752 | } | ||
1753 | |||
1754 | gate_result_t gate_coroutine_yield() | ||
1755 | { | ||
1756 | gate_result_t ret = gate_staco_switch_to_scheduler(); | ||
1757 | return ret; | ||
1758 | } | ||
1759 | |||
1760 | gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id) | ||
1761 | { | ||
1762 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1763 | gate_staco_session_t* session = gate_staco_session_get(); | ||
1764 | gate_index_t free_id; | ||
1765 | |||
1766 | do | ||
1767 | { | ||
1768 | if (!session) | ||
1769 | { | ||
1770 | ret = GATE_RESULT_INVALIDSTATE; | ||
1771 | break; | ||
1772 | } | ||
1773 | free_id = gate_staco_get_free_context_index(session); | ||
1774 | if (free_id < 0) | ||
1775 | { | ||
1776 | ret = GATE_RESULT_OUTOFRESOURCES; | ||
1777 | break; | ||
1778 | } | ||
1779 | ret = gate_staco_create_context(session, free_id, func, param); | ||
1780 | GATE_BREAK_IF_FAILED(ret); | ||
1781 | if (ptr_id) | ||
1782 | { | ||
1783 | *ptr_id = free_id; | ||
1784 | } | ||
1785 | } while (0); | ||
1786 | return ret; | ||
1787 | } | ||
1788 | gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id) | ||
1789 | { | ||
1790 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1791 | gate_staco_session_t* session = gate_staco_session_get(); | ||
1792 | |||
1793 | do | ||
1794 | { | ||
1795 | if (!session) | ||
1796 | { | ||
1797 | ret = GATE_RESULT_INVALIDSTATE; | ||
1798 | break; | ||
1799 | } | ||
1800 | |||
1801 | if ((id < 0) && (id >= sizeof(session->contexts) / sizeof(session->contexts[0]))) | ||
1802 | { | ||
1803 | ret = GATE_RESULT_INVALIDARG; | ||
1804 | break; | ||
1805 | } | ||
1806 | |||
1807 | if (session->contexts[id].state != GATE_STACO_STATE_RUNNING) | ||
1808 | { | ||
1809 | ret = GATE_RESULT_INVALIDSTATE; | ||
1810 | break; | ||
1811 | } | ||
1812 | |||
1813 | session->next_context = id; | ||
1814 | ret = gate_staco_switch_to_scheduler(); | ||
1815 | |||
1816 | } while (0); | ||
1817 | return ret; | ||
1818 | |||
1819 | } | ||
1820 | |||
1821 | gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result) | ||
1822 | { | ||
1823 | gate_result_t ret = GATE_RESULT_FAILED; | ||
1824 | gate_staco_session_t* session = gate_staco_session_get(); | ||
1825 | gate_staco_context_t volatile* ptr_target_ctx; | ||
1826 | |||
1827 | do | ||
1828 | { | ||
1829 | if (!session) | ||
1830 | { | ||
1831 | ret = GATE_RESULT_INVALIDSTATE; | ||
1832 | break; | ||
1833 | } | ||
1834 | |||
1835 | if ((id < 0) && (id >= sizeof(session->contexts) / sizeof(session->contexts[0]))) | ||
1836 | { | ||
1837 | ret = GATE_RESULT_INVALIDARG; | ||
1838 | break; | ||
1839 | } | ||
1840 | |||
1841 | ptr_target_ctx = &session->contexts[id]; | ||
1842 | |||
1843 | ret = GATE_RESULT_OK; | ||
1844 | while (ptr_target_ctx->state == GATE_STACO_STATE_RUNNING) | ||
1845 | { | ||
1846 | ret = gate_staco_switch_to_scheduler(); | ||
1847 | GATE_BREAK_IF_FAILED(ret); | ||
1848 | } | ||
1849 | if (ptr_target_ctx->state != GATE_STACO_STATE_COMPLETED) | ||
1850 | { | ||
1851 | ret = GATE_RESULT_INVALIDSTATE; | ||
1852 | break; | ||
1853 | } | ||
1854 | |||
1855 | ret = GATE_RESULT_OK; | ||
1856 | if (ptr_coroutine_result) | ||
1857 | { | ||
1858 | *ptr_coroutine_result = ptr_target_ctx->result; | ||
1859 | } | ||
1860 | |||
1861 | gate_staco_destroy_context(ptr_target_ctx); | ||
1862 | gate_staco_destroy_context(ptr_target_ctx); | ||
1863 | |||
1864 | } while (0); | ||
1865 | return ret; | ||
1866 | } | ||
1867 | |||
1868 | #endif /* GATE_COROUTINES_NATIVE_STACKSWITCH */ | ||
1869 | |||
1870 | |||
1871 | #if defined(GATE_COROUTINES_NOIMPL) | ||
1872 | |||
1873 | gate_bool_t gate_coroutine_supported() | ||
1874 | { | ||
1875 | return false; | ||
1876 | } | ||
1877 | |||
1878 | |||
1879 | gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param) | ||
1880 | { | ||
1881 | GATE_UNUSED_ARG(func); | ||
1882 | GATE_UNUSED_ARG(param); | ||
1883 | return GATE_RESULT_NOTIMPLEMENTED; | ||
1884 | } | ||
1885 | gate_bool_t gate_coroutine_enabled() | ||
1886 | { | ||
1887 | return false; | ||
1888 | } | ||
1889 | gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id) | ||
1890 | { | ||
1891 | GATE_UNUSED_ARG(ptr_id); | ||
1892 | return GATE_RESULT_NOTIMPLEMENTED; | ||
1893 | } | ||
1894 | gate_result_t gate_coroutine_yield() | ||
1895 | { | ||
1896 | return GATE_RESULT_NOTIMPLEMENTED; | ||
1897 | } | ||
1898 | gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id) | ||
1899 | { | ||
1900 | GATE_UNUSED_ARG(func); | ||
1901 | GATE_UNUSED_ARG(param); | ||
1902 | GATE_UNUSED_ARG(ptr_id); | ||
1903 | return GATE_RESULT_NOTIMPLEMENTED; | ||
1904 | } | ||
1905 | gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id) | ||
1906 | { | ||
1907 | GATE_UNUSED_ARG(id); | ||
1908 | return GATE_RESULT_NOTIMPLEMENTED; | ||
1909 | } | ||
1910 | gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result) | ||
1911 | { | ||
1912 | GATE_UNUSED_ARG(id); | ||
1913 | GATE_UNUSED_ARG(ptr_coroutine_result); | ||
1914 | return GATE_RESULT_NOTIMPLEMENTED; | ||
1915 | } | ||
1916 | |||
1917 | #endif | ||
1918 |