GCC Code Coverage Report


Directory: src/gate/
File: src/gate/coroutines.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 172 218 78.9%
Functions: 17 18 94.4%
Branches: 55 94 58.5%

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(&current_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(&current_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(&current_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