fix a comment typo
[Rockbox.git] / uisimulator / sdl / thread-sdl.c
blob5aae9a4bf801077b645b5362fc2e0e5bdca14208
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 "system.h"
30 #include "kernel.h"
31 #include "thread.h"
32 #include "debug.h"
34 /* Define this as 1 to show informational messages that are not errors. */
35 #define THREAD_SDL_DEBUGF_ENABLED 0
37 #if THREAD_SDL_DEBUGF_ENABLED
38 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
39 static char __name[32];
40 #define THREAD_SDL_GET_NAME(thread) \
41 ({ thread_get_name(__name, ARRAYLEN(__name), thread); __name; })
42 #else
43 #define THREAD_SDL_DEBUGF(...)
44 #define THREAD_SDL_GET_NAME(thread)
45 #endif
47 #define THREAD_PANICF(str...) \
48 ({ fprintf(stderr, str); exit(-1); })
50 /* Thread/core entries as in rockbox core */
51 struct core_entry cores[NUM_CORES];
52 struct thread_entry threads[MAXTHREADS];
53 /* Jump buffers for graceful exit - kernel threads don't stay neatly
54 * in their start routines responding to messages so this is the only
55 * way to get them back in there so they may exit */
56 static jmp_buf thread_jmpbufs[MAXTHREADS];
57 static SDL_mutex *m;
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_SemPost(thread->context.s);
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 cores[CURRENT_CORE].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 struct thread_entry *thread;
120 memset(cores, 0, sizeof(cores));
121 memset(threads, 0, sizeof(threads));
123 m = SDL_CreateMutex();
125 if (SDL_LockMutex(m) == -1)
127 fprintf(stderr, "Couldn't lock mutex\n");
128 return false;
131 /* Slot 0 is reserved for the main thread - initialize it here and
132 then create the SDL thread - it is possible to have a quick, early
133 shutdown try to access the structure. */
134 thread = &threads[0];
135 thread->stack = (uintptr_t *)" ";
136 thread->stack_size = 8;
137 thread->name = "main";
138 thread->state = STATE_RUNNING;
139 thread->context.s = SDL_CreateSemaphore(0);
140 cores[CURRENT_CORE].running = thread;
142 if (thread->context.s == NULL)
144 fprintf(stderr, "Failed to create main semaphore\n");
145 return false;
148 thread->context.t = SDL_CreateThread(thread_sdl_app_main, param);
150 if (thread->context.t == NULL)
152 SDL_DestroySemaphore(thread->context.s);
153 fprintf(stderr, "Failed to create main thread\n");
154 return false;
157 THREAD_SDL_DEBUGF("Main thread: %p\n", thread);
159 SDL_UnlockMutex(m);
160 return true;
163 /* A way to yield and leave the threading system for extended periods */
164 void thread_sdl_thread_lock(void *me)
166 SDL_LockMutex(m);
167 cores[CURRENT_CORE].running = (struct thread_entry *)me;
169 if (threads_exit)
170 thread_exit();
173 void * thread_sdl_thread_unlock(void)
175 struct thread_entry *current = cores[CURRENT_CORE].running;
176 SDL_UnlockMutex(m);
177 return current;
180 static struct thread_entry * find_empty_thread_slot(void)
182 struct thread_entry *thread = NULL;
183 int n;
185 for (n = 0; n < MAXTHREADS; n++)
187 int state = threads[n].state;
189 if (state == STATE_KILLED)
191 thread = &threads[n];
192 break;
196 return thread;
199 static void add_to_list_l(struct thread_entry **list,
200 struct thread_entry *thread)
202 if (*list == NULL)
204 /* Insert into unoccupied list */
205 thread->l.next = thread;
206 thread->l.prev = thread;
207 *list = thread;
209 else
211 /* Insert last */
212 thread->l.next = *list;
213 thread->l.prev = (*list)->l.prev;
214 thread->l.prev->l.next = thread;
215 (*list)->l.prev = thread;
219 static void remove_from_list_l(struct thread_entry **list,
220 struct thread_entry *thread)
222 if (thread == thread->l.next)
224 /* The only item */
225 *list = NULL;
226 return;
229 if (thread == *list)
231 /* List becomes next item */
232 *list = thread->l.next;
235 /* Fix links to jump over the removed entry. */
236 thread->l.prev->l.next = thread->l.next;
237 thread->l.next->l.prev = thread->l.prev;
240 struct thread_entry *thread_get_current(void)
242 return cores[CURRENT_CORE].running;
245 void switch_thread(void)
247 struct thread_entry *current = cores[CURRENT_CORE].running;
249 enable_irq();
251 switch (current->state)
253 case STATE_RUNNING:
255 SDL_UnlockMutex(m);
256 /* Any other thread waiting already will get it first */
257 SDL_LockMutex(m);
258 break;
259 } /* STATE_RUNNING: */
261 case STATE_BLOCKED:
263 int oldlevel;
265 SDL_UnlockMutex(m);
266 SDL_SemWait(current->context.s);
267 SDL_LockMutex(m);
269 oldlevel = disable_irq_save();
270 current->state = STATE_RUNNING;
271 restore_irq(oldlevel);
272 break;
273 } /* STATE_BLOCKED: */
275 case STATE_BLOCKED_W_TMO:
277 int result, oldlevel;
279 SDL_UnlockMutex(m);
280 result = SDL_SemWaitTimeout(current->context.s, current->tmo_tick);
281 SDL_LockMutex(m);
283 oldlevel = disable_irq_save();
285 if (current->state == STATE_BLOCKED_W_TMO)
287 /* Timed out */
288 remove_from_list_l(current->bqp, current);
290 #ifdef HAVE_WAKEUP_EXT_CB
291 if (current->wakeup_ext_cb != NULL)
292 current->wakeup_ext_cb(current);
293 #endif
294 current->state = STATE_RUNNING;
297 if (result == SDL_MUTEX_TIMEDOUT)
299 /* Other signals from an explicit wake could have been made before
300 * arriving here if we timed out waiting for the semaphore. Make
301 * sure the count is reset. */
302 while (SDL_SemValue(current->context.s) > 0)
303 SDL_SemTryWait(current->context.s);
306 restore_irq(oldlevel);
307 break;
308 } /* STATE_BLOCKED_W_TMO: */
310 case STATE_SLEEPING:
312 SDL_UnlockMutex(m);
313 SDL_SemWaitTimeout(current->context.s, current->tmo_tick);
314 SDL_LockMutex(m);
315 current->state = STATE_RUNNING;
316 break;
317 } /* STATE_SLEEPING: */
320 cores[CURRENT_CORE].running = current;
322 if (threads_exit)
323 thread_exit();
326 void sleep_thread(int ticks)
328 struct thread_entry *current = cores[CURRENT_CORE].running;
329 int rem;
331 current->state = STATE_SLEEPING;
333 rem = (SDL_GetTicks() - start_tick) % (1000/HZ);
334 if (rem < 0)
335 rem = 0;
337 current->tmo_tick = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
340 void block_thread(struct thread_entry *current)
342 current->state = STATE_BLOCKED;
343 add_to_list_l(current->bqp, current);
346 void block_thread_w_tmo(struct thread_entry *current, int ticks)
348 current->state = STATE_BLOCKED_W_TMO;
349 current->tmo_tick = (1000/HZ)*ticks;
350 add_to_list_l(current->bqp, current);
353 unsigned int wakeup_thread(struct thread_entry **list)
355 struct thread_entry *thread = *list;
357 if (thread != NULL)
359 switch (thread->state)
361 case STATE_BLOCKED:
362 case STATE_BLOCKED_W_TMO:
363 remove_from_list_l(list, thread);
364 thread->state = STATE_RUNNING;
365 SDL_SemPost(thread->context.s);
366 return THREAD_OK;
370 return THREAD_NONE;
373 unsigned int thread_queue_wake(struct thread_entry **list)
375 unsigned int result = THREAD_NONE;
377 for (;;)
379 unsigned int rc = wakeup_thread(list);
381 if (rc == THREAD_NONE)
382 break;
384 result |= rc;
387 return result;
390 void thread_thaw(struct thread_entry *thread)
392 if (thread->state == STATE_FROZEN)
394 thread->state = STATE_RUNNING;
395 SDL_SemPost(thread->context.s);
399 int runthread(void *data)
401 struct thread_entry *current;
402 jmp_buf *current_jmpbuf;
404 /* Cannot access thread variables before locking the mutex as the
405 data structures may not be filled-in yet. */
406 SDL_LockMutex(m);
407 cores[CURRENT_CORE].running = (struct thread_entry *)data;
408 current = cores[CURRENT_CORE].running;
409 current_jmpbuf = &thread_jmpbufs[current - threads];
411 /* Setup jump for exit */
412 if (setjmp(*current_jmpbuf) == 0)
414 /* Run the thread routine */
415 if (current->state == STATE_FROZEN)
417 SDL_UnlockMutex(m);
418 SDL_SemWait(current->context.s);
419 SDL_LockMutex(m);
420 cores[CURRENT_CORE].running = current;
423 if (!threads_exit)
425 current->context.start();
426 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
427 current - threads, THREAD_SDL_GET_NAME(current));
428 /* Thread routine returned - suicide */
431 thread_exit();
433 else
435 /* Unlock and exit */
436 SDL_UnlockMutex(m);
439 return 0;
442 struct thread_entry*
443 create_thread(void (*function)(void), void* stack, size_t stack_size,
444 unsigned flags, const char *name)
446 struct thread_entry *thread;
447 SDL_Thread* t;
448 SDL_sem *s;
450 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
452 thread = find_empty_thread_slot();
453 if (thread == NULL)
455 DEBUGF("Failed to find thread slot\n");
456 return NULL;
459 s = SDL_CreateSemaphore(0);
460 if (s == NULL)
462 DEBUGF("Failed to create semaphore\n");
463 return NULL;
466 t = SDL_CreateThread(runthread, thread);
467 if (t == NULL)
469 DEBUGF("Failed to create SDL thread\n");
470 SDL_DestroySemaphore(s);
471 return NULL;
474 thread->stack = stack;
475 thread->stack_size = stack_size;
476 thread->name = name;
477 thread->state = (flags & CREATE_THREAD_FROZEN) ?
478 STATE_FROZEN : STATE_RUNNING;
479 thread->context.start = function;
480 thread->context.t = t;
481 thread->context.s = s;
483 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
484 thread - threads, THREAD_SDL_GET_NAME(thread));
486 return thread;
489 void init_threads(void)
491 /* Main thread is already initialized */
492 if (cores[CURRENT_CORE].running != &threads[0])
494 THREAD_PANICF("Wrong main thread in init_threads: %p\n",
495 cores[CURRENT_CORE].running);
498 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
499 0, THREAD_SDL_GET_NAME(&threads[0]));
502 void remove_thread(struct thread_entry *thread)
504 struct thread_entry *current = cores[CURRENT_CORE].running;
505 SDL_Thread *t;
506 SDL_sem *s;
508 int oldlevel = disable_irq_save();
510 if (thread == NULL)
512 thread = current;
515 t = thread->context.t;
516 s = thread->context.s;
517 thread->context.t = NULL;
519 if (thread != current)
521 switch (thread->state)
523 case STATE_BLOCKED:
524 case STATE_BLOCKED_W_TMO:
525 /* Remove thread from object it's waiting on */
526 remove_from_list_l(thread->bqp, thread);
528 #ifdef HAVE_WAKEUP_EXT_CB
529 if (thread->wakeup_ext_cb != NULL)
530 thread->wakeup_ext_cb(thread);
531 #endif
532 break;
535 SDL_SemPost(s);
538 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
539 thread - threads, THREAD_SDL_GET_NAME(thread));
541 thread->state = STATE_KILLED;
542 thread_queue_wake(&thread->queue);
544 SDL_DestroySemaphore(s);
546 if (thread == current)
548 /* Do a graceful exit - perform the longjmp back into the thread
549 function to return */
550 restore_irq(oldlevel);
551 longjmp(thread_jmpbufs[current - threads], 1);
554 SDL_KillThread(t);
555 restore_irq(oldlevel);
558 void thread_exit(void)
560 remove_thread(NULL);
563 void thread_wait(struct thread_entry *thread)
565 struct thread_entry *current = cores[CURRENT_CORE].running;
567 if (thread == NULL)
568 thread = current;
570 if (thread->state != STATE_KILLED)
572 current->bqp = &thread->queue;
573 block_thread(current);
574 switch_thread();
578 int thread_stack_usage(const struct thread_entry *thread)
580 return 50;
581 (void)thread;
584 unsigned thread_get_status(const struct thread_entry *thread)
586 return thread->state;
589 /* Return name if one or ID if none */
590 void thread_get_name(char *buffer, int size,
591 struct thread_entry *thread)
593 if (size <= 0)
594 return;
596 *buffer = '\0';
598 if (thread)
600 /* Display thread name if one or ID if none */
601 bool named = thread->name && *thread->name;
602 const char *fmt = named ? "%s" : "%08lX";
603 intptr_t name = named ?
604 (intptr_t)thread->name : (intptr_t)thread;
605 snprintf(buffer, size, fmt, name);