A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 329 lines 9.8 kB view raw
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}