Make it compile again on linux...
[kugel-rb.git] / uisimulator / sdl / thread-sdl.c
blob905a017ee0cbe6e182776945103899e0d754ee7a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Dan Everton
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdbool.h>
23 #include <time.h>
24 #include <SDL.h>
25 #include <SDL_thread.h>
26 #include <stdlib.h>
27 #include <memory.h>
28 #include <setjmp.h>
29 #include "system-sdl.h"
30 #include "thread-sdl.h"
31 #include "system.h"
32 #include "kernel.h"
33 #include "thread.h"
34 #include "debug.h"
36 /* Define this as 1 to show informational messages that are not errors. */
37 #define THREAD_SDL_DEBUGF_ENABLED 0
39 #if THREAD_SDL_DEBUGF_ENABLED
40 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
41 static char __name[32];
42 #define THREAD_SDL_GET_NAME(thread) \
43 ({ thread_get_name(__name, ARRAYLEN(__name), thread); __name; })
44 #else
45 #define THREAD_SDL_DEBUGF(...)
46 #define THREAD_SDL_GET_NAME(thread)
47 #endif
49 #define THREAD_PANICF(str...) \
50 ({ fprintf(stderr, str); exit(-1); })
52 /* Thread/core entries as in rockbox core */
53 struct core_entry cores[NUM_CORES];
54 struct thread_entry threads[MAXTHREADS];
55 /* Jump buffers for graceful exit - kernel threads don't stay neatly
56 * in their start routines responding to messages so this is the only
57 * way to get them back in there so they may exit */
58 static jmp_buf thread_jmpbufs[MAXTHREADS];
59 static SDL_mutex *m;
60 static bool threads_exit = false;
62 extern long start_tick;
64 void thread_sdl_shutdown(void)
66 int i;
67 /* Take control */
68 SDL_LockMutex(m);
70 /* Tell all threads jump back to their start routines, unlock and exit
71 gracefully - we'll check each one in turn for it's status. Threads
72 _could_ terminate via remove_thread or multiple threads could exit
73 on each unlock but that is safe. */
74 threads_exit = true;
76 for (i = 0; i < MAXTHREADS; i++)
78 struct thread_entry *thread = &threads[i];
79 if (thread->context.t != NULL)
81 /* Signal thread on delay or block */
82 SDL_Thread *t = thread->context.t;
83 SDL_SemPost(thread->context.s);
84 SDL_UnlockMutex(m);
85 /* Wait for it to finish */
86 SDL_WaitThread(t, NULL);
87 /* Relock for next thread signal */
88 SDL_LockMutex(m);
92 SDL_UnlockMutex(m);
93 SDL_DestroyMutex(m);
96 /* Do main thread creation in this file scope to avoid the need to double-
97 return to a prior call-level which would be unaware of the fact setjmp
98 was used */
99 extern void app_main(void *param);
100 static int thread_sdl_app_main(void *param)
102 SDL_LockMutex(m);
103 cores[CURRENT_CORE].running = &threads[0];
105 /* Set the jump address for return */
106 if (setjmp(thread_jmpbufs[0]) == 0)
108 app_main(param);
109 /* should not ever be reached but... */
110 THREAD_PANICF("app_main returned!\n");
113 /* Unlock and exit */
114 SDL_UnlockMutex(m);
115 return 0;
118 /* Initialize SDL threading */
119 bool thread_sdl_init(void *param)
121 struct thread_entry *thread;
122 memset(cores, 0, sizeof(cores));
123 memset(threads, 0, sizeof(threads));
125 m = SDL_CreateMutex();
127 if (SDL_LockMutex(m) == -1)
129 fprintf(stderr, "Couldn't lock mutex\n");
130 return false;
133 /* Slot 0 is reserved for the main thread - initialize it here and
134 then create the SDL thread - it is possible to have a quick, early
135 shutdown try to access the structure. */
136 thread = &threads[0];
137 thread->stack = (uintptr_t *)" ";
138 thread->stack_size = 8;
139 thread->name = "main";
140 thread->state = STATE_RUNNING;
141 thread->context.s = SDL_CreateSemaphore(0);
142 cores[CURRENT_CORE].running = thread;
144 if (thread->context.s == NULL)
146 fprintf(stderr, "Failed to create main semaphore\n");
147 return false;
150 thread->context.t = SDL_CreateThread(thread_sdl_app_main, param);
152 if (thread->context.t == NULL)
154 SDL_DestroySemaphore(thread->context.s);
155 fprintf(stderr, "Failed to create main thread\n");
156 return false;
159 THREAD_SDL_DEBUGF("Main thread: %p\n", thread);
161 SDL_UnlockMutex(m);
162 return true;
165 /* A way to yield and leave the threading system for extended periods */
166 void thread_sdl_thread_lock(void *me)
168 SDL_LockMutex(m);
169 cores[CURRENT_CORE].running = (struct thread_entry *)me;
171 if (threads_exit)
172 thread_exit();
175 void * thread_sdl_thread_unlock(void)
177 struct thread_entry *current = cores[CURRENT_CORE].running;
178 SDL_UnlockMutex(m);
179 return current;
182 static struct thread_entry * find_empty_thread_slot(void)
184 struct thread_entry *thread = NULL;
185 int n;
187 for (n = 0; n < MAXTHREADS; n++)
189 int state = threads[n].state;
191 if (state == STATE_KILLED)
193 thread = &threads[n];
194 break;
198 return thread;
201 static void add_to_list_l(struct thread_entry **list,
202 struct thread_entry *thread)
204 if (*list == NULL)
206 /* Insert into unoccupied list */
207 thread->l.next = thread;
208 thread->l.prev = thread;
209 *list = thread;
211 else
213 /* Insert last */
214 thread->l.next = *list;
215 thread->l.prev = (*list)->l.prev;
216 thread->l.prev->l.next = thread;
217 (*list)->l.prev = thread;
221 static void remove_from_list_l(struct thread_entry **list,
222 struct thread_entry *thread)
224 if (thread == thread->l.next)
226 /* The only item */
227 *list = NULL;
228 return;
231 if (thread == *list)
233 /* List becomes next item */
234 *list = thread->l.next;
237 /* Fix links to jump over the removed entry. */
238 thread->l.prev->l.next = thread->l.next;
239 thread->l.next->l.prev = thread->l.prev;
242 struct thread_entry *thread_get_current(void)
244 return cores[CURRENT_CORE].running;
247 void switch_thread(void)
249 struct thread_entry *current = cores[CURRENT_CORE].running;
251 enable_irq();
253 switch (current->state)
255 case STATE_RUNNING:
257 SDL_UnlockMutex(m);
258 /* Any other thread waiting already will get it first */
259 SDL_LockMutex(m);
260 break;
261 } /* STATE_RUNNING: */
263 case STATE_BLOCKED:
265 int oldlevel;
267 SDL_UnlockMutex(m);
268 SDL_SemWait(current->context.s);
269 SDL_LockMutex(m);
271 oldlevel = disable_irq_save();
272 current->state = STATE_RUNNING;
273 restore_irq(oldlevel);
274 break;
275 } /* STATE_BLOCKED: */
277 case STATE_BLOCKED_W_TMO:
279 int result, oldlevel;
281 SDL_UnlockMutex(m);
282 result = SDL_SemWaitTimeout(current->context.s, current->tmo_tick);
283 SDL_LockMutex(m);
285 oldlevel = disable_irq_save();
287 if (current->state == STATE_BLOCKED_W_TMO)
289 /* Timed out */
290 remove_from_list_l(current->bqp, current);
292 #ifdef HAVE_WAKEUP_EXT_CB
293 if (current->wakeup_ext_cb != NULL)
294 current->wakeup_ext_cb(current);
295 #endif
296 current->state = STATE_RUNNING;
299 if (result == SDL_MUTEX_TIMEDOUT)
301 /* Other signals from an explicit wake could have been made before
302 * arriving here if we timed out waiting for the semaphore. Make
303 * sure the count is reset. */
304 while (SDL_SemValue(current->context.s) > 0)
305 SDL_SemTryWait(current->context.s);
308 restore_irq(oldlevel);
309 break;
310 } /* STATE_BLOCKED_W_TMO: */
312 case STATE_SLEEPING:
314 SDL_UnlockMutex(m);
315 SDL_SemWaitTimeout(current->context.s, current->tmo_tick);
316 SDL_LockMutex(m);
317 current->state = STATE_RUNNING;
318 break;
319 } /* STATE_SLEEPING: */
322 cores[CURRENT_CORE].running = current;
324 if (threads_exit)
325 thread_exit();
328 void sleep_thread(int ticks)
330 struct thread_entry *current = cores[CURRENT_CORE].running;
331 int rem;
333 current->state = STATE_SLEEPING;
335 rem = (SDL_GetTicks() - start_tick) % (1000/HZ);
336 if (rem < 0)
337 rem = 0;
339 current->tmo_tick = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
342 void block_thread(struct thread_entry *current)
344 current->state = STATE_BLOCKED;
345 add_to_list_l(current->bqp, current);
348 void block_thread_w_tmo(struct thread_entry *current, int ticks)
350 current->state = STATE_BLOCKED_W_TMO;
351 current->tmo_tick = (1000/HZ)*ticks;
352 add_to_list_l(current->bqp, current);
355 unsigned int wakeup_thread(struct thread_entry **list)
357 struct thread_entry *thread = *list;
359 if (thread != NULL)
361 switch (thread->state)
363 case STATE_BLOCKED:
364 case STATE_BLOCKED_W_TMO:
365 remove_from_list_l(list, thread);
366 thread->state = STATE_RUNNING;
367 SDL_SemPost(thread->context.s);
368 return THREAD_OK;
372 return THREAD_NONE;
375 unsigned int thread_queue_wake(struct thread_entry **list)
377 unsigned int result = THREAD_NONE;
379 for (;;)
381 unsigned int rc = wakeup_thread(list);
383 if (rc == THREAD_NONE)
384 break;
386 result |= rc;
389 return result;
392 void thread_thaw(struct thread_entry *thread)
394 if (thread->state == STATE_FROZEN)
396 thread->state = STATE_RUNNING;
397 SDL_SemPost(thread->context.s);
401 int runthread(void *data)
403 struct thread_entry *current;
404 jmp_buf *current_jmpbuf;
406 /* Cannot access thread variables before locking the mutex as the
407 data structures may not be filled-in yet. */
408 SDL_LockMutex(m);
409 cores[CURRENT_CORE].running = (struct thread_entry *)data;
410 current = cores[CURRENT_CORE].running;
411 current_jmpbuf = &thread_jmpbufs[current - threads];
413 /* Setup jump for exit */
414 if (setjmp(*current_jmpbuf) == 0)
416 /* Run the thread routine */
417 if (current->state == STATE_FROZEN)
419 SDL_UnlockMutex(m);
420 SDL_SemWait(current->context.s);
421 SDL_LockMutex(m);
422 cores[CURRENT_CORE].running = current;
425 if (!threads_exit)
427 current->context.start();
428 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
429 current - threads, THREAD_SDL_GET_NAME(current));
430 /* Thread routine returned - suicide */
433 thread_exit();
435 else
437 /* Unlock and exit */
438 SDL_UnlockMutex(m);
441 return 0;
444 struct thread_entry*
445 create_thread(void (*function)(void), void* stack, size_t stack_size,
446 unsigned flags, const char *name)
448 struct thread_entry *thread;
449 SDL_Thread* t;
450 SDL_sem *s;
452 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
454 thread = find_empty_thread_slot();
455 if (thread == NULL)
457 DEBUGF("Failed to find thread slot\n");
458 return NULL;
461 s = SDL_CreateSemaphore(0);
462 if (s == NULL)
464 DEBUGF("Failed to create semaphore\n");
465 return NULL;
468 t = SDL_CreateThread(runthread, thread);
469 if (t == NULL)
471 DEBUGF("Failed to create SDL thread\n");
472 SDL_DestroySemaphore(s);
473 return NULL;
476 thread->stack = stack;
477 thread->stack_size = stack_size;
478 thread->name = name;
479 thread->state = (flags & CREATE_THREAD_FROZEN) ?
480 STATE_FROZEN : STATE_RUNNING;
481 thread->context.start = function;
482 thread->context.t = t;
483 thread->context.s = s;
485 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
486 thread - threads, THREAD_SDL_GET_NAME(thread));
488 return thread;
491 void init_threads(void)
493 /* Main thread is already initialized */
494 if (cores[CURRENT_CORE].running != &threads[0])
496 THREAD_PANICF("Wrong main thread in init_threads: %p\n",
497 cores[CURRENT_CORE].running);
500 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
501 0, THREAD_SDL_GET_NAME(&threads[0]));
504 void remove_thread(struct thread_entry *thread)
506 struct thread_entry *current = cores[CURRENT_CORE].running;
507 SDL_Thread *t;
508 SDL_sem *s;
510 int oldlevel = disable_irq_save();
512 if (thread == NULL)
514 thread = current;
517 t = thread->context.t;
518 s = thread->context.s;
519 thread->context.t = NULL;
521 if (thread != current)
523 switch (thread->state)
525 case STATE_BLOCKED:
526 case STATE_BLOCKED_W_TMO:
527 /* Remove thread from object it's waiting on */
528 remove_from_list_l(thread->bqp, thread);
530 #ifdef HAVE_WAKEUP_EXT_CB
531 if (thread->wakeup_ext_cb != NULL)
532 thread->wakeup_ext_cb(thread);
533 #endif
534 break;
537 SDL_SemPost(s);
540 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
541 thread - threads, THREAD_SDL_GET_NAME(thread));
543 thread->state = STATE_KILLED;
544 thread_queue_wake(&thread->queue);
546 SDL_DestroySemaphore(s);
548 if (thread == current)
550 /* Do a graceful exit - perform the longjmp back into the thread
551 function to return */
552 restore_irq(oldlevel);
553 longjmp(thread_jmpbufs[current - threads], 1);
556 SDL_KillThread(t);
557 restore_irq(oldlevel);
560 void thread_exit(void)
562 remove_thread(NULL);
565 void thread_wait(struct thread_entry *thread)
567 struct thread_entry *current = cores[CURRENT_CORE].running;
569 if (thread == NULL)
570 thread = current;
572 if (thread->state != STATE_KILLED)
574 current->bqp = &thread->queue;
575 block_thread(current);
576 switch_thread();
580 int thread_stack_usage(const struct thread_entry *thread)
582 return 50;
583 (void)thread;
586 unsigned thread_get_status(const struct thread_entry *thread)
588 return thread->state;
591 /* Return name if one or ID if none */
592 void thread_get_name(char *buffer, int size,
593 struct thread_entry *thread)
595 if (size <= 0)
596 return;
598 *buffer = '\0';
600 if (thread)
602 /* Display thread name if one or ID if none */
603 bool named = thread->name && *thread->name;
604 const char *fmt = named ? "%s" : "%08lX";
605 intptr_t name = named ?
606 (intptr_t)thread->name : (intptr_t)thread;
607 snprintf(buffer, size, fmt, name);