GCC Code Coverage Report


Directory: src/gate/
File: src/gate/coroutines.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 180 221 81.4%
Functions: 18 18 100.0%
Branches: 62 102 60.8%

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 1 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 1 gate_int64_t max_diff_micro = (gate_int64_t)milliseconds * (gate_int64_t)1000;
68
69 do
70 {
71 1 ret = gate_timecounter_now(&tc_start);
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
73
74 do
75 {
76 418629 ret = gate_coroutine_yield();
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418629 times.
418629 GATE_BREAK_IF_FAILED(ret);
78 418629 ret = gate_timecounter_now(&tc_now);
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 418629 times.
418629 GATE_BREAK_IF_FAILED(ret);
80
2/2
✓ Branch 1 taken 418628 times.
✓ Branch 2 taken 1 times.
418629 } while (gate_timecounter_diff(tc_now, tc_start) < max_diff_micro);
81
82 } while (0);
83
84 1 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 837322 static gate_result_t gate_posix_co_init()
619 {
620 837322 gate_result_t ret = GATE_RESULT_FAILED;
621
622 837322 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 837320 times.
837322 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 837320 ret = GATE_RESULT_OK;
644 }
645 837322 return ret;
646 }
647
648 3 gate_bool_t gate_coroutine_supported()
649 {
650 3 gate_result_t result = gate_posix_co_init();
651 3 return GATE_SUCCEEDED(result);
652 }
653
654 19 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 19 void* previous_value = NULL;
658 19 result = gate_thread_storage_get(&global_posix_co_threadstore_key, &previous_value);
659
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if(GATE_FAILED(result))
660 {
661 GATE_DEBUG_TRACE_FAILED(result, "gate_thread_storage_get() failed");
662 return NULL;
663 }
664 19 result = gate_thread_storage_set(&global_posix_co_threadstore_key, (void*)session);
665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if(GATE_FAILED(result))
666 {
667 GATE_DEBUG_TRACE_FAILED(result, "gate_thread_storage_set() failed");
668 return NULL;
669 }
670 19 return (gate_posix_co_session_t volatile*)previous_value;
671 }
672
673 11 static void gate_posix_co_dispatcher(int a0, int a1, int a2, int a3)
674 {
675 gate_posix_co_entry_args_t args;
676 gate_posix_co_context_t volatile* ptr_ctx;
677
678 11 args.items[0] = a0;
679 11 args.items[1] = a1;
680 11 args.items[2] = a2;
681 11 args.items[3] = a3;
682
683 11 ptr_ctx = args.ptr_ctx;
684
685 11 gate_posix_co_session_register(ptr_ctx->session);
686
687 11 ptr_ctx->result = ptr_ctx->func(ptr_ctx->param);
688 11 ptr_ctx->state = GATE_POSIX_CO_STATE_COMPLETED;
689
690 11 gate_posix_uctx_swap((void*)&ptr_ctx->uctx, (void*)&ptr_ctx->session->master_ctx);
691 }
692
693 837308 static gate_posix_co_context_t volatile* gate_posix_co_get_context()
694 {
695 gate_result_t result;
696 837308 void* data = NULL;
697 gate_posix_co_session_t volatile* volatile ptr_session;
698 gate_uintptr_t local_address;
699 gate_size_t ctx_count;
700 gate_size_t ndx;
701
702 837308 result = gate_posix_co_init();
703
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 837308 times.
837308 if (GATE_FAILED(result))
704 {
705 return NULL;
706 }
707
708 837308 result = gate_thread_storage_get(&global_posix_co_threadstore_key, &data);
709
3/4
✓ Branch 0 taken 837308 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 837302 times.
837308 if (GATE_FAILED(result) || (data == NULL))
710 {
711 6 return NULL;
712 }
713 837302 ptr_session = (gate_posix_co_session_t volatile*)data;
714 837302 local_address = (gate_uintptr_t)&ptr_session;
715 837302 ctx_count = sizeof(ptr_session->contexts) / sizeof(ptr_session->contexts[0]);
716
1/2
✓ Branch 0 taken 1255945 times.
✗ Branch 1 not taken.
1255945 for (ndx = 0; ndx != ctx_count; ++ndx)
717 {
718 1255945 gate_posix_co_context_t volatile* ptr_ctx = &ptr_session->contexts[ndx];
719 1255945 gate_uintptr_t stack_address_begin = (gate_uintptr_t)ptr_ctx->stack;
720 1255945 gate_uintptr_t stack_address_end = stack_address_begin + GATE_POSIX_CO_DEFAULT_STACK_SIZE;
721
4/4
✓ Branch 0 taken 1255941 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 837302 times.
✓ Branch 3 taken 418639 times.
1255945 if ((local_address >= stack_address_begin) && (local_address <= stack_address_end))
722 {
723 837302 return ptr_ctx;
724 }
725 }
726 return NULL;
727 }
728
729 11 static gate_result_t gate_posix_co_context_create(gate_posix_co_context_t volatile* ctx,
730 gate_posix_co_session_t volatile* session,
731 gate_coroutine_function_t func, void* param)
732 {
733 11 void* stack_space = gate_mem_alloc(GATE_POSIX_CO_DEFAULT_STACK_SIZE);
734 gate_posix_co_entry_args_t entry_args;
735 int* p;
736
737
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stack_space == NULL)
738 {
739 return GATE_RESULT_OUTOFMEMORY;
740 }
741 11 gate_mem_clear((void*)ctx, sizeof(gate_posix_co_context_t));
742
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 if (0 != gate_posix_uctx_get((void*)&ctx->uctx))
743 {
744 gate_mem_dealloc(stack_space);
745 return GATE_RESULT_OUTOFRESOURCES;
746 }
747 11 gate_posix_uctx_setup((void*)&ctx->uctx, stack_space, GATE_POSIX_CO_DEFAULT_STACK_SIZE);
748 11 ctx->stack = stack_space;
749 11 ctx->session = session;
750 11 ctx->state = GATE_POSIX_CO_STATE_RUNNING;
751 11 ctx->func = func;
752 11 ctx->param = param;
753 11 ctx->result = GATE_RESULT_UNKNOWNERROR;
754
755 11 entry_args.ptr_ctx = ctx;
756 11 p = &entry_args.items[0];
757 11 gate_posix_uctx_make((void*)&ctx->uctx, &gate_posix_co_dispatcher, p[0], p[1], p[2], p[3]);
758 11 return GATE_RESULT_OK;
759 }
760
761 837299 static void gate_posix_co_swap_context(gate_posix_co_session_t volatile* session,
762 gate_index_t new_context_index,
763 void volatile* old_context)
764 {
765 837299 session->current_context = new_context_index;
766 837299 gate_posix_uctx_swap((void*)old_context, (void*)&session->contexts[new_context_index].uctx);
767 837299 }
768
769 261 static gate_result_t gate_posix_co_context_destroy(gate_posix_co_context_t volatile* ctx)
770 {
771
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 250 times.
261 if (ctx->stack != NULL)
772 {
773 11 gate_mem_dealloc(ctx->stack);
774 }
775 261 gate_mem_clear((void*)ctx, sizeof(gate_posix_co_context_t));
776 261 return GATE_RESULT_OK;
777 }
778
779 837289 static gate_index_t gate_posix_co_get_context_index(gate_posix_co_context_t volatile* ptr_ctx)
780 {
781 gate_size_t ndx;
782 837289 gate_posix_co_session_t volatile* session = ptr_ctx->session;
783 837289 gate_size_t cnt = sizeof(session->contexts) / sizeof(session->contexts[0]);
784
785
1/2
✓ Branch 0 taken 1255928 times.
✗ Branch 1 not taken.
1255928 for (ndx = 0; ndx != cnt; ++ndx)
786 {
787
2/2
✓ Branch 0 taken 837289 times.
✓ Branch 1 taken 418639 times.
1255928 if (&session->contexts[ndx] == ptr_ctx)
788 {
789 837289 return (gate_index_t)ndx;
790 }
791 }
792 return -1;
793 }
794
795 7 static gate_index_t gate_posix_co_get_free_context(gate_posix_co_session_t volatile* session)
796 {
797 gate_size_t ndx;
798 7 gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]);
799
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 for (ndx = 0; ndx != count; ++ndx)
800 {
801
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 8 times.
15 if (session->contexts[ndx].state == GATE_POSIX_CO_STATE_UNINIT)
802 {
803 7 return ndx;
804 }
805 }
806 return -1;
807 }
808
809
810 4 gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param)
811 {
812 4 gate_result_t ret = GATE_RESULT_FAILED;
813 4 gate_posix_co_session_t volatile session = GATE_INIT_EMPTY;
814 4 gate_posix_co_session_t volatile* ptr_old_session = NULL;
815 gate_index_t ndx;
816 gate_index_t cnt;
817 gate_index_t new_index;
818 gate_index_t next_index;
819
820 4 ret = gate_posix_co_init();
821
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (GATE_FAILED(ret))
822 {
823 return ret;
824 }
825
826 do
827 {
828 4 gate_mem_clear((void*)&session, sizeof(session));
829 4 ptr_old_session = gate_posix_co_session_register(&session);
830
831 4 ret = gate_posix_co_context_create(&session.contexts[0], &session, func, param);
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
833
834 4 cnt = sizeof(session.contexts) / sizeof(session.contexts[0]);
835 4 session.current_context = -1;
836 for (;;)
837 {
838 837294 next_index = -1;
839
2/2
✓ Branch 0 taken 26793478 times.
✓ Branch 1 taken 4 times.
26793482 for (ndx = 0; ndx != cnt; ++ndx)
840 {
841 26793478 new_index = (gate_index_t)((gate_size_t)(ndx + session.current_context + 1) % (gate_size_t)cnt);
842
2/2
✓ Branch 0 taken 837294 times.
✓ Branch 1 taken 25956184 times.
26793478 if (session.contexts[new_index].state == GATE_POSIX_CO_STATE_RUNNING)
843 {
844 837294 next_index = new_index;
845 837294 break;
846 }
847 }
848
849
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 837294 times.
837298 if (next_index < 0)
850 {
851 4 break;
852 }
853 837294 gate_posix_co_swap_context(&session, next_index, &session.master_ctx);
854 }
855
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 4 times.
260 for (ndx = 0; ndx != cnt; ++ndx)
856 {
857 256 gate_posix_co_context_destroy(&session.contexts[ndx]);
858 }
859 } while (0);
860
861 4 gate_posix_co_session_register(ptr_old_session);
862
863 4 return ret;
864 }
865
866 7 gate_bool_t gate_coroutine_enabled()
867 {
868 7 gate_result_t result = gate_posix_co_init();
869
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (GATE_FAILED(result))
870 {
871 return false;
872 }
873 7 return gate_posix_co_get_context() != NULL;
874 }
875
876 1 gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id)
877 {
878 1 gate_posix_co_context_t volatile* ptr_ctx = gate_posix_co_get_context();
879 gate_index_t index;
880
881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_ctx)
882 {
883 return GATE_RESULT_INVALIDSTATE;
884 }
885 1 index = gate_posix_co_get_context_index(ptr_ctx);
886
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (index < 0)
887 {
888 return GATE_RESULT_INVALIDSTATE;
889 }
890
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_id)
891 {
892 1 *ptr_id = (gate_coroutine_id_t)index;
893 }
894 1 return GATE_RESULT_OK;
895 }
896
897 837283 gate_result_t gate_coroutine_yield()
898 {
899 837283 gate_result_t ret = GATE_RESULT_FAILED;
900 837283 gate_posix_co_context_t volatile* ptr_ctx = gate_posix_co_get_context();
901 gate_index_t current_index;
902 do
903 {
904
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 837283 times.
837283 if (ptr_ctx == NULL)
905 {
906 ret = GATE_RESULT_INVALIDSTATE;
907 break;
908 }
909 837283 current_index = gate_posix_co_get_context_index(ptr_ctx);
910
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 837283 times.
837283 if (current_index < 0)
911 {
912 ret = GATE_RESULT_INVALIDSTATE;
913 break;
914 }
915 837283 gate_posix_uctx_swap((void*)&ptr_ctx->session->contexts[current_index].uctx,
916 837283 (void*)&ptr_ctx->session->master_ctx);
917 837283 ret = GATE_RESULT_OK;
918 } while (0);
919 837283 return ret;
920 }
921
922 7 gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id)
923 {
924 7 gate_result_t ret = GATE_RESULT_FAILED;
925
926 do
927 {
928 7 gate_posix_co_context_t volatile* current_context = gate_posix_co_get_context();
929 gate_posix_co_session_t volatile* session;
930 gate_posix_co_context_t volatile* new_context;
931 gate_index_t index;
932
933
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (current_context == NULL)
934 {
935 ret = GATE_RESULT_INVALIDSTATE;
936 break;
937 }
938 7 session = current_context->session;
939
940 7 index = gate_posix_co_get_free_context(session);
941
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (index < 0)
942 {
943 ret = GATE_RESULT_OUTOFRESOURCES;
944 break;
945 }
946
947 7 new_context = &session->contexts[index];
948 7 gate_posix_co_context_create(new_context, session, func, param);
949
950
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (ptr_id)
951 {
952 7 *ptr_id = (gate_coroutine_id_t)index;
953 }
954 7 ret = GATE_RESULT_OK;
955 } while (0);
956
957 7 return ret;
958 }
959
960 5 gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id)
961 {
962 gate_result_t ret;
963 5 gate_index_t next_index = (gate_index_t)id;
964 5 gate_posix_co_context_t volatile* cur_context = gate_posix_co_get_context();
965
966 do
967 {
968 gate_posix_co_session_t volatile* session;
969
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (cur_context == NULL)
970 {
971 ret = GATE_RESULT_INVALIDSTATE;
972 break;
973 }
974 5 session = cur_context->session;
975
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])))
976 {
977 ret = GATE_RESULT_INVALIDSTATE;
978 break;
979 }
980
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (session->contexts[next_index].state != GATE_POSIX_CO_STATE_RUNNING)
981 {
982 ret = GATE_RESULT_INVALIDSTATE;
983 break;
984 }
985
986 5 gate_posix_co_swap_context(session, next_index, &cur_context->uctx);
987 5 ret = GATE_RESULT_OK;
988 } while (0);
989 5 return ret;
990 }
991
992 5 gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result)
993 {
994 5 gate_result_t ret = GATE_RESULT_FAILED;
995 5 gate_index_t index = (gate_index_t)id;
996 5 gate_posix_co_context_t volatile* cur_context = gate_posix_co_get_context();
997 gate_posix_co_session_t volatile* session;
998 gate_index_t cur_index;
999 do
1000 {
1001
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (cur_context == NULL)
1002 {
1003 ret = GATE_RESULT_INVALIDSTATE;
1004 break;
1005 }
1006 5 session = cur_context->session;
1007 5 cur_index = gate_posix_co_get_context_index(cur_context);
1008
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if ((index < 0) || (index >= sizeof(session->contexts) / sizeof(session->contexts[0])))
1009 {
1010 ret = GATE_RESULT_INVALIDSTATE;
1011 break;
1012 }
1013
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (index == cur_index)
1014 {
1015 ret = GATE_RESULT_INVALIDSTATE;
1016 break;
1017 }
1018
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 5 times.
20 while (session->contexts[index].state == GATE_POSIX_CO_STATE_RUNNING)
1019 {
1020 15 ret = gate_coroutine_yield();
1021
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 GATE_BREAK_IF_FAILED(ret);
1022 }
1023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (session->contexts[index].state != GATE_POSIX_CO_STATE_COMPLETED)
1024 {
1025 ret = GATE_RESULT_INVALIDSTATE;
1026 break;
1027 }
1028
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (ptr_coroutine_result)
1029 {
1030 5 *ptr_coroutine_result = session->contexts[index].result;
1031 }
1032 5 gate_posix_co_context_destroy(&session->contexts[index]);
1033 5 ret = GATE_RESULT_OK;
1034 } while (0);
1035 5 return ret;
1036 }
1037
1038 #endif /* GATE_COROUTINES_POSIX */
1039
1040
1041 #if defined(GATE_COROUTINES_THREADS)
1042
1043 #include "gate/synchronization.h"
1044
1045
1046 #define GATE_THCO_STATE_NONE 0
1047 #define GATE_THCO_STATE_RUNNING 1
1048 #define GATE_THCO_STATE_COMPLETED 2
1049
1050 typedef struct gate_thco_session_class gate_thco_session_t;
1051
1052 static gate_atomic_flag_t gate_thco_initialized = GATE_ATOMIC_FLAG_INIT;
1053 static gate_thread_storage_t gate_thco_context_link = GATE_INIT_EMPTY;
1054
1055 static gate_result_t gate_thco_init()
1056 {
1057 gate_result_t ret = GATE_RESULT_OK;
1058 gate_bool_t was_initialized = gate_atomic_flag_set(&gate_thco_initialized);
1059 if (!was_initialized)
1060 {
1061 ret = gate_thread_storage_alloc(&gate_thco_context_link);
1062 if (GATE_FAILED(ret))
1063 {
1064 gate_atomic_flag_clear(&gate_thco_initialized);
1065 return ret;
1066 }
1067 }
1068 return ret;
1069 }
1070
1071 typedef struct gate_thco_context_class
1072 {
1073 gate_thco_session_t volatile* session;
1074 gate_thread_t thread_handle;
1075 gate_syncevent_t run_event;
1076 gate_uint32_t state;
1077 gate_coroutine_function_t func;
1078 void* param;
1079 gate_result_t result;
1080
1081 } gate_thco_context_t;
1082
1083 struct gate_thco_session_class
1084 {
1085 gate_syncevent_t sched_event;
1086 gate_thco_context_t contexts[128];
1087 gate_size_t contexts_used;
1088 gate_index_t active_context;
1089 };
1090
1091 static gate_index_t gate_thco_get_index(gate_thco_context_t volatile const* ctx)
1092 {
1093 gate_size_t ndx;
1094 gate_thco_session_t volatile* session = ctx->session;
1095 for (ndx = 0; ndx != session->contexts_used; ++ndx)
1096 {
1097 if (ctx == &session->contexts[ndx])
1098 {
1099 return (gate_index_t)ndx;
1100 }
1101 }
1102 return -1;
1103 }
1104
1105 static gate_result_t gate_thco_context_dispatcher(void* param)
1106 {
1107 gate_thco_context_t volatile* ctx = (gate_thco_context_t volatile*)param;
1108 gate_result_t result = GATE_RESULT_FAILED;
1109 do
1110 {
1111 if (!ctx)
1112 {
1113 result = GATE_RESULT_INVALIDARG;
1114 break;
1115 }
1116
1117 result = gate_thread_storage_set(&gate_thco_context_link, param);
1118 if (GATE_SUCCEEDED(result))
1119 {
1120 result = gate_syncevent_wait((gate_syncevent_t*)&ctx->run_event);
1121 if (GATE_SUCCEEDED(result))
1122 {
1123 if (!ctx->func)
1124 {
1125 ctx->result = GATE_RESULT_INVALIDARG;
1126 }
1127 else
1128 {
1129 ctx->result = ctx->func(ctx->param);
1130 }
1131 ctx->state = GATE_THCO_STATE_COMPLETED;
1132 }
1133 gate_thread_storage_set(&gate_thco_context_link, NULL);
1134 }
1135 gate_syncevent_set((gate_syncevent_t*)&ctx->session->sched_event);
1136 } while (0);
1137 return result;
1138 }
1139
1140 static gate_result_t gate_thco_context_destroy(gate_thco_context_t volatile* ctx)
1141 {
1142 gate_result_t ret = GATE_RESULT_OK;
1143 gate_thread_join((gate_thread_t*)&ctx->thread_handle, NULL);
1144 gate_syncevent_destroy((gate_syncevent_t*)&ctx->run_event);
1145 gate_mem_clear((void*)ctx, sizeof(gate_thco_context_t));
1146 return ret;
1147 }
1148
1149 static gate_result_t gate_thco_context_init(gate_thco_session_t volatile* session,
1150 gate_thco_context_t volatile* ctx,
1151 gate_coroutine_function_t func, void* param)
1152 {
1153 gate_result_t ret = GATE_RESULT_FAILED;
1154
1155 do
1156 {
1157 gate_mem_clear((void*)ctx, sizeof(gate_thco_context_t));
1158 ctx->session = session;
1159 ctx->func = func;
1160 ctx->param = param;
1161 ctx->result = GATE_RESULT_FAILED;
1162
1163 ret = gate_syncevent_create((gate_syncevent_t*)&ctx->run_event, false);
1164 GATE_BREAK_IF_FAILED(ret);
1165
1166 gate_syncevent_reset((gate_syncevent_t*)&ctx->run_event);
1167
1168 ctx->state = GATE_THCO_STATE_RUNNING;
1169 ret = gate_thread_start_code(&gate_thco_context_dispatcher, (void*)ctx, (gate_thread_t*)&ctx->thread_handle, NULL);
1170 if (GATE_FAILED(ret))
1171 {
1172 gate_syncevent_destroy((gate_syncevent_t*)&ctx->run_event);
1173 break;
1174 }
1175
1176 ret = GATE_RESULT_OK;
1177 } while (0);
1178
1179 if (GATE_FAILED(ret))
1180 {
1181 gate_mem_clear((void*)ctx, sizeof(gate_thco_context_t));
1182 }
1183 return ret;
1184 }
1185
1186
1187
1188 static gate_thco_context_t volatile* gate_thco_context_new(gate_thco_session_t volatile* session,
1189 gate_coroutine_function_t func, void* param)
1190 {
1191 gate_thco_context_t volatile* ret = NULL;
1192 gate_size_t index;
1193 gate_thco_context_t volatile* ctx = NULL;
1194 gate_result_t result;
1195
1196 do
1197 {
1198 for (index = 0; index != session->contexts_used; ++index)
1199 {
1200 ctx = &session->contexts[index];
1201 if (ctx->state == GATE_THCO_STATE_NONE)
1202 {
1203 ret = ctx;
1204 break;
1205 }
1206 }
1207 if (ret == NULL)
1208 {
1209 if (session->contexts_used >= sizeof(session->contexts) / sizeof(session->contexts[0]))
1210 {
1211 break;
1212 }
1213 ret = &session->contexts[session->contexts_used];
1214 ++session->contexts_used;
1215 }
1216
1217 result = gate_thco_context_init(session, ret, func, param);
1218 if (GATE_FAILED(result))
1219 {
1220 ret = NULL;
1221 }
1222 } while (0);
1223
1224 return ret;
1225 }
1226
1227
1228 gate_bool_t gate_coroutine_supported()
1229 {
1230 return true;
1231 }
1232 gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param)
1233 {
1234 gate_result_t ret = GATE_RESULT_FAILED;
1235 gate_thco_session_t volatile session;
1236 gate_thco_context_t volatile* ptr_ctx;
1237 gate_bool_t running = true;
1238 gate_size_t index;
1239 gate_size_t cnt;
1240
1241 do
1242 {
1243 gate_mem_clear((void*)&session, sizeof(session));
1244
1245 ret = gate_thco_init();
1246 GATE_BREAK_IF_FAILED(ret);
1247
1248 ret = gate_syncevent_create((gate_syncevent_t*)&session.sched_event, false);
1249 GATE_BREAK_IF_FAILED(ret);
1250
1251 ptr_ctx = gate_thco_context_new(&session, func, param);
1252 if (NULL == ptr_ctx)
1253 {
1254 ret = GATE_RESULT_OUTOFRESOURCES;
1255 break;
1256 }
1257
1258 session.active_context = gate_thco_get_index(ptr_ctx);
1259 if (session.active_context < 0)
1260 {
1261 ret = GATE_RESULT_FAILED;
1262 }
1263 else
1264 {
1265 do
1266 {
1267 ptr_ctx = &session.contexts[session.active_context];
1268
1269 ret = gate_syncevent_reset((gate_syncevent_t*)&session.sched_event);
1270 GATE_BREAK_IF_FAILED(ret);
1271 ret = gate_syncevent_set((gate_syncevent_t*)&ptr_ctx->run_event);
1272 GATE_BREAK_IF_FAILED(ret);
1273 ret = gate_syncevent_wait((gate_syncevent_t*)&session.sched_event);
1274 GATE_BREAK_IF_FAILED(ret);
1275
1276 running = false;
1277 for (cnt = 0; cnt != session.contexts_used; ++cnt)
1278 {
1279 index = ((gate_size_t)(session.active_context + 1 + (gate_index_t)cnt)) % session.contexts_used;
1280
1281 if (session.contexts[index].state == GATE_THCO_STATE_RUNNING)
1282 {
1283 running = true;
1284 session.active_context = (gate_index_t)index;
1285 break;
1286 }
1287 }
1288 } while (running && (session.contexts_used > 0));
1289 }
1290
1291 for (cnt = 0; cnt != session.contexts_used; ++cnt)
1292 {
1293 ptr_ctx = &session.contexts[cnt];
1294 if (ptr_ctx->state == GATE_THCO_STATE_COMPLETED)
1295 {
1296 gate_thco_context_destroy(ptr_ctx);
1297 }
1298 }
1299
1300 } while (0);
1301
1302 gate_syncevent_destroy((gate_syncevent_t*)&session.sched_event);
1303
1304 return ret;
1305 }
1306 gate_bool_t gate_coroutine_enabled()
1307 {
1308 void* data = NULL;
1309 gate_result_t result = gate_thread_storage_get(&gate_thco_context_link, &data);
1310 if (GATE_SUCCEEDED(result))
1311 {
1312 return (data != NULL);
1313 }
1314 else
1315 {
1316 return false;
1317 }
1318 }
1319 gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id)
1320 {
1321 void* data = NULL;
1322 gate_result_t result = gate_thread_storage_get(&gate_thco_context_link, &data);
1323 if (GATE_SUCCEEDED(result))
1324 {
1325 if (data == NULL)
1326 {
1327 result = GATE_RESULT_INVALIDSTATE;
1328 }
1329 else
1330 {
1331 if (ptr_id)
1332 {
1333 *ptr_id = (gate_coroutine_id_t)data;
1334 }
1335 }
1336 }
1337 return result;
1338 }
1339 gate_result_t gate_coroutine_yield()
1340 {
1341 gate_result_t result;
1342 gate_coroutine_id_t current_id;
1343 gate_thco_context_t volatile* ptr_current_ctx;
1344 do
1345 {
1346 result = gate_coroutine_get_current(&current_id);
1347 GATE_BREAK_IF_FAILED(result);
1348 ptr_current_ctx = (gate_thco_context_t volatile*)current_id;
1349
1350 result = gate_syncevent_reset((gate_syncevent_t*)&ptr_current_ctx->run_event);
1351 GATE_BREAK_IF_FAILED(result);
1352 result = gate_syncevent_set((gate_syncevent_t*)&ptr_current_ctx->session->sched_event);
1353 GATE_BREAK_IF_FAILED(result);
1354 gate_syncevent_wait((gate_syncevent_t*)&ptr_current_ctx->run_event);
1355 } while (0);
1356
1357 return result;
1358 }
1359 gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id)
1360 {
1361 gate_result_t ret;
1362
1363 do
1364 {
1365 gate_coroutine_id_t id;
1366 gate_thco_context_t volatile* current_ctx;
1367 gate_thco_context_t volatile* new_ctx;
1368
1369 ret = gate_coroutine_get_current(&id);
1370 GATE_BREAK_IF_FAILED(ret);
1371
1372 current_ctx = (gate_thco_context_t*)id;
1373 new_ctx = gate_thco_context_new(current_ctx->session, func, param);
1374 if (NULL == new_ctx)
1375 {
1376 ret = GATE_RESULT_OUTOFRESOURCES;
1377 break;
1378 }
1379
1380 if (ptr_id != NULL)
1381 {
1382 *ptr_id = (gate_coroutine_id_t)new_ctx;
1383 }
1384 } while (0);
1385
1386 return ret;
1387 }
1388 gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id)
1389 {
1390 gate_result_t result;
1391 gate_coroutine_id_t current_id;
1392 gate_thco_context_t volatile* ptr_current_ctx;
1393 gate_thco_context_t volatile* ptr_next_ctx;
1394 gate_index_t next_index;
1395
1396 do
1397 {
1398 result = gate_coroutine_get_current(&current_id);
1399 GATE_BREAK_IF_FAILED(result);
1400 ptr_current_ctx = (gate_thco_context_t volatile*)current_id;
1401 ptr_next_ctx = (gate_thco_context_t volatile*)id;
1402
1403 if (ptr_current_ctx->session != ptr_next_ctx->session)
1404 {
1405 result = GATE_RESULT_INVALIDARG;
1406 break;
1407 }
1408
1409 next_index = gate_thco_get_index(ptr_next_ctx);
1410 if (next_index < 0)
1411 {
1412 result = GATE_RESULT_INVALIDARG;
1413 break;
1414 }
1415
1416 if (ptr_next_ctx->state != GATE_THCO_STATE_RUNNING)
1417 {
1418 result = GATE_RESULT_INVALIDSTATE;
1419 break;
1420 }
1421
1422 result = gate_syncevent_reset((gate_syncevent_t*)&ptr_current_ctx->run_event);
1423 GATE_BREAK_IF_FAILED(result);
1424 ptr_next_ctx->session->active_context = next_index;
1425 result = gate_syncevent_set((gate_syncevent_t*)&ptr_next_ctx->run_event);
1426 GATE_BREAK_IF_FAILED(result);
1427 gate_syncevent_wait((gate_syncevent_t*)&ptr_current_ctx->run_event);
1428 } while (0);
1429
1430 return result;
1431 }
1432 gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result)
1433 {
1434 gate_result_t ret;
1435
1436 do
1437 {
1438 gate_coroutine_id_t current_id;
1439 gate_thco_context_t volatile* ptr_current_ctx;
1440 gate_thco_context_t volatile* ptr_next_ctx;
1441
1442 ret = gate_coroutine_get_current(&current_id);
1443 GATE_BREAK_IF_FAILED(ret);
1444
1445 ptr_current_ctx = (gate_thco_context_t volatile*)current_id;
1446 ptr_next_ctx = (gate_thco_context_t volatile*)id;
1447 if (ptr_current_ctx->session != ptr_next_ctx->session)
1448 {
1449 ret = GATE_RESULT_INVALIDARG;
1450 break;
1451 }
1452
1453 while (GATE_SUCCEEDED(ret) && (ptr_next_ctx->state == GATE_THCO_STATE_RUNNING))
1454 {
1455 ret = gate_coroutine_switch_to(id);
1456 }
1457 GATE_BREAK_IF_FAILED(ret);
1458
1459 if (ptr_next_ctx->state == GATE_THCO_STATE_COMPLETED)
1460 {
1461 if (ptr_coroutine_result)
1462 {
1463 *ptr_coroutine_result = ptr_next_ctx->result;
1464 }
1465 gate_thco_context_destroy(ptr_next_ctx);
1466 ptr_next_ctx->state = GATE_THCO_STATE_NONE;
1467 ret = GATE_RESULT_OK;
1468 }
1469 else
1470 {
1471 ret = GATE_RESULT_INVALIDSTATE;
1472 }
1473 } while (0);
1474
1475 return ret;
1476 }
1477
1478 #endif /* GATE_COROUTINES_THREADS */
1479
1480
1481 #if defined(GATE_COROUTINES_NATIVE_STACKSWITCH)
1482
1483 #include "gate/callstacks.h"
1484
1485 /* STAck based COroutines = STACO */
1486
1487 #define GATE_STACO_DEFAULT_SIZE (1024 * 1024)
1488
1489 #define GATE_STACO_STATE_UNINIT 0
1490 #define GATE_STACO_STATE_RUNNING 1
1491 #define GATE_STACO_STATE_COMPLETED 2
1492
1493 typedef struct gate_staco_session_class gate_staco_session_t;
1494
1495 typedef struct gate_staco_context_class
1496 {
1497 gate_callstack_context_t stack_ctx;
1498 void* stack_ptr;
1499 gate_size_t stack_size;
1500 gate_staco_session_t volatile* session;
1501 unsigned state;
1502 gate_coroutine_function_t func;
1503 void* param;
1504 gate_result_t result;
1505 } gate_staco_context_t;
1506
1507 struct gate_staco_session_class
1508 {
1509 gate_callstack_context_t volatile scheduler_stack_ctx;
1510
1511 gate_staco_context_t volatile contexts[32];
1512 gate_index_t volatile current_context;
1513 gate_index_t volatile next_context;
1514 };
1515
1516 static gate_staco_session_t* global_staco_session = NULL;
1517
1518 static gate_staco_session_t* gate_staco_session_get()
1519 {
1520 return global_staco_session;
1521 }
1522
1523
1524 gate_bool_t gate_coroutine_supported()
1525 {
1526 return true;
1527 }
1528
1529 struct gate_staco_entry_param
1530 {
1531 gate_staco_context_t volatile* target_context;
1532 gate_callstack_context_t volatile* creator_context;
1533 gate_callstack_context_t volatile* exit_context;
1534 };
1535
1536 static gate_result_t gate_staco_switch_context(gate_callstack_context_t volatile* current_to_save,
1537 gate_callstack_context_t volatile* next_to_load)
1538 {
1539 return gate_callstack_switch((gate_callstack_context_t*)current_to_save, (gate_callstack_context_t*)next_to_load);
1540 }
1541
1542 static gate_result_t gate_staco_entry(void* param)
1543 {
1544 struct gate_staco_entry_param* entry_param = (struct gate_staco_entry_param*)param;
1545
1546 gate_staco_context_t volatile* local_context = entry_param->target_context;
1547 gate_callstack_context_t volatile* ptr_creator_context = entry_param->creator_context;
1548 gate_callstack_context_t volatile* ptr_exit_context = entry_param->exit_context;
1549
1550 local_context->state = GATE_STACO_STATE_RUNNING;
1551
1552 gate_staco_switch_context(&local_context->stack_ctx, ptr_creator_context);
1553
1554 local_context->result = local_context->func(local_context->param);
1555 local_context->state = GATE_STACO_STATE_COMPLETED;
1556
1557 gate_staco_switch_context(&local_context->stack_ctx, ptr_exit_context);
1558 return GATE_RESULT_OK;
1559 }
1560
1561 static gate_index_t gate_staco_get_free_context_index(gate_staco_session_t* session)
1562 {
1563 gate_size_t ndx;
1564 gate_size_t count = sizeof(session->contexts) / sizeof(session->contexts[0]);
1565 for (ndx = 0; ndx != count; ++ndx)
1566 {
1567 if (session->contexts[ndx].state == GATE_STACO_STATE_UNINIT)
1568 {
1569 return (gate_index_t)ndx;
1570 }
1571 }
1572 return -1;
1573 }
1574
1575 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)
1576 {
1577 gate_result_t ret = GATE_RESULT_FAILED;
1578 struct gate_staco_entry_param entry_param;
1579 gate_callstack_context_t volatile creator_ctx = GATE_INIT_EMPTY;
1580 gate_staco_context_t volatile* ptr_ctx;
1581 gate_result_t retval_dummy = GATE_RESULT_FAILED;
1582
1583 do
1584 {
1585 ptr_ctx = &session->contexts[index];
1586 ptr_ctx->func = func;
1587 ptr_ctx->param = func_param;
1588 ptr_ctx->result = GATE_RESULT_UNKNOWNERROR;
1589 ptr_ctx->session = session;
1590 ptr_ctx->stack_size = GATE_STACO_DEFAULT_SIZE;
1591 ret = gate_callstack_create((gate_size_t*)&ptr_ctx->stack_size, (void**)&ptr_ctx->stack_ptr);
1592 GATE_BREAK_IF_FAILED(ret);
1593
1594 entry_param.target_context = ptr_ctx;
1595 entry_param.creator_context = &creator_ctx;
1596 entry_param.exit_context = &session->scheduler_stack_ctx;
1597 ret = gate_callstack_run(ptr_ctx->stack_ptr, ptr_ctx->stack_size,
1598 (gate_callstack_context_t*)&creator_ctx,
1599 &gate_staco_entry, &entry_param,
1600 &retval_dummy);
1601
1602 if (GATE_FAILED(ret))
1603 {
1604 gate_callstack_destroy(ptr_ctx->stack_ptr, ptr_ctx->stack_size);
1605 }
1606 } while (0);
1607
1608 return ret;
1609 }
1610
1611 static void gate_staco_destroy_context(gate_staco_context_t volatile* ctx)
1612 {
1613 if (ctx->stack_ptr)
1614 {
1615 gate_callstack_destroy(ctx->stack_ptr, ctx->stack_size);
1616 }
1617 gate_mem_clear((void*)ctx, sizeof(gate_staco_context_t));
1618 }
1619
1620 static gate_result_t gate_staco_scheduler(gate_staco_session_t* session, gate_coroutine_function_t first_func, void* first_param)
1621 {
1622 gate_result_t ret = GATE_RESULT_FAILED;
1623 gate_staco_context_t volatile* ptr_next_context;
1624 gate_size_t ndx;
1625 gate_size_t const count = sizeof(session->contexts) / sizeof(session->contexts[0]);
1626 gate_size_t check_index;
1627 gate_index_t next_context_index;
1628
1629 do
1630 {
1631 gate_mem_clear(session, sizeof(gate_staco_session_t));
1632
1633 ret = gate_staco_create_context(session, 0, first_func, first_param);
1634 GATE_BREAK_IF_FAILED(ret);
1635
1636 session->current_context = 0;
1637
1638 for (;;)
1639 {
1640 next_context_index = -1;
1641
1642 if ((session->next_context >= 0) && (session->next_context < (gate_index_t)count))
1643 {
1644 if (session->contexts[session->next_context].state == GATE_STACO_STATE_RUNNING)
1645 {
1646 next_context_index = session->next_context;
1647 }
1648 session->next_context = -1;
1649 }
1650
1651 if (next_context_index == -1)
1652 {
1653 for (ndx = 0; ndx != count; ++ndx)
1654 {
1655 check_index = (gate_size_t)(session->current_context + 1 + ndx) % count;
1656 if (session->contexts[check_index].state == GATE_STACO_STATE_RUNNING)
1657 {
1658 next_context_index = check_index;
1659 break;
1660 }
1661 }
1662 }
1663
1664 if (next_context_index < 0)
1665 {
1666 break;
1667 }
1668
1669 ptr_next_context = &session->contexts[next_context_index];
1670 session->current_context = next_context_index;
1671 ret = gate_staco_switch_context(&session->scheduler_stack_ctx, &ptr_next_context->stack_ctx);
1672 GATE_BREAK_IF_FAILED(ret);
1673
1674 }
1675
1676 } while (0);
1677 return ret;
1678 }
1679
1680 static gate_result_t gate_staco_start_session(gate_coroutine_function_t func, void* param)
1681 {
1682 gate_result_t ret;
1683 gate_staco_session_t session;
1684
1685 gate_mem_clear(&session, sizeof(session));
1686 global_staco_session = &session;
1687 ret = gate_staco_scheduler(&session, func, param);
1688 global_staco_session = NULL;
1689 return ret;
1690 }
1691
1692 static gate_result_t gate_staco_switch_to_scheduler()
1693 {
1694 gate_result_t ret = GATE_RESULT_FAILED;
1695 gate_staco_session_t* session;
1696 gate_index_t current_context_index;
1697 do
1698 {
1699 session = gate_staco_session_get();
1700 if (!session)
1701 {
1702 ret = GATE_RESULT_INVALIDSTATE;
1703 break;
1704 }
1705 current_context_index = session->current_context;
1706 if ((current_context_index < 0) || (current_context_index >= sizeof(session->contexts) / sizeof(session->contexts[0])))
1707 {
1708 ret = GATE_RESULT_INVALIDSTATE;
1709 break;
1710 }
1711 ret = gate_staco_switch_context(&session->contexts[current_context_index].stack_ctx, &session->scheduler_stack_ctx);
1712 } while (0);
1713
1714 return ret;
1715 }
1716
1717 gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param)
1718 {
1719 gate_result_t ret;
1720 gate_coroutine_id_t id = 0;
1721 gate_staco_session_t* session = gate_staco_session_get();
1722
1723 if (session == NULL)
1724 {
1725 /* no session registered, register new one */
1726 ret = gate_staco_start_session(func, param);
1727 }
1728 else
1729 {
1730 /* session registered, just add a new coroutine and wait for it */
1731 ret = gate_coroutine_create(func, param, &id);
1732 if (GATE_SUCCEEDED(ret))
1733 {
1734 ret = gate_coroutine_await(id, NULL);
1735 }
1736 }
1737 return ret;
1738 }
1739
1740 gate_bool_t gate_coroutine_enabled()
1741 {
1742 return gate_staco_session_get() != NULL;
1743 }
1744 gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id)
1745 {
1746 gate_result_t ret = GATE_RESULT_INVALIDSTATE;
1747 gate_staco_session_t* session = gate_staco_session_get();
1748 do
1749 {
1750 if (!session)
1751 {
1752 break;
1753 }
1754
1755 if (ptr_id)
1756 {
1757 *ptr_id = session->current_context;
1758 }
1759 ret = GATE_RESULT_OK;
1760 } while (0);
1761 return ret;
1762 }
1763
1764 gate_result_t gate_coroutine_yield()
1765 {
1766 gate_result_t ret = gate_staco_switch_to_scheduler();
1767 return ret;
1768 }
1769
1770 gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id)
1771 {
1772 gate_result_t ret = GATE_RESULT_FAILED;
1773 gate_staco_session_t* session = gate_staco_session_get();
1774 gate_index_t free_id;
1775
1776 do
1777 {
1778 if (!session)
1779 {
1780 ret = GATE_RESULT_INVALIDSTATE;
1781 break;
1782 }
1783 free_id = gate_staco_get_free_context_index(session);
1784 if (free_id < 0)
1785 {
1786 ret = GATE_RESULT_OUTOFRESOURCES;
1787 break;
1788 }
1789 ret = gate_staco_create_context(session, free_id, func, param);
1790 GATE_BREAK_IF_FAILED(ret);
1791 if (ptr_id)
1792 {
1793 *ptr_id = free_id;
1794 }
1795 } while (0);
1796 return ret;
1797 }
1798 gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id)
1799 {
1800 gate_result_t ret = GATE_RESULT_FAILED;
1801 gate_staco_session_t* session = gate_staco_session_get();
1802
1803 do
1804 {
1805 if (!session)
1806 {
1807 ret = GATE_RESULT_INVALIDSTATE;
1808 break;
1809 }
1810
1811 if ((id < 0) && (id >= sizeof(session->contexts) / sizeof(session->contexts[0])))
1812 {
1813 ret = GATE_RESULT_INVALIDARG;
1814 break;
1815 }
1816
1817 if (session->contexts[id].state != GATE_STACO_STATE_RUNNING)
1818 {
1819 ret = GATE_RESULT_INVALIDSTATE;
1820 break;
1821 }
1822
1823 session->next_context = id;
1824 ret = gate_staco_switch_to_scheduler();
1825
1826 } while (0);
1827 return ret;
1828
1829 }
1830
1831 gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result)
1832 {
1833 gate_result_t ret = GATE_RESULT_FAILED;
1834 gate_staco_session_t* session = gate_staco_session_get();
1835 gate_staco_context_t volatile* ptr_target_ctx;
1836
1837 do
1838 {
1839 if (!session)
1840 {
1841 ret = GATE_RESULT_INVALIDSTATE;
1842 break;
1843 }
1844
1845 if ((id < 0) && (id >= sizeof(session->contexts) / sizeof(session->contexts[0])))
1846 {
1847 ret = GATE_RESULT_INVALIDARG;
1848 break;
1849 }
1850
1851 ptr_target_ctx = &session->contexts[id];
1852
1853 ret = GATE_RESULT_OK;
1854 while (ptr_target_ctx->state == GATE_STACO_STATE_RUNNING)
1855 {
1856 ret = gate_staco_switch_to_scheduler();
1857 GATE_BREAK_IF_FAILED(ret);
1858 }
1859 GATE_BREAK_IF_FAILED(ret);
1860 if (ptr_target_ctx->state != GATE_STACO_STATE_COMPLETED)
1861 {
1862 ret = GATE_RESULT_INVALIDSTATE;
1863 break;
1864 }
1865
1866 if (ptr_coroutine_result)
1867 {
1868 *ptr_coroutine_result = ptr_target_ctx->result;
1869 }
1870
1871 gate_staco_destroy_context(ptr_target_ctx);
1872 gate_staco_destroy_context(ptr_target_ctx);
1873
1874 } while (0);
1875 return ret;
1876 }
1877
1878 #endif /* GATE_COROUTINES_NATIVE_STACKSWITCH */
1879
1880
1881 #if defined(GATE_COROUTINES_NOIMPL)
1882
1883 gate_bool_t gate_coroutine_supported()
1884 {
1885 return false;
1886 }
1887
1888
1889 gate_result_t gate_coroutine_run(gate_coroutine_function_t func, void* param)
1890 {
1891 GATE_UNUSED_ARG(func);
1892 GATE_UNUSED_ARG(param);
1893 return GATE_RESULT_NOTIMPLEMENTED;
1894 }
1895 gate_bool_t gate_coroutine_enabled()
1896 {
1897 return false;
1898 }
1899 gate_result_t gate_coroutine_get_current(gate_coroutine_id_t* ptr_id)
1900 {
1901 GATE_UNUSED_ARG(ptr_id);
1902 return GATE_RESULT_NOTIMPLEMENTED;
1903 }
1904 gate_result_t gate_coroutine_yield()
1905 {
1906 return GATE_RESULT_NOTIMPLEMENTED;
1907 }
1908 gate_result_t gate_coroutine_create(gate_coroutine_function_t func, void* param, gate_coroutine_id_t* ptr_id)
1909 {
1910 GATE_UNUSED_ARG(func);
1911 GATE_UNUSED_ARG(param);
1912 GATE_UNUSED_ARG(ptr_id);
1913 return GATE_RESULT_NOTIMPLEMENTED;
1914 }
1915 gate_result_t gate_coroutine_switch_to(gate_coroutine_id_t id)
1916 {
1917 GATE_UNUSED_ARG(id);
1918 return GATE_RESULT_NOTIMPLEMENTED;
1919 }
1920 gate_result_t gate_coroutine_await(gate_coroutine_id_t id, gate_result_t* ptr_coroutine_result)
1921 {
1922 GATE_UNUSED_ARG(id);
1923 GATE_UNUSED_ARG(ptr_coroutine_result);
1924 return GATE_RESULT_NOTIMPLEMENTED;
1925 }
1926
1927 #endif
1928