Serene Runtime 1.0.0
C runtime for the Serene programming language
Loading...
Searching...
No Matches
fiber_tests.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdatomic.h>
22#include <stdint.h>
23
24#include "base.h"
25#include "serene/rt/fiber.h"
26
27#define FIBER_TESTS(X) \
28 X("fiber::types", test_fiber_types), X("fiber::switch", test_fiber_switch), \
29 X("fiber::stack", test_fiber_stack), \
30 X("fiber::sched::run", test_fiber_sched_run), \
31 X("fiber::sched::yield", test_fiber_sched_yield), \
32 X("fiber::sched::suspend", test_fiber_sched_suspend), \
33 X("fiber::sched::park_abort", test_fiber_sched_park_abort), \
34 X("fiber::sched::registry", test_fiber_sched_registry), \
35 X("fiber::sched::double_wake", test_fiber_sched_double_wake), \
36 X("fiber::sched::wait_for", test_fiber_sched_wait_for), \
37 X("fiber::sched::wait_for_done", test_fiber_sched_wait_for_done), \
38 X("fiber::sched::wait_for_many", test_fiber_sched_wait_for_many), \
39 X("fiber::sched::mt_run", test_fiber_sched_mt_run), \
40 X("fiber::sched::mt_yield", test_fiber_sched_mt_yield), \
41 X("fiber::sched::stop", test_fiber_sched_stop)
42
43static void test_fiber_types() {
44 // Smoke test: the public types exist and the build/test wiring works.
45 // The saved context is a single stack-pointer word.
46 TEST_CHECK(sizeof(srn_fiber_ctx_t) == sizeof(void *));
47 // The lifecycle states run from creation to completion in order.
53}
54
55// The POSIX provider hands back a guard-paged region: `guard` is the mmap base
56// (the PROT_NONE page), `limit` is the low end of the usable area one page
57// above it, and `start` is the high end where the stack pointer begins.
58static void test_fiber_stack() {
59 size_t page = srn_mm_get_os_page_size();
60
62
63 TEST_CHECK(s.guard != nullptr);
64 TEST_CHECK(s.limit != nullptr);
65 TEST_CHECK(s.start != nullptr);
66 // The guard sits exactly one page below the usable region.
67 TEST_CHECK((char *)s.limit == (char *)s.guard + page);
68 // The usable region is non-empty, page-aligned, and at least the default.
69 TEST_CHECK((char *)s.start > (char *)s.limit);
70 TEST_CHECK(((uintptr_t)s.limit & (page - 1)) == 0);
72
73 // The whole usable region is writable end to end. The guard is left alone.
74 ((char *)s.limit)[0] = (char)0x5A;
75 ((char *)s.start)[-1] = (char)0x5A;
76
78}
79
80// Ping-pong: a worker fiber and the calling thread hand control back and forth.
81// This exercises the whole switch path -- srn_fiber_ctx_make seeds the worker,
82// the first srn_fiber_switch starts it via the trampoline, each yield resumes
83// the thread, and the worker finishes with srn_fiber_switch_final. The worker
84// runs on a real guard-paged stack from the provider.
87static int fiber_pp_ticks;
88static int fiber_pp_rounds;
89
99
100static void test_fiber_switch() {
102
104
106 fiber_pp_worker.stack = worker_stack;
108 srn_fiber_ctx_make(&fiber_pp_worker.fiber_ctx, worker_stack, fiber_pp_entry,
109 nullptr);
110
111 fiber_pp_ticks = 0;
112 fiber_pp_rounds = 5;
113
114 // `rounds` yields, plus one more switch to let the worker run to completion.
115 for (int i = 0; i <= fiber_pp_rounds; i++) {
117 }
118
121
122 srn_fiber_stack_free(worker_stack);
123}
124
125// A shared non-null return value: the launcher rejects a null entry result, and
126// these tests do not inspect the payload, only that a fiber ran and finished.
127static int fiber_ok_value;
128
129// fiber::sched::run -- spawn N fibers, each bumps a counter and returns. After
130// srn_sched_run drains the queue every fiber must have run once and reached
131// DONE, with its result recorded.
133
135 UNUSED(ctx);
136 UNUSED(arg);
138 return &fiber_ok_value;
139}
140
141static void test_fiber_sched_run() {
142 MAKE_ENGINE(mm, engine);
143 MAKE_CONTEXT(engine, ctx);
144 srn_scheduler_t *sched = engine->scheduler;
145 ASSERT_NOT_NULL(sched);
146
148 enum { N = 4 };
149 srn_fiber_t *fibers[N];
150 for (int i = 0; i < N; i++) {
151 fibers[i] = srn_fiber_make(ctx, sched, fiber_run_entry, nullptr, 0);
152 ASSERT_NOT_NULL(fibers[i]);
153 }
154
155 srn_sched_run(sched, 1);
156
158 for (int i = 0; i < N; i++) {
159 // The struct lives in the context block after reaping. Only the stack is
160 // released, so state and result are still readable here.
161 TEST_CHECK(fibers[i]->state == SRN_FIBER_DONE);
162 TEST_CHECK(fibers[i]->result == &fiber_ok_value);
163 }
164
165 RELEASE_CONTEXT(ctx);
166 SHUTDOWN_ENGINE(mm, engine);
167}
168
169// fiber::sched::yield -- two fibers each log their id twice, yielding between.
170// Run on one worker. Per-worker queues do not guarantee a strict interleaving,
171// so the invariant checked is that both fibers ran to completion: each id
172// appears exactly twice, four entries in all.
173static int fiber_yield_log[4];
174static int fiber_yield_n;
175
177 UNUSED(ctx);
178 int id = (int)(intptr_t)arg;
179 for (int i = 0; i < 2; i++) {
182 }
183 return &fiber_ok_value;
184}
185
187 MAKE_ENGINE(mm, engine);
188 MAKE_CONTEXT(engine, ctx);
189 srn_scheduler_t *sched = engine->scheduler;
190 ASSERT_NOT_NULL(sched);
191
192 fiber_yield_n = 0;
193 (void)srn_fiber_make(ctx, sched, fiber_yield_entry, (void *)(intptr_t)1, 0);
194 (void)srn_fiber_make(ctx, sched, fiber_yield_entry, (void *)(intptr_t)2, 0);
195
196 srn_sched_run(sched, 1);
197
199 int ones = 0;
200 int twos = 0;
201 for (int i = 0; i < 4; i++) {
202 if (fiber_yield_log[i] == 1) {
203 ones++;
204 } else if (fiber_yield_log[i] == 2) {
205 twos++;
206 }
207 }
208 TEST_CHECK(ones == 2);
209 TEST_CHECK(twos == 2);
210
211 RELEASE_CONTEXT(ctx);
212 SHUTDOWN_ENGINE(mm, engine);
213}
214
215// fiber::sched::suspend -- a one-slot mailbox handoff. The consumer runs first
216// and parks. Its commit registers it as the waiter (the slot is empty). The
217// producer fills the slot and readies the consumer, which resumes and reads it.
218typedef struct {
219 int value;
223
225static int fiber_mbox_got;
226
227// Park commit: register as the mailbox waiter, unless a value is already there
228// (in which case decline to park so the consumer resumes at once).
229static bool fiber_mbox_park(srn_fiber_t *self, void *arg) {
230 fiber_mbox_t *mb = arg;
231 if (mb->has_value) {
232 return false;
233 }
234 mb->waiter = self;
235 return true;
236}
237
245
247 UNUSED(ctx);
248 UNUSED(arg);
249 fiber_mbox.value = 42;
250 fiber_mbox.has_value = true;
251 if (fiber_mbox.waiter != nullptr) {
252 srn_fiber_t *w = fiber_mbox.waiter;
253 fiber_mbox.waiter = nullptr;
255 }
256 return &fiber_ok_value;
257}
258
260 MAKE_ENGINE(mm, engine);
261 MAKE_CONTEXT(engine, ctx);
262 srn_scheduler_t *sched = engine->scheduler;
263 ASSERT_NOT_NULL(sched);
264
266 fiber_mbox_got = 0;
267
268 // Order matters: the consumer must be enqueued first so it runs and suspends
269 // before the producer fills the slot.
270 (void)srn_fiber_make(ctx, sched, fiber_consumer_entry, nullptr, 0);
271 (void)srn_fiber_make(ctx, sched, fiber_producer_entry, nullptr, 0);
272
273 srn_sched_run(sched, 1);
274
276
277 RELEASE_CONTEXT(ctx);
278 SHUTDOWN_ENGINE(mm, engine);
279}
280
281// fiber::sched::park_abort -- the commit declines to park. The value is already
282// in the slot when the consumer runs, so fiber_mbox_park returns false and the
283// consumer resumes immediately without ever parking or registering a waiter.
285 MAKE_ENGINE(mm, engine);
286 MAKE_CONTEXT(engine, ctx);
287 srn_scheduler_t *sched = engine->scheduler;
288 ASSERT_NOT_NULL(sched);
289
291 fiber_mbox.value = 7;
292 fiber_mbox.has_value = true; // value present before the consumer runs
293 fiber_mbox_got = 0;
294
295 (void)srn_fiber_make(ctx, sched, fiber_consumer_entry, nullptr, 0);
296
297 srn_sched_run(sched, 1);
298
300 TEST_CHECK(fiber_mbox.waiter == nullptr); // never registered as a waiter
301
302 RELEASE_CONTEXT(ctx);
303 SHUTDOWN_ENGINE(mm, engine);
304}
305
306// fiber::sched::registry -- a fiber that suspends with no one to wake it. The
307// run loop drains the ready queue and returns, leaving the fiber parked and its
308// stack mapped. The scheduler still tracks it through the registry, so shutdown
309// reclaims the stack instead of leaking it.
310// Park commit that registers the fiber nowhere, so nothing can ever wake it.
311static bool fiber_stuck_park(srn_fiber_t *self, void *arg) {
312 UNUSED(self);
313 UNUSED(arg);
314 return true; // stay parked, with no waker
315}
316
318 UNUSED(ctx);
319 UNUSED(arg);
320 srn_fiber_suspend(fiber_stuck_park, nullptr); // never readied
321 return &fiber_ok_value;
322}
323
325 MAKE_ENGINE(mm, engine);
326 MAKE_CONTEXT(engine, ctx);
327 srn_scheduler_t *sched = engine->scheduler;
328 ASSERT_NOT_NULL(sched);
329
330 srn_fiber_t *stuck =
331 srn_fiber_make(ctx, sched, fiber_stuck_entry, nullptr, 0);
332 ASSERT_NOT_NULL(stuck);
333
334 // Drains the ready queue. The stuck fiber suspends and is abandoned.
335 srn_sched_run(sched, 1);
337
338 // The registry lets shutdown find and free the abandoned fiber's stack.
339 srn_sched_shutdown(sched);
340
341 RELEASE_CONTEXT(ctx);
342 SHUTDOWN_ENGINE(mm, engine);
343}
344
345// fiber::sched::double_wake -- two wakers ready the same suspended fiber (as an
346// IO event and a timeout might race). The wake must be idempotent: the fiber
347// runs exactly once, not enqueued twice. A double enqueue would resume an
348// already-reaped fiber on the second pass and trip the sanitizer.
352
353static bool fiber_dwake_park(srn_fiber_t *self, void *arg) {
354 UNUSED(arg);
356 return false;
357 }
358 fiber_dwake_waiter = self;
359 return true;
360}
361
363 void *arg) {
364 UNUSED(ctx);
365 UNUSED(arg);
368 return &fiber_ok_value;
369}
370
372 void *arg) {
373 UNUSED(ctx);
374 UNUSED(arg);
376 if (fiber_dwake_waiter != nullptr) {
378 fiber_dwake_waiter = nullptr;
379 srn_fiber_ready(w); // first waker: SUSPENDED -> READY, enqueued
380 srn_fiber_ready(w); // second waker: already READY -> no-op
381 }
382 return &fiber_ok_value;
383}
384
386 MAKE_ENGINE(mm, engine);
387 MAKE_CONTEXT(engine, ctx);
388 srn_scheduler_t *sched = engine->scheduler;
389 ASSERT_NOT_NULL(sched);
390
391 fiber_dwake_waiter = nullptr;
392 fiber_dwake_signalled = false;
394
395 // Consumer first, so it runs and suspends before the producer wakes it.
396 (void)srn_fiber_make(ctx, sched, fiber_dwake_consumer_entry, nullptr, 0);
397 (void)srn_fiber_make(ctx, sched, fiber_dwake_producer_entry, nullptr, 0);
398
399 srn_sched_run(sched, 1);
400
402
403 RELEASE_CONTEXT(ctx);
404 SHUTDOWN_ENGINE(mm, engine);
405}
406
407// fiber::sched::wait_for -- the caller blocks until the target finishes and
408// reads its result. The waiter runs first and parks. The target runs later,
409// finishes, and the DONE handling wakes the waiter.
410static int fiber_wf_result; // the target's result
411static srn_fiber_t *fiber_wf_target; // set before the run
412static srn_fiber_result_t fiber_wf_seen; // what the waiter read
413
415 UNUSED(ctx);
416 UNUSED(arg);
417 return &fiber_wf_result;
418}
419
426
428 MAKE_ENGINE(mm, engine);
429 MAKE_CONTEXT(engine, ctx);
430 srn_scheduler_t *sched = engine->scheduler;
431 ASSERT_NOT_NULL(sched);
432
433 fiber_wf_seen = nullptr;
434 // Waiter made first, so it runs and parks before the target finishes.
435 (void)srn_fiber_make(ctx, sched, fiber_wf_waiter_entry, nullptr, 0);
437 srn_fiber_make(ctx, sched, fiber_wf_target_entry, nullptr, 0);
438
439 srn_sched_run(sched, 1);
440
442
443 RELEASE_CONTEXT(ctx);
444 SHUTDOWN_ENGINE(mm, engine);
445}
446
447// fiber::sched::wait_for_done -- waiting for an already-finished target returns
448// its result at once. The commit sees DONE and declines to park.
450 MAKE_ENGINE(mm, engine);
451 MAKE_CONTEXT(engine, ctx);
452 srn_scheduler_t *sched = engine->scheduler;
453 ASSERT_NOT_NULL(sched);
454
455 fiber_wf_seen = nullptr;
456 // Target made first, so it runs and finishes before the waiter runs.
458 srn_fiber_make(ctx, sched, fiber_wf_target_entry, nullptr, 0);
459 (void)srn_fiber_make(ctx, sched, fiber_wf_waiter_entry, nullptr, 0);
460
461 srn_sched_run(sched, 1);
462
464
465 RELEASE_CONTEXT(ctx);
466 SHUTDOWN_ENGINE(mm, engine);
467}
468
469// fiber::sched::wait_for_many -- several fibers wait for one target. All of
470// them wake and read its result once it finishes.
471static int fiber_wf_woke;
472
474 void *arg) {
475 UNUSED(ctx);
476 UNUSED(arg);
479 }
480 return &fiber_ok_value;
481}
482
484 MAKE_ENGINE(mm, engine);
485 MAKE_CONTEXT(engine, ctx);
486 srn_scheduler_t *sched = engine->scheduler;
487 ASSERT_NOT_NULL(sched);
488
489 fiber_wf_woke = 0;
490 // Three waiters made first so they all park, then the target finishes.
491 (void)srn_fiber_make(ctx, sched, fiber_wf_counting_waiter, nullptr, 0);
492 (void)srn_fiber_make(ctx, sched, fiber_wf_counting_waiter, nullptr, 0);
493 (void)srn_fiber_make(ctx, sched, fiber_wf_counting_waiter, nullptr, 0);
495 srn_fiber_make(ctx, sched, fiber_wf_target_entry, nullptr, 0);
496
497 srn_sched_run(sched, 1);
498
500
501 RELEASE_CONTEXT(ctx);
502 SHUTDOWN_ENGINE(mm, engine);
503}
504
505// fiber::sched::mt_run -- many fibers across several worker threads, each doing
506// one atomic increment. With true parallelism the only safe shared state is the
507// atomic counter. After the run every increment must have landed (no lost
508// updates) and the pool must have reached quiescence, which is what makes
509// srn_sched_run return.
510static atomic_int fiber_mt_counter;
511
513 UNUSED(ctx);
514 UNUSED(arg);
515 atomic_fetch_add(&fiber_mt_counter, 1);
516 return &fiber_ok_value;
517}
518
520 MAKE_ENGINE(mm, engine);
521 MAKE_CONTEXT(engine, ctx);
522 srn_scheduler_t *sched = engine->scheduler;
523 ASSERT_NOT_NULL(sched);
524
525 atomic_store(&fiber_mt_counter, 0);
526 enum { N = 256 };
527 for (int i = 0; i < N; i++) {
528 (void)srn_fiber_make(ctx, sched, fiber_mt_entry, nullptr, 0);
529 }
530
531 srn_sched_run(sched, 4);
532
533 TEST_CHECK(atomic_load(&fiber_mt_counter) == N);
534
535 RELEASE_CONTEXT(ctx);
536 SHUTDOWN_ENGINE(mm, engine);
537}
538
539// fiber::sched::mt_yield -- fibers that yield repeatedly across worker threads,
540// so each is re-enqueued between rounds and may resume on a different thread
541// than it last ran on. Exercises the cross-thread re-enqueue and the park/wake
542// path under contention. The counter totals every round of every fiber.
543static atomic_int fiber_mt_yield_counter;
544
546 UNUSED(ctx);
547 int rounds = (int)(intptr_t)arg;
548 for (int i = 0; i < rounds; i++) {
549 atomic_fetch_add(&fiber_mt_yield_counter, 1);
551 }
552 return &fiber_ok_value;
553}
554
556 MAKE_ENGINE(mm, engine);
557 MAKE_CONTEXT(engine, ctx);
558 srn_scheduler_t *sched = engine->scheduler;
559 ASSERT_NOT_NULL(sched);
560
561 atomic_store(&fiber_mt_yield_counter, 0);
562 enum { N = 64, ROUNDS = 8 };
563 for (int i = 0; i < N; i++) {
564 (void)srn_fiber_make(ctx, sched, fiber_mt_yield_entry,
565 (void *)(intptr_t)ROUNDS, 0);
566 }
567
568 srn_sched_run(sched, 4);
569
570 TEST_CHECK(atomic_load(&fiber_mt_yield_counter) == N * ROUNDS);
571
572 RELEASE_CONTEXT(ctx);
573 SHUTDOWN_ENGINE(mm, engine);
574}
575
576// fiber::sched::stop -- a fiber stops the scheduler before all the work is
577// done. The stopper runs a few rounds then calls srn_sched_stop, so
578// srn_sched_run returns instead of draining the queue. The pending fibers
579// behind it are left unrun, and srn_sched_shutdown reaps their stacks. Checks
580// the run returns rather than hangs, only the stopper ran, and the leftover
581// teardown is clean.
582static atomic_int fiber_stop_rounds;
583
585 UNUSED(ctx);
586 srn_scheduler_t *sched = arg;
587 for (int i = 0; i < 3; i++) {
588 atomic_fetch_add(&fiber_stop_rounds, 1);
590 }
591 srn_sched_stop(sched);
592 return &fiber_ok_value;
593}
594
596 UNUSED(ctx);
597 UNUSED(arg);
598 // Never reached: the stopper ends the run first. It adds a large amount, so
599 // any accidental execution is visible in the count.
600 atomic_fetch_add(&fiber_stop_rounds, 1000);
601 return &fiber_ok_value;
602}
603
605 MAKE_ENGINE(mm, engine);
606 MAKE_CONTEXT(engine, ctx);
607 srn_scheduler_t *sched = engine->scheduler;
608 ASSERT_NOT_NULL(sched);
609
610 atomic_store(&fiber_stop_rounds, 0);
611 // Stopper first so it reaches the single worker before the pending fibers.
612 (void)srn_fiber_make(ctx, sched, fiber_stopper_entry, sched, 0);
613 enum { PENDING = 3 };
614 for (int i = 0; i < PENDING; i++) {
615 (void)srn_fiber_make(ctx, sched, fiber_pending_entry, nullptr, 0);
616 }
617
618 // Returns when the stopper calls srn_sched_stop, with the pending fibers
619 // still queued.
620 srn_sched_run(sched, 1);
621
622 // Only the stopper ran (three rounds); no pending fiber ran.
623 TEST_CHECK(atomic_load(&fiber_stop_rounds) == 3);
624
625 // Reap the pending fibers while their structs (in the context block) are
626 // still alive, before releasing the context. SHUTDOWN_ENGINE calls shutdown
627 // again, which is a no-op on an already torn-down scheduler.
628 srn_sched_shutdown(sched);
629
630 RELEASE_CONTEXT(ctx);
631 SHUTDOWN_ENGINE(mm, engine);
632}
#define TEST_CHECK(cond)
Definition acutest.h:96
#define RELEASE_CONTEXT(x)
Definition base.h:46
#define ASSERT_NOT_NULL(x)
Definition base.h:30
#define SHUTDOWN_ENGINE(mm, engine)
Definition base.h:38
#define MAKE_ENGINE(mm, engine)
Definition base.h:32
#define MAKE_CONTEXT(engine, x)
Definition base.h:42
void srn_fiber_ctx_make(srn_fiber_ctx_t *fiber_ctx, srn_fiber_stack_t stack, void(*fn)(void *), void *arg)
Initialise a fresh fiber context so the first srn_fiber_swap into it begins executing fn(arg) on stac...
Definition ctx_x86_64.c:29
size_t srn_mm_get_os_page_size(void)
Retutrns the OS page size.
Definition default.c:270
void srn_fiber_switch_final(srn_fiber_t *to)
Like srn_fiber_switch, but for a fiber that has finished and must not be resumed: control transfers t...
Definition fiber.c:85
srn_fiber_t * srn_fiber_make(srn_context_t *ctx, srn_scheduler_t *sched, srn_fiber_entry_t entry, void *arg, size_t stack_size)
Create a fiber that will run entry(ctx, arg).
Definition fiber.c:169
void srn_fiber_init_thread(srn_fiber_t *f)
Represent the calling OS thread as the running fiber ("#0"), so the scheduler or a test can switch aw...
Definition fiber.c:137
void srn_fiber_switch(srn_fiber_t *from, srn_fiber_t *to)
Compiled without AddressSanitizer instrumentation: in stack-use-after-return mode ASan would place fr...
Definition fiber.c:62
void srn_fiber_on_entry(srn_fiber_t *from)
Call as the first action inside a fresh fiber's entry.
Definition fiber.c:107
AI Generated (🤦) Fiber subsystem overview.
static size_t srn_fiber_stack_size(srn_fiber_stack_t s)
Definition fiber.h:437
@ SRN_FIBER_NEW
Created, stack mapped, never resumed.
Definition fiber.h:182
@ SRN_FIBER_RUNNING
Currently executing.
Definition fiber.h:186
@ SRN_FIBER_READY
On the run queue, eligible to run.
Definition fiber.h:184
@ SRN_FIBER_DONE
Entry returned. The result is final.
Definition fiber.h:190
@ SRN_FIBER_SUSPENDED
Parked off the run queue, awaits srn_fiber_ready.
Definition fiber.h:188
#define SRN_FIBER_DEFAULT_STACK_SIZE
Default size of every fiber stack.
Definition fiber.h:138
void * srn_fiber_result_t
Definition fiber.h:117
static int fiber_wf_woke
static int fiber_yield_n
static srn_fiber_t * fiber_dwake_waiter
static void test_fiber_sched_registry()
static void test_fiber_sched_wait_for_many()
static bool fiber_dwake_signalled
static void test_fiber_switch()
static int fiber_wf_result
static atomic_int fiber_mt_yield_counter
static srn_fiber_result_t fiber_pending_entry(srn_context_t *ctx, void *arg)
static void test_fiber_sched_double_wake()
static srn_fiber_result_t fiber_run_entry(srn_context_t *ctx, void *arg)
static bool fiber_mbox_park(srn_fiber_t *self, void *arg)
static srn_fiber_result_t fiber_wf_seen
static atomic_int fiber_mt_counter
static srn_fiber_result_t fiber_dwake_consumer_entry(srn_context_t *ctx, void *arg)
static srn_fiber_result_t fiber_yield_entry(srn_context_t *ctx, void *arg)
static void test_fiber_sched_mt_yield()
static void test_fiber_stack()
Definition fiber_tests.h:58
static srn_fiber_result_t fiber_mt_entry(srn_context_t *ctx, void *arg)
static srn_fiber_result_t fiber_stopper_entry(srn_context_t *ctx, void *arg)
static srn_fiber_result_t fiber_wf_target_entry(srn_context_t *ctx, void *arg)
static void test_fiber_types()
Definition fiber_tests.h:43
static int fiber_yield_log[4]
static srn_fiber_t fiber_pp_thread
Definition fiber_tests.h:85
static void test_fiber_sched_mt_run()
static int fiber_run_counter
static srn_fiber_result_t fiber_dwake_producer_entry(srn_context_t *ctx, void *arg)
static int fiber_dwake_runs
static srn_fiber_result_t fiber_wf_counting_waiter(srn_context_t *ctx, void *arg)
static void test_fiber_sched_suspend()
static bool fiber_stuck_park(srn_fiber_t *self, void *arg)
static void test_fiber_sched_park_abort()
static srn_fiber_result_t fiber_stuck_entry(srn_context_t *ctx, void *arg)
static atomic_int fiber_stop_rounds
static srn_fiber_result_t fiber_producer_entry(srn_context_t *ctx, void *arg)
static srn_fiber_result_t fiber_mt_yield_entry(srn_context_t *ctx, void *arg)
static void test_fiber_sched_yield()
static void test_fiber_sched_wait_for()
static bool fiber_dwake_park(srn_fiber_t *self, void *arg)
static void test_fiber_sched_run()
static int fiber_pp_rounds
Definition fiber_tests.h:88
static void fiber_pp_entry(void *arg)
Definition fiber_tests.h:90
static srn_fiber_result_t fiber_wf_waiter_entry(srn_context_t *ctx, void *arg)
static srn_fiber_t * fiber_wf_target
static void test_fiber_sched_stop()
static int fiber_ok_value
static srn_fiber_t fiber_pp_worker
Definition fiber_tests.h:86
static int fiber_pp_ticks
Definition fiber_tests.h:87
static int fiber_mbox_got
static fiber_mbox_t fiber_mbox
static srn_fiber_result_t fiber_consumer_entry(srn_context_t *ctx, void *arg)
static void test_fiber_sched_wait_for_done()
void srn_fiber_ready(srn_fiber_t *fiber)
Mark a suspended fiber runnable again.
Definition scheduler.c:918
srn_fiber_result_t srn_fiber_wait_for(srn_fiber_t *target)
Block the calling fiber until target finishes, then return its result.
Definition scheduler.c:963
void srn_sched_shutdown(srn_scheduler_t *sched)
The one stop tear down of the fiber subsystem, should be called once srn_sched_run has returned.
Definition scheduler.c:314
void srn_sched_stop(srn_scheduler_t *sched)
Ask a running scheduler to stop.
Definition scheduler.c:849
void srn_sched_run(srn_scheduler_t *sched, int nworkers)
Run the scheduler with nworkers os threads draining it, returning once the pool goes quiescent (every...
Definition scheduler.c:784
void srn_fiber_suspend(srn_fiber_park_fn commit, void *arg)
A suspended fiber is on no scheduler queue, and the scheduler does not track what it waits on – whoev...
Definition scheduler.c:898
void srn_fiber_yield(void)
Yield cooperatively: re-enqueue the running fiber and run the next ready one.
Definition scheduler.c:876
srn_fiber_stack_t srn_fiber_stack_alloc(size_t size)
Allocate a stack of at least size usable bytes plus a guard page, or SRN_FIBER_DEFAULT_STACK_SIZE whe...
Definition stack_posix.c:34
void srn_fiber_stack_free(srn_fiber_stack_t stack)
Definition stack_posix.c:67
srn_fiber_t * waiter
The saved context of a suspended fiber is a single word: its stack pointer at the moment it was switc...
Definition fiber.h:147
One stack per fiber, mapped with a guard page at the low end so an overflow faults deterministically ...
Definition fiber.h:168
void * guard
The protected page to detect stack overflows.
Definition fiber.h:174
void * limit
Low end of usable region.
Definition fiber.h:172
void * start
High end, stack pointer initialises to this address.
Definition fiber.h:170
_Atomic srn_fiber_state_t state
The lifecycle state.
Definition fiber.h:217
#define UNUSED(x)
Definition utils.h:43