add a custom rule for building librbspeex prior to building rbutil.
[Rockbox.git] / uisimulator / sdl / thread-sdl.c
blobb8297072f27ff1a0161f2dcafecb908e0d8191b9
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Dan Everton
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdbool.h>
21 #include <time.h>
22 #include <SDL.h>
23 #include <SDL_thread.h>
24 #include <stdlib.h>
25 #include <memory.h>
26 #include <setjmp.h>
27 #include "system-sdl.h"
28 #include "thread-sdl.h"
29 #include "kernel.h"
30 #include "thread.h"
31 #include "debug.h"
33 /* Define this as 1 to show informational messages that are not errors. */
34 #define THREAD_SDL_DEBUGF_ENABLED 0
36 #if THREAD_SDL_DEBUGF_ENABLED
37 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
38 static char __name[32];
39 #define THREAD_SDL_GET_NAME(thread) \
40 ({ thread_get_name(__name, sizeof(__name)/sizeof(__name[0]), thread); __name; })
41 #else
42 #define THREAD_SDL_DEBUGF(...)
43 #define THREAD_SDL_GET_NAME(thread)
44 #endif
46 #define THREAD_PANICF(str...) \
47 ({ fprintf(stderr, str); exit(-1); })
49 /* Thread/core entries as in rockbox core */
50 struct core_entry cores[NUM_CORES];
51 struct thread_entry threads[MAXTHREADS];
52 /* Jump buffers for graceful exit - kernel threads don't stay neatly
53 * in their start routines responding to messages so this is the only
54 * way to get them back in there so they may exit */
55 static jmp_buf thread_jmpbufs[MAXTHREADS];
56 static SDL_mutex *m;
57 static struct thread_entry *running;
58 static bool threads_exit = false;
60 extern long start_tick;
62 void thread_sdl_shutdown(void)
64 int i;
65 /* Take control */
66 SDL_LockMutex(m);
68 /* Tell all threads jump back to their start routines, unlock and exit
69 gracefully - we'll check each one in turn for it's status. Threads
70 _could_ terminate via remove_thread or multiple threads could exit
71 on each unlock but that is safe. */
72 threads_exit = true;
74 for (i = 0; i < MAXTHREADS; i++)
76 struct thread_entry *thread = &threads[i];
77 if (thread->context.t != NULL)
79 /* Signal thread on delay or block */
80 SDL_Thread *t = thread->context.t;
81 SDL_CondSignal(thread->context.c);
82 SDL_UnlockMutex(m);
83 /* Wait for it to finish */
84 SDL_WaitThread(t, NULL);
85 /* Relock for next thread signal */
86 SDL_LockMutex(m);
90 SDL_UnlockMutex(m);
91 SDL_DestroyMutex(m);
94 /* Do main thread creation in this file scope to avoid the need to double-
95 return to a prior call-level which would be unaware of the fact setjmp
96 was used */
97 extern void app_main(void *param);
98 static int thread_sdl_app_main(void *param)
100 SDL_LockMutex(m);
101 running = &threads[0];
103 /* Set the jump address for return */
104 if (setjmp(thread_jmpbufs[0]) == 0)
106 app_main(param);
107 /* should not ever be reached but... */
108 THREAD_PANICF("app_main returned!\n");
111 /* Unlock and exit */
112 SDL_UnlockMutex(m);
113 return 0;
116 /* Initialize SDL threading */
117 bool thread_sdl_init(void *param)
119 memset(threads, 0, sizeof(threads));
121 m = SDL_CreateMutex();
123 if (SDL_LockMutex(m) == -1)
125 fprintf(stderr, "Couldn't lock mutex\n");
126 return false;
129 /* Slot 0 is reserved for the main thread - initialize it here and
130 then create the SDL thread - it is possible to have a quick, early
131 shutdown try to access the structure. */
132 running = &threads[0];
133 running->stack = " ";
134 running->stack_size = 8;
135 running->name = "main";
136 running->state = STATE_RUNNING;
137 running->context.c = SDL_CreateCond();
138 cores[CURRENT_CORE].irq_level = STAY_IRQ_LEVEL;
140 if (running->context.c == NULL)
142 fprintf(stderr, "Failed to create main condition variable\n");
143 return false;
146 running->context.t = SDL_CreateThread(thread_sdl_app_main, param);
148 if (running->context.t == NULL)
150 fprintf(stderr, "Failed to create main thread\n");
151 return false;
154 THREAD_SDL_DEBUGF("Main thread: %p\n", running);
156 SDL_UnlockMutex(m);
157 return true;
160 /* A way to yield and leave the threading system for extended periods */
161 void thread_sdl_thread_lock(void *me)
163 SDL_LockMutex(m);
164 running = (struct thread_entry *)me;
166 if (threads_exit)
167 remove_thread(NULL);
170 void * thread_sdl_thread_unlock(void)
172 struct thread_entry *current = running;
173 SDL_UnlockMutex(m);
174 return current;
177 static int find_empty_thread_slot(void)
179 int n;
181 for (n = 0; n < MAXTHREADS; n++)
183 int state = threads[n].state;
185 if (state == STATE_KILLED)
186 break;
189 return n;
192 static void add_to_list_l(struct thread_entry **list,
193 struct thread_entry *thread)
195 if (*list == NULL)
197 /* Insert into unoccupied list */
198 thread->l.next = thread;
199 thread->l.prev = thread;
200 *list = thread;
202 else
204 /* Insert last */
205 thread->l.next = *list;
206 thread->l.prev = (*list)->l.prev;
207 thread->l.prev->l.next = thread;
208 (*list)->l.prev = thread;
212 static void remove_from_list_l(struct thread_entry **list,
213 struct thread_entry *thread)
215 if (thread == thread->l.next)
217 /* The only item */
218 *list = NULL;
219 return;
222 if (thread == *list)
224 /* List becomes next item */
225 *list = thread->l.next;
228 /* Fix links to jump over the removed entry. */
229 thread->l.prev->l.next = thread->l.next;
230 thread->l.next->l.prev = thread->l.prev;
233 static void run_blocking_ops(void)
235 int level = cores[CURRENT_CORE].irq_level;
237 if (level != STAY_IRQ_LEVEL)
239 cores[CURRENT_CORE].irq_level = STAY_IRQ_LEVEL;
240 set_irq_level(level);
244 struct thread_entry *thread_get_current(void)
246 return running;
249 void switch_thread(struct thread_entry *old)
251 struct thread_entry *current = running;
253 SDL_UnlockMutex(m);
254 /* Any other thread waiting already will get it first */
255 SDL_LockMutex(m);
256 running = current;
258 if (threads_exit)
259 remove_thread(NULL);
261 (void)old;
264 void sleep_thread(int ticks)
266 struct thread_entry *current;
267 int rem;
269 current = running;
270 current->state = STATE_SLEEPING;
272 rem = (SDL_GetTicks() - start_tick) % (1000/HZ);
273 if (rem < 0)
274 rem = 0;
276 rem = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
278 if (rem == 0)
280 /* Unlock and give up rest of quantum */
281 SDL_UnlockMutex(m);
282 SDL_Delay(0);
283 SDL_LockMutex(m);
285 else
287 /* These sleeps must be signalable for thread exit */
288 SDL_CondWaitTimeout(current->context.c, m, rem);
291 running = current;
293 current->state = STATE_RUNNING;
295 if (threads_exit)
296 remove_thread(NULL);
299 int runthread(void *data)
301 struct thread_entry *current;
302 jmp_buf *current_jmpbuf;
304 /* Cannot access thread variables before locking the mutex as the
305 data structures may not be filled-in yet. */
306 SDL_LockMutex(m);
307 running = (struct thread_entry *)data;
308 current = running;
309 current_jmpbuf = &thread_jmpbufs[running - threads];
311 /* Setup jump for exit */
312 if (setjmp(*current_jmpbuf) == 0)
314 /* Run the thread routine */
315 if (current->state == STATE_FROZEN)
317 SDL_CondWait(current->context.c, m);
318 running = current;
322 if (!threads_exit)
324 current->context.start();
325 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
326 current - threads, THREAD_SDL_GET_NAME(current));
327 /* Thread routine returned - suicide */
330 remove_thread(NULL);
332 else
334 /* Unlock and exit */
335 SDL_UnlockMutex(m);
338 return 0;
341 struct thread_entry*
342 create_thread(void (*function)(void), void* stack, int stack_size,
343 unsigned flags, const char *name)
345 /** Avoid compiler warnings */
346 SDL_Thread* t;
347 SDL_cond *cond;
348 int slot;
350 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
352 slot = find_empty_thread_slot();
353 if (slot >= MAXTHREADS)
355 DEBUGF("Failed to find thread slot\n");
356 return NULL;
359 cond = SDL_CreateCond();
360 if (cond == NULL)
362 DEBUGF("Failed to create condition variable\n");
363 return NULL;
366 t = SDL_CreateThread(runthread, &threads[slot]);
367 if (t == NULL)
369 DEBUGF("Failed to create SDL thread\n");
370 SDL_DestroyCond(cond);
371 return NULL;
374 threads[slot].stack = stack;
375 threads[slot].stack_size = stack_size;
376 threads[slot].name = name;
377 threads[slot].state = (flags & CREATE_THREAD_FROZEN) ?
378 STATE_FROZEN : STATE_RUNNING;
379 threads[slot].context.start = function;
380 threads[slot].context.t = t;
381 threads[slot].context.c = cond;
383 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
384 slot, THREAD_SDL_GET_NAME(&threads[slot]));
386 return &threads[slot];
389 void _block_thread(struct thread_queue *tq)
391 struct thread_entry *thread = running;
393 thread->state = STATE_BLOCKED;
394 thread->bqp = tq;
395 add_to_list_l(&tq->queue, thread);
397 run_blocking_ops();
399 SDL_CondWait(thread->context.c, m);
400 running = thread;
402 if (threads_exit)
403 remove_thread(NULL);
406 void block_thread_w_tmo(struct thread_queue *tq, int ticks)
408 struct thread_entry *thread = running;
410 thread->state = STATE_BLOCKED_W_TMO;
411 thread->bqp = tq;
412 add_to_list_l(&tq->queue, thread);
414 run_blocking_ops();
416 SDL_CondWaitTimeout(thread->context.c, m, (1000/HZ) * ticks);
417 running = thread;
419 if (thread->state == STATE_BLOCKED_W_TMO)
421 /* Timed out */
422 remove_from_list_l(&tq->queue, thread);
423 thread->state = STATE_RUNNING;
426 if (threads_exit)
427 remove_thread(NULL);
430 struct thread_entry * _wakeup_thread(struct thread_queue *tq)
432 struct thread_entry *thread = tq->queue;
434 if (thread == NULL)
436 return NULL;
439 switch (thread->state)
441 case STATE_BLOCKED:
442 case STATE_BLOCKED_W_TMO:
443 remove_from_list_l(&tq->queue, thread);
444 thread->state = STATE_RUNNING;
445 SDL_CondSignal(thread->context.c);
446 return thread;
447 default:
448 return NULL;
452 void thread_thaw(struct thread_entry *thread)
454 if (thread->state == STATE_FROZEN)
456 thread->state = STATE_RUNNING;
457 SDL_CondSignal(thread->context.c);
461 void init_threads(void)
463 /* Main thread is already initialized */
464 if (running != &threads[0])
466 THREAD_PANICF("Wrong main thread in init_threads: %p\n", running);
469 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
470 0, THREAD_SDL_GET_NAME(&threads[0]));
473 void remove_thread(struct thread_entry *thread)
475 struct thread_entry *current = running;
476 SDL_Thread *t;
477 SDL_cond *c;
479 int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
481 if (thread == NULL)
483 thread = current;
486 t = thread->context.t;
487 c = thread->context.c;
488 thread->context.t = NULL;
490 if (thread != current)
492 switch (thread->state)
494 case STATE_BLOCKED:
495 case STATE_BLOCKED_W_TMO:
496 /* Remove thread from object it's waiting on */
497 remove_from_list_l(&thread->bqp->queue, thread);
498 break;
501 SDL_CondSignal(c);
504 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
505 thread - threads, THREAD_SDL_GET_NAME(thread));
507 thread_queue_wake_no_listlock(&thread->queue);
508 thread->state = STATE_KILLED;
510 SDL_DestroyCond(c);
512 if (thread == current)
514 /* Do a graceful exit - perform the longjmp back into the thread
515 function to return */
516 set_irq_level(oldlevel);
517 longjmp(thread_jmpbufs[current - threads], 1);
520 SDL_KillThread(t);
521 set_irq_level(oldlevel);
524 void thread_wait(struct thread_entry *thread)
526 if (thread == NULL)
527 thread = running;
529 if (thread->state != STATE_KILLED)
531 block_thread_no_listlock(&thread->queue);
535 int thread_stack_usage(const struct thread_entry *thread)
537 return 50;
538 (void)thread;
541 unsigned thread_get_status(const struct thread_entry *thread)
543 return thread->state;
546 /* Return name if one or ID if none */
547 void thread_get_name(char *buffer, int size,
548 struct thread_entry *thread)
550 if (size <= 0)
551 return;
553 *buffer = '\0';
555 if (thread)
557 /* Display thread name if one or ID if none */
558 bool named = thread->name && *thread->name;
559 const char *fmt = named ? "%s" : "%08lX";
560 intptr_t name = named ?
561 (intptr_t)thread->name : (intptr_t)thread;
562 snprintf(buffer, size, fmt, name);