Remove the full url path from links to the wiki and display the wiki name only instea...
[Rockbox.git] / uisimulator / sdl / thread-sdl.c
blob0bd7d2534e27b43af7fe546a8384ff1f70a42f3c
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 "thread-sdl.h"
28 #include "kernel.h"
29 #include "thread.h"
30 #include "debug.h"
32 /* Define this as 1 to show informational messages that are not errors. */
33 #define THREAD_SDL_DEBUGF_ENABLED 0
35 #if THREAD_SDL_DEBUGF_ENABLED
36 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
37 static char __name[32];
38 #define THREAD_SDL_GET_NAME(thread) \
39 ({ thread_get_name(__name, sizeof(__name)/sizeof(__name[0]), thread); __name; })
40 #else
41 #define THREAD_SDL_DEBUGF(...)
42 #define THREAD_SDL_GET_NAME(thread)
43 #endif
45 #define THREAD_PANICF(str...) \
46 ({ fprintf(stderr, str); exit(-1); })
48 /* Thread entries as in core */
49 struct thread_entry threads[MAXTHREADS];
50 /* Jump buffers for graceful exit - kernel threads don't stay neatly
51 * in their start routines responding to messages so this is the only
52 * way to get them back in there so they may exit */
53 static jmp_buf thread_jmpbufs[MAXTHREADS];
54 static SDL_mutex *m;
55 static struct thread_entry *running;
56 static bool threads_exit = false;
58 extern long start_tick;
60 void thread_sdl_shutdown(void)
62 int i;
63 /* Take control */
64 SDL_LockMutex(m);
66 /* Tell all threads jump back to their start routines, unlock and exit
67 gracefully - we'll check each one in turn for it's status. Threads
68 _could_ terminate via remove_thread or multiple threads could exit
69 on each unlock but that is safe. */
70 threads_exit = true;
72 for (i = 0; i < MAXTHREADS; i++)
74 struct thread_entry *thread = &threads[i];
75 if (thread->context.t != NULL)
77 /* Signal thread on delay or block */
78 SDL_Thread *t = thread->context.t;
79 SDL_CondSignal(thread->context.c);
80 SDL_UnlockMutex(m);
81 /* Wait for it to finish */
82 SDL_WaitThread(t, NULL);
83 /* Relock for next thread signal */
84 SDL_LockMutex(m);
88 SDL_UnlockMutex(m);
89 SDL_DestroyMutex(m);
92 /* Do main thread creation in this file scope to avoid the need to double-
93 return to a prior call-level which would be unaware of the fact setjmp
94 was used */
95 extern void app_main(void *param);
96 static int thread_sdl_app_main(void *param)
98 SDL_LockMutex(m);
99 running = &threads[0];
101 /* Set the jump address for return */
102 if (setjmp(thread_jmpbufs[0]) == 0)
104 app_main(param);
105 /* should not ever be reached but... */
106 THREAD_PANICF("app_main returned!\n");
109 /* Unlock and exit */
110 SDL_UnlockMutex(m);
111 return 0;
114 /* Initialize SDL threading */
115 bool thread_sdl_init(void *param)
117 memset(threads, 0, sizeof(threads));
119 m = SDL_CreateMutex();
121 if (SDL_LockMutex(m) == -1)
123 fprintf(stderr, "Couldn't lock mutex\n");
124 return false;
127 /* Slot 0 is reserved for the main thread - initialize it here and
128 then create the SDL thread - it is possible to have a quick, early
129 shutdown try to access the structure. */
130 running = &threads[0];
131 running->stack = " ";
132 running->stack_size = 8;
133 running->name = "main";
134 running->statearg = STATE_RUNNING;
135 running->context.c = SDL_CreateCond();
137 if (running->context.c == NULL)
139 fprintf(stderr, "Failed to create main condition variable\n");
140 return false;
143 running->context.t = SDL_CreateThread(thread_sdl_app_main, param);
145 if (running->context.t == NULL)
147 fprintf(stderr, "Failed to create main thread\n");
148 return false;
151 THREAD_SDL_DEBUGF("Main thread: %p\n", running);
153 SDL_UnlockMutex(m);
154 return true;
157 static int find_empty_thread_slot(void)
159 int n;
161 for (n = 0; n < MAXTHREADS; n++)
163 if (threads[n].name == NULL)
164 break;
167 return n;
170 static void add_to_list(struct thread_entry **list,
171 struct thread_entry *thread)
173 if (*list == NULL)
175 /* Insert into unoccupied list */
176 thread->next = thread;
177 thread->prev = thread;
178 *list = thread;
180 else
182 /* Insert last */
183 thread->next = *list;
184 thread->prev = (*list)->prev;
185 thread->prev->next = thread;
186 (*list)->prev = thread;
190 static void remove_from_list(struct thread_entry **list,
191 struct thread_entry *thread)
193 if (thread == thread->next)
195 /* The only item */
196 *list = NULL;
197 return;
200 if (thread == *list)
202 /* List becomes next item */
203 *list = thread->next;
206 /* Fix links to jump over the removed entry. */
207 thread->prev->next = thread->next;
208 thread->next->prev = thread->prev;
211 struct thread_entry *thread_get_current(void)
213 return running;
216 void thread_sdl_lock(void)
218 SDL_LockMutex(m);
221 void thread_sdl_unlock(void)
223 SDL_UnlockMutex(m);
226 void switch_thread(bool save_context, struct thread_entry **blocked_list)
228 struct thread_entry *current = running;
230 SDL_UnlockMutex(m);
231 /* Any other thread waiting already will get it first */
232 SDL_LockMutex(m);
233 running = current;
235 if (threads_exit)
236 remove_thread(NULL);
238 (void)save_context; (void)blocked_list;
241 void sleep_thread(int ticks)
243 struct thread_entry *current;
244 int rem;
246 current = running;
247 current->statearg = STATE_SLEEPING;
249 rem = (SDL_GetTicks() - start_tick) % (1000/HZ);
250 if (rem < 0)
251 rem = 0;
253 rem = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
255 if (rem == 0)
257 /* Unlock and give up rest of quantum */
258 SDL_UnlockMutex(m);
259 SDL_Delay(0);
260 SDL_LockMutex(m);
262 else
264 /* These sleeps must be signalable for thread exit */
265 SDL_CondWaitTimeout(current->context.c, m, rem);
268 running = current;
270 current->statearg = STATE_RUNNING;
272 if (threads_exit)
273 remove_thread(NULL);
276 int runthread(void *data)
278 struct thread_entry *current;
279 jmp_buf *current_jmpbuf;
281 /* Cannot access thread variables before locking the mutex as the
282 data structures may not be filled-in yet. */
283 SDL_LockMutex(m);
284 running = (struct thread_entry *)data;
285 current = running;
286 current_jmpbuf = &thread_jmpbufs[running - threads];
288 /* Setup jump for exit */
289 if (setjmp(*current_jmpbuf) == 0)
291 /* Run the thread routine */
292 current->context.start();
293 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
294 current - threads, THREAD_SDL_GET_NAME(current));
295 /* Thread routine returned - suicide */
296 remove_thread(NULL);
298 else
300 /* Unlock and exit */
301 SDL_UnlockMutex(m);
304 return 0;
307 struct thread_entry*
308 create_thread(void (*function)(void), void* stack, int stack_size,
309 const char *name)
311 /** Avoid compiler warnings */
312 SDL_Thread* t;
313 SDL_cond *cond;
314 int slot;
316 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
318 slot = find_empty_thread_slot();
319 if (slot >= MAXTHREADS)
321 DEBUGF("Failed to find thread slot\n");
322 return NULL;
325 cond = SDL_CreateCond();
326 if (cond == NULL)
328 DEBUGF("Failed to create condition variable\n");
329 return NULL;
332 t = SDL_CreateThread(runthread, &threads[slot]);
333 if (t == NULL)
335 DEBUGF("Failed to create SDL thread\n");
336 SDL_DestroyCond(cond);
337 return NULL;
340 threads[slot].stack = stack;
341 threads[slot].stack_size = stack_size;
342 threads[slot].name = name;
343 threads[slot].statearg = STATE_RUNNING;
344 threads[slot].context.start = function;
345 threads[slot].context.t = t;
346 threads[slot].context.c = cond;
348 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
349 slot, THREAD_SDL_GET_NAME(&threads[slot]));
351 return &threads[slot];
354 void block_thread(struct thread_entry **list)
356 struct thread_entry *thread = running;
358 thread->statearg = STATE_BLOCKED;
359 add_to_list(list, thread);
361 SDL_CondWait(thread->context.c, m);
362 running = thread;
364 if (threads_exit)
365 remove_thread(NULL);
368 void block_thread_w_tmo(struct thread_entry **list, int ticks)
370 struct thread_entry *thread = running;
372 thread->statearg = STATE_BLOCKED_W_TMO;
373 add_to_list(list, thread);
375 SDL_CondWaitTimeout(thread->context.c, m, (1000/HZ) * ticks);
376 running = thread;
378 if (thread->statearg == STATE_BLOCKED_W_TMO)
380 /* Timed out */
381 remove_from_list(list, thread);
382 thread->statearg = STATE_RUNNING;
385 if (threads_exit)
386 remove_thread(NULL);
389 void wakeup_thread(struct thread_entry **list)
391 struct thread_entry *thread = *list;
393 if (thread == NULL)
395 return;
398 switch (thread->statearg)
400 case STATE_BLOCKED:
401 case STATE_BLOCKED_W_TMO:
402 remove_from_list(list, thread);
403 thread->statearg = STATE_RUNNING;
404 SDL_CondSignal(thread->context.c);
405 break;
409 void init_threads(void)
411 /* Main thread is already initialized */
412 if (running != &threads[0])
414 THREAD_PANICF("Wrong main thread in init_threads: %p\n", running);
417 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
418 0, THREAD_SDL_GET_NAME(&threads[0]));
421 void remove_thread(struct thread_entry *thread)
423 struct thread_entry *current = running;
424 SDL_Thread *t;
425 SDL_cond *c;
427 if (thread == NULL)
429 thread = current;
432 t = thread->context.t;
433 c = thread->context.c;
434 thread->context.t = NULL;
436 if (thread != current)
437 SDL_CondSignal(c);
439 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
440 thread - threads, THREAD_SDL_GET_NAME(thread));
442 thread->name = NULL;
444 SDL_DestroyCond(c);
446 if (thread == current)
448 /* Do a graceful exit - perform the longjmp back into the thread
449 function to return */
450 longjmp(thread_jmpbufs[current - threads], 1);
453 SDL_KillThread(t);
456 int thread_stack_usage(const struct thread_entry *thread)
458 return 50;
459 (void)thread;
462 int thread_get_status(const struct thread_entry *thread)
464 return thread->statearg;
467 /* Return name if one or ID if none */
468 void thread_get_name(char *buffer, int size,
469 struct thread_entry *thread)
471 if (size <= 0)
472 return;
474 *buffer = '\0';
476 if (thread)
478 /* Display thread name if one or ID if none */
479 bool named = thread->name && *thread->name;
480 const char *fmt = named ? "%s" : "%08lX";
481 intptr_t name = named ?
482 (intptr_t)thread->name : (intptr_t)thread;
483 snprintf(buffer, size, fmt, name);