Serene Runtime 1.0.0
C runtime for the Serene programming language
Loading...
Searching...
No Matches
thread_posix.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 library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 library 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19/// @file
20/// POSIX implementation of the thread-level operations in
21/// serene/rt/fiber/thread.h, realised with pthreads. The pthreads mutex and
22/// condition functions return their error code as the return value and leave the
23/// global `errno` untouched. Every such code is mapped to an srn_thread_status_t
24/// so no pthread code escapes the public API. The raw code is also logged
25/// through THREAD_LOG, which is a debug-only diagnostic and not part of the
26/// interface.
27
29#include "serene/utils.h"
30
31#include <errno.h>
32
33#define THREAD_LOG(FMT, ...) DBG("THREAD", FMT __VA_OPT__(, ) __VA_ARGS__)
34
35/// Map a pthreads return code onto the neutral status. Anything without a
36/// dedicated code (EDEADLK, EINVAL, ESRCH, and so on) becomes SRN_THREAD_ERROR.
37/// The precise code is recovered from the THREAD_LOG line at the call site.
39 switch (rc) {
40 case 0:
41 return SRN_THREAD_OK;
42 case EAGAIN:
43 return SRN_THREAD_AGAIN;
44 case ENOMEM:
45 return SRN_THREAD_NOMEM;
46 case EPERM:
47 return SRN_THREAD_PERM;
48 default:
49 return SRN_THREAD_ERROR;
50 }
51}
52
53/// pthreads run a thread through a `void *(*)(void *)` routine, while the
54/// modeled operation runs a `void (*)(void *)`. This trampoline bridges the two:
55/// it reads the function and argument the spawn recorded on the thread, runs
56/// them, and returns the null pthreads expects.
57static void *posix_trampoline(void *arg) {
58 srn_thread_t *t = arg;
59 t->fn(t->arg);
60 return nullptr;
61}
62
63srn_thread_status_t srn_thread_spawn(srn_thread_t *t, void (*fn)(void *), void *arg) {
65 PANIC_IF_NULL(fn);
66
67 t->fn = fn;
68 t->arg = arg;
69
70 int rc = pthread_create(&t->handle, nullptr, posix_trampoline, t);
71 if (rc != 0) {
72 THREAD_LOG("pthread_create failed (errno %d)", rc);
73 }
74 return status_from_errno(rc);
75}
76
79 // The thread's return value is discarded. The trampoline always returns null.
80 int rc = pthread_join(t->handle, nullptr);
81 if (rc != 0) {
82 THREAD_LOG("pthread_join failed (errno %d)", rc);
83 }
84 return status_from_errno(rc);
85}
86
89 int rc = pthread_mutex_init(&m->handle, nullptr);
90 if (rc != 0) {
91 THREAD_LOG("pthread_mutex_init failed (errno %d)", rc);
92 }
93 return status_from_errno(rc);
94}
95
98 int rc = pthread_mutex_lock(&m->handle);
99 if (rc != 0) {
100 THREAD_LOG("pthread_mutex_lock failed (errno %d)", rc);
101 }
102 return status_from_errno(rc);
103}
104
106 PANIC_IF_NULL(m);
107 int rc = pthread_mutex_unlock(&m->handle);
108 if (rc != 0) {
109 THREAD_LOG("pthread_mutex_unlock failed (errno %d)", rc);
110 }
111 return status_from_errno(rc);
112}
113
115 PANIC_IF_NULL(m);
116 int rc = pthread_mutex_destroy(&m->handle);
117 if (rc != 0) {
118 THREAD_LOG("pthread_mutex_destroy failed (errno %d)", rc);
119 }
120 return status_from_errno(rc);
121}
122
124 PANIC_IF_NULL(c);
125 int rc = pthread_cond_init(&c->handle, nullptr);
126 if (rc != 0) {
127 THREAD_LOG("pthread_cond_init failed (errno %d)", rc);
128 }
129 return status_from_errno(rc);
130}
131
133 PANIC_IF_NULL(c);
134 PANIC_IF_NULL(m);
135 int rc = pthread_cond_wait(&c->handle, &m->handle);
136 if (rc != 0) {
137 THREAD_LOG("pthread_cond_wait failed (errno %d)", rc);
138 }
139 return status_from_errno(rc);
140}
141
143 PANIC_IF_NULL(c);
144 int rc = pthread_cond_signal(&c->handle);
145 if (rc != 0) {
146 THREAD_LOG("pthread_cond_signal failed (errno %d)", rc);
147 }
148 return status_from_errno(rc);
149}
150
152 PANIC_IF_NULL(c);
153 int rc = pthread_cond_broadcast(&c->handle);
154 if (rc != 0) {
155 THREAD_LOG("pthread_cond_broadcast failed (errno %d)", rc);
156 }
157 return status_from_errno(rc);
158}
159
161 PANIC_IF_NULL(c);
162 int rc = pthread_cond_destroy(&c->handle);
163 if (rc != 0) {
164 THREAD_LOG("pthread_cond_destroy failed (errno %d)", rc);
165 }
166 return status_from_errno(rc);
167}
pthread_cond_t handle
Definition thread.h:55
pthread_mutex_t handle
Definition thread.h:51
void * arg
Definition thread.h:47
pthread_t handle
Definition thread.h:41
void(* fn)(void *)
The function the thread runs and its argument, recorded by srn_thread_spawn and read once by the back...
Definition thread.h:46
srn_thread_t, srn_mutex_t, and srn_cond_t model the thread-level operations the runtime needs,...
srn_thread_status_t
Result of a thread operation.
Definition thread.h:61
@ SRN_THREAD_AGAIN
The system lacked the resources to start the thread, such as a per-process thread limit.
Definition thread.h:65
@ SRN_THREAD_OK
Definition thread.h:62
@ SRN_THREAD_ERROR
A failure the backend could not map to the above.
Definition thread.h:71
@ SRN_THREAD_PERM
The caller lacks permission for the requested operation.
Definition thread.h:69
@ SRN_THREAD_NOMEM
Out of memory.
Definition thread.h:67
srn_thread_status_t srn_mutex_destroy(srn_mutex_t *m)
Release a mutex's resources.
#define THREAD_LOG(FMT,...)
srn_thread_status_t srn_mutex_init(srn_mutex_t *m)
srn_thread_status_t srn_cond_destroy(srn_cond_t *c)
Release a condition's resources.
static void * posix_trampoline(void *arg)
pthreads run a thread through a void *(*)(void *) routine, while the modeled operation runs a void (*...
srn_thread_status_t srn_thread_join(srn_thread_t *t)
Block until the thread started for t returns.
srn_thread_status_t srn_mutex_unlock(srn_mutex_t *m)
srn_thread_status_t srn_cond_wait(srn_cond_t *c, srn_mutex_t *m)
Release m, sleep until notified, then re-acquire m before returning.
srn_thread_status_t srn_mutex_lock(srn_mutex_t *m)
srn_thread_status_t srn_cond_init(srn_cond_t *c)
srn_thread_status_t srn_cond_notify_one(srn_cond_t *c)
Wake one waiter.
static srn_thread_status_t status_from_errno(int rc)
Map a pthreads return code onto the neutral status.
srn_thread_status_t srn_cond_notify_all(srn_cond_t *c)
Wake every waiter.
srn_thread_status_t srn_thread_spawn(srn_thread_t *t, void(*fn)(void *), void *arg)
Run fn(arg) on a new OS thread.
#define PANIC_IF_NULL(ptr)
Definition utils.h:64