Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
06_request_term.c
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// Example 06 -- asking the scheduler to stop.
20//
21// A scheduler normally runs until it goes quiescent, every worker idle with no
22// work left. `srn_sched_stop` stops it before that point. Each worker
23// notices the request at the top of its loop and stops once the fiber it is
24// running yields or finishes, so a fiber is never cut off mid step. Work still
25// queued is left unrun, and its stacks are reclaimed at shutdown through the
26// scheduler's registry. The call is safe from any thread but NOT from a
27// signal handler, it takes the scheduler mutex and signals its condition
28// variable. A Ctrl-C or SIGTERM shutdown routes the signal into ordinary
29// thread context first (a thread in sigwait, or a signalfd registered with
30// the reactor) and stops from there.
31//
32// A controller fiber runs first and requests termination, then returns
33// normally. It is the slice in flight, and termination lets it finish cleanly.
34// A batch of task fibers sit queued behind it. Because the controller stops the
35// scheduler before they get a turn, they never run, and the final count of
36// tasks that ran is zero. That is the point: a requested stop abandons queued
37// work rather than draining it.
38
39#include <stdatomic.h>
40#include <stdio.h>
41
42#include <serene/runtime.h>
43
44#define TASK_COUNT 16
45
46static atomic_int tasks_ran;
47
48static srn_fiber_result_t task(srn_context_t *ctx, void *arg) {
49 (void)ctx;
50 (void)arg;
51 atomic_fetch_add(&tasks_ran, 1);
52 return nullptr;
53}
54
56 (void)ctx;
57 // The scheduler arrives through the argument, so the controller can act on
58 // it.
59 srn_scheduler_t *sched = arg;
60 printf("controller: stopping the scheduler before the tasks run\n");
61 srn_sched_stop(sched);
62 // Returning normally is the clean boundary the worker stops on.
63 return nullptr;
64}
65
66int main(void) {
67 SERENE_RUNTIME_INIT_WITH_SCHED(engine, sched, nullptr);
68 srn_context_t *ctx = srn_context_make(engine);
69
70 atomic_store(&tasks_ran, 0);
71
72 // The controller is created first so it reaches the front of the ready queue
73 // first and requests the stop before any task is dequeued.
74 (void)srn_fiber_spawn(ctx, controller, sched);
75 for (int i = 0; i < TASK_COUNT; i++) {
76 (void)srn_fiber_spawn(ctx, task, nullptr);
77 }
78
79 // One worker keeps the order deterministic: the controller runs, stops the
80 // scheduler, and the worker exits at the top of its next loop with the tasks
81 // still queued.
82 srn_sched_run(sched, 1);
83
84 int ran = atomic_load(&tasks_ran);
85 printf("%d of %d tasks ran before termination\n", ran, TASK_COUNT);
86 printf("the rest were left queued and are reclaimed at shutdown\n");
87
88 // Teardown order matters once a run stops with work still queued. The unrun
89 // tasks are still on the scheduler's registry, yet their fiber structs live
90 // in this context's blocks, so the context must not be released while they
91 // are unreaped. SERENE_RUNTIME_SHUTDOWN reaps the scheduler first and the
92 // memory manager then reclaims every context chain, this one included.
94 return 0;
95}
static srn_fiber_result_t controller(srn_context_t *ctx, void *arg)
static srn_fiber_result_t task(srn_context_t *ctx, void *arg)
int main(void)
#define TASK_COUNT
static atomic_int tasks_ran
srn_context_t * srn_context_make(srn_engine_t *engine)
Make an empty context, by allocating a new memory block.
Definition context.c:39
srn_fiber_t * srn_fiber_spawn(srn_context_t *ctx, srn_fiber_entry_t entry, void *arg)
Make and schedule a fiber with every default, the engine's scheduler, the configured stack size,...
Definition fiber.c:247
void * srn_fiber_result_t
What a fiber's entry produces, type-erased.
Definition fiber.h:157
#define SERENE_RUNTIME_SHUTDOWN(engine)
Tear down what SERENE_RUNTIME_INIT brought up, in reverse order, the engine (reactor,...
Definition runtime.h:58
#define SERENE_RUNTIME_INIT_WITH_SCHED(engine, sched, config)
SERENE_RUNTIME_INIT plus a sched variable bound to the engine's scheduler, which every run and fiber ...
Definition runtime.h:46
void srn_sched_stop(srn_scheduler_t *sched)
Ask a running scheduler to stop.
Definition scheduler.c:939
void srn_sched_run(srn_scheduler_t *sched, size_t nworkers)
Run the scheduler with nworkers os threads draining it, returning once the pool goes quiescent (every...
Definition scheduler.c:843