A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Thomas Martitz
11 *
12 * Generic unix threading support
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24#include <stdlib.h>
25#include <stdbool.h>
26#include <signal.h>
27#include <stdio.h>
28#include <setjmp.h>
29#include <unistd.h>
30#include <pthread.h>
31#include <errno.h>
32#include "debug.h"
33
34static volatile bool sig_handler_called;
35static volatile jmp_buf tramp_buf;
36static volatile jmp_buf bootstrap_buf;
37static void (*thread_func)(void);
38static const int trampoline_sig = SIGUSR1;
39static pthread_t main_thread;
40
41static struct ctx {
42 jmp_buf thread_buf;
43} thread_bufs[MAXTHREADS];
44static threadbit_t free_thread_bufs;
45static struct ctx* thread_context, *target_context;
46
47static void trampoline(int sig);
48static void bootstrap_context(void) __attribute__((noinline));
49
50static void init_thread_bufs(void)
51{
52 for (unsigned int bufidx = 0; bufidx < MAXTHREADS; bufidx++)
53 threadbit_set_bit(&free_thread_bufs, bufidx);
54}
55
56static struct ctx * alloc_thread_buf(void)
57{
58 unsigned int bufidx = threadbit_ffs(&free_thread_bufs);
59 threadbit_clear_bit(&free_thread_bufs, bufidx);
60 return &thread_bufs[bufidx];
61}
62
63static void free_thread_buf(struct ctx *threadbuf)
64{
65 unsigned int bufidx = threadbuf - thread_bufs;
66 threadbit_set_bit(&free_thread_bufs, bufidx);
67}
68
69/* The *_context functions are heavily based on Gnu pth
70 * http://www.gnu.org/software/pth/
71 *
72 * adjusted to work in a multi-thread environment to
73 * offer a ucontext-like API
74 */
75
76/*
77 * VARIANT 2: THE SIGNAL STACK TRICK
78 *
79 * This uses sigstack/sigaltstack() and friends and is really the
80 * most tricky part of Pth. When you understand the following
81 * stuff you're a good Unix hacker and then you've already
82 * understood the gory ingredients of Pth. So, either welcome to
83 * the club of hackers, or do yourself a favor and skip this ;)
84 *
85 * The ingenious fact is that this variant runs really on _all_ POSIX
86 * compliant systems without special platform kludges. But be _VERY_
87 * carefully when you change something in the following code. The slightest
88 * change or reordering can lead to horribly broken code. Really every
89 * function call in the following case is intended to be how it is, doubt
90 * me...
91 *
92 * For more details we strongly recommend you to read the companion
93 * paper ``Portable Multithreading -- The Signal Stack Trick for
94 * User-Space Thread Creation'' from Ralf S. Engelschall. A copy of the
95 * draft of this paper you can find in the file rse-pmt.ps inside the
96 * GNU Pth distribution.
97 */
98
99static int make_context(struct ctx *ctx, void (*f)(void), char *sp, size_t stack_size)
100{
101 struct sigaction sa;
102 struct sigaction osa;
103 stack_t ss;
104 stack_t oss;
105 sigset_t osigs;
106 sigset_t sigs;
107
108 disable_irq();
109 /*
110 * Preserve the trampoline_sig signal state, block trampoline_sig,
111 * and establish our signal handler. The signal will
112 * later transfer control onto the signal stack.
113 */
114 sigemptyset(&sigs);
115 sigaddset(&sigs, trampoline_sig);
116 sigprocmask(SIG_BLOCK, &sigs, &osigs);
117 sa.sa_handler = trampoline;
118 sigemptyset(&sa.sa_mask);
119 sa.sa_flags = SA_ONSTACK;
120 if (sigaction(trampoline_sig, &sa, &osa) != 0)
121 {
122 DEBUGF("%s(): %s\n", __func__, strerror(errno));
123 return false;
124 }
125 /*
126 * Set the new stack.
127 *
128 * For sigaltstack we're lucky [from sigaltstack(2) on
129 * FreeBSD 3.1]: ``Signal stacks are automatically adjusted
130 * for the direction of stack growth and alignment
131 * requirements''
132 *
133 * For sigstack we have to decide ourself [from sigstack(2)
134 * on Solaris 2.6]: ``The direction of stack growth is not
135 * indicated in the historical definition of struct sigstack.
136 * The only way to portably establish a stack pointer is for
137 * the application to determine stack growth direction.''
138 */
139 ss.ss_sp = sp;
140 ss.ss_size = stack_size;
141 ss.ss_flags = 0;
142 if (sigaltstack(&ss, &oss) < 0)
143 {
144 DEBUGF("%s(): %s\n", __func__, strerror(errno));
145 return false;
146 }
147
148 /*
149 * Now transfer control onto the signal stack and set it up.
150 * It will return immediately via "return" after the setjmp()
151 * was performed. Be careful here with race conditions. The
152 * signal can be delivered the first time sigsuspend() is
153 * called.
154 */
155 sig_handler_called = false;
156 main_thread = pthread_self();
157 sigfillset(&sigs);
158 sigdelset(&sigs, trampoline_sig);
159 pthread_kill(main_thread, trampoline_sig);
160 while(!sig_handler_called)
161 sigsuspend(&sigs);
162
163 /*
164 * Inform the system that we are back off the signal stack by
165 * removing the alternative signal stack. Be careful here: It
166 * first has to be disabled, before it can be removed.
167 */
168 sigaltstack(NULL, &ss);
169 ss.ss_flags = SS_DISABLE;
170 if (sigaltstack(&ss, NULL) < 0)
171 {
172 DEBUGF("%s(): %s\n", __func__, strerror(errno));
173 return false;
174 }
175 sigaltstack(NULL, &ss);
176 if (!(ss.ss_flags & SS_DISABLE))
177 {
178 DEBUGF("%s(): %s\n", __func__, strerror(errno));
179 return false;
180 }
181 if (!(oss.ss_flags & SS_DISABLE))
182 sigaltstack(&oss, NULL);
183
184 /*
185 * Restore the old trampoline_sig signal handler and mask
186 */
187 sigaction(trampoline_sig, &osa, NULL);
188 sigprocmask(SIG_SETMASK, &osigs, NULL);
189
190 /*
191 * Tell the trampoline and bootstrap function where to dump
192 * the new machine context, and what to do afterwards...
193 */
194 thread_func = f;
195 thread_context = ctx;
196
197 /*
198 * Now enter the trampoline again, but this time not as a signal
199 * handler. Instead we jump into it directly. The functionally
200 * redundant ping-pong pointer arithmentic is neccessary to avoid
201 * type-conversion warnings related to the `volatile' qualifier and
202 * the fact that `jmp_buf' usually is an array type.
203 */
204 if (setjmp(*((jmp_buf *)&bootstrap_buf)) == 0)
205 longjmp(*((jmp_buf *)&tramp_buf), 1);
206
207 /*
208 * Ok, we returned again, so now we're finished
209 */
210 enable_irq();
211 return true;
212}
213
214static void trampoline(int sig)
215{
216 (void)sig;
217 /* sanity check, no other thread should be here */
218 if (pthread_self() != main_thread)
219 return;
220
221 if (setjmp(*((jmp_buf *)&tramp_buf)) == 0)
222 {
223 sig_handler_called = true;
224 return;
225 }
226 /* longjump'd back in */
227 bootstrap_context();
228}
229
230void bootstrap_context(void)
231{
232 /* copy to local storage so we can spawn further threads
233 * in the meantime */
234 void (*thread_entry)(void) = thread_func;
235 struct ctx *t = thread_context;
236
237 /*
238 * Save current machine state (on new stack) and
239 * go back to caller until we're scheduled for real...
240 */
241 if (setjmp(t->thread_buf) == 0)
242 longjmp(*((jmp_buf *)&bootstrap_buf), 1);
243
244 /*
245 * The new thread is now running: GREAT!
246 * Now we just invoke its init function....
247 */
248 thread_entry();
249 DEBUGF("thread left\n");
250 free_thread_buf(t);
251 thread_exit();
252}
253
254static inline void set_context(struct ctx *c)
255{
256 longjmp(c->thread_buf, 1);
257}
258
259static inline void swap_context(struct ctx *old, struct ctx *new)
260{
261 if (!old || setjmp(old->thread_buf) == 0)
262 longjmp(new->thread_buf, 1);
263}
264
265static inline void get_context(struct ctx *c)
266{
267 setjmp(c->thread_buf);
268}
269
270
271static void setup_thread(struct regs *context);
272
273#define INIT_MAIN_THREAD
274static void init_main_thread(void *addr)
275{
276 /* get a context for the main thread so that we can jump to it from
277 * other threads */
278 struct regs *context = (struct regs*)addr;
279 init_thread_bufs();
280 context->uc = alloc_thread_buf();
281 get_context(context->uc);
282}
283
284#define THREAD_STARTUP_INIT(core, thread, function) \
285 ({ (thread)->context.stack_size = (thread)->stack_size, \
286 (thread)->context.stack = (uintptr_t)(thread)->stack; \
287 (thread)->context.start = function; })
288
289
290
291/*
292 * Prepare context to make the thread runnable by calling swapcontext on it
293 */
294static void setup_thread(struct regs *context)
295{
296 void (*fn)(void) = context->start;
297 context->uc = alloc_thread_buf();
298 while (!make_context(context->uc, fn, (char*)context->stack, context->stack_size))
299 DEBUGF("Thread creation failed. Retrying");
300}
301
302
303/*
304 * Save the ucontext_t pointer for later use in swapcontext()
305 *
306 * Cannot do getcontext() here, because jumping back to the context
307 * resumes after the getcontext call (i.e. store_context), but we need
308 * to resume from load_context()
309 */
310static inline void store_context(void* addr)
311{
312 struct regs *r = (struct regs*)addr;
313 target_context = r->uc;
314}
315
316/*
317 * Perform context switch
318 */
319static inline void load_context(const void* addr)
320{
321 struct regs *r = (struct regs*)addr;
322 if (UNLIKELY(r->start))
323 {
324 setup_thread(r);
325 r->start = NULL;
326 }
327 swap_context(target_context, r->uc);
328 target_context = NULL;
329}