summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/sdl/thread-sdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/hosted/sdl/thread-sdl.c')
-rw-r--r--firmware/target/hosted/sdl/thread-sdl.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/firmware/target/hosted/sdl/thread-sdl.c b/firmware/target/hosted/sdl/thread-sdl.c
new file mode 100644
index 0000000000..fbe2621d40
--- /dev/null
+++ b/firmware/target/hosted/sdl/thread-sdl.c
@@ -0,0 +1,610 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Dan Everton
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
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"
35
36/* Define this as 1 to show informational messages that are not errors. */
37#define THREAD_SDL_DEBUGF_ENABLED 0
38
39#if THREAD_SDL_DEBUGF_ENABLED
40#define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
41static 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
48
49#define THREAD_PANICF(str...) \
50 ({ fprintf(stderr, str); exit(-1); })
51
52/* Thread/core entries as in rockbox core */
53static struct core_entry cores[NUM_CORES];
54struct 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 */
58static jmp_buf thread_jmpbufs[MAXTHREADS];
59/* this mutex locks out other Rockbox threads while one runs,
60 * that enables us to simulate a cooperative environment even if
61 * the host is preemptive */
62static SDL_mutex *m;
63static volatile bool threads_exit = false;
64
65extern long start_tick;
66
67void sim_thread_shutdown(void)
68{
69 int i;
70
71 /* Tell all threads jump back to their start routines, unlock and exit
72 gracefully - we'll check each one in turn for it's status. Threads
73 _could_ terminate via remove_thread or multiple threads could exit
74 on each unlock but that is safe. */
75
76 /* Do this before trying to acquire lock */
77 threads_exit = true;
78
79 /* Take control */
80 SDL_LockMutex(m);
81
82 for (i = 0; i < MAXTHREADS; i++)
83 {
84 struct thread_entry *thread = &threads[i];
85 /* exit all current threads, except the main one */
86 if (thread->context.t != NULL)
87 {
88 /* Signal thread on delay or block */
89 SDL_Thread *t = thread->context.t;
90 SDL_SemPost(thread->context.s);
91 SDL_UnlockMutex(m);
92 /* Wait for it to finish */
93 SDL_WaitThread(t, NULL);
94 /* Relock for next thread signal */
95 SDL_LockMutex(m);
96 }
97 }
98
99 SDL_UnlockMutex(m);
100 SDL_DestroyMutex(m);
101}
102
103static void new_thread_id(unsigned int slot_num,
104 struct thread_entry *thread)
105{
106 unsigned int version =
107 (thread->id + (1u << THREAD_ID_VERSION_SHIFT))
108 & THREAD_ID_VERSION_MASK;
109
110 if (version == 0)
111 version = 1u << THREAD_ID_VERSION_SHIFT;
112
113 thread->id = version | (slot_num & THREAD_ID_SLOT_MASK);
114}
115
116static struct thread_entry * find_empty_thread_slot(void)
117{
118 struct thread_entry *thread = NULL;
119 int n;
120
121 for (n = 0; n < MAXTHREADS; n++)
122 {
123 int state = threads[n].state;
124
125 if (state == STATE_KILLED)
126 {
127 thread = &threads[n];
128 break;
129 }
130 }
131
132 return thread;
133}
134
135
136/* Initialize SDL threading */
137void init_threads(void)
138{
139 struct thread_entry *thread;
140 int n;
141
142 memset(cores, 0, sizeof(cores));
143 memset(threads, 0, sizeof(threads));
144
145 m = SDL_CreateMutex();
146
147 if (SDL_LockMutex(m) == -1)
148 {
149 fprintf(stderr, "Couldn't lock mutex\n");
150 return;
151 }
152
153 /* Initialize all IDs */
154 for (n = 0; n < MAXTHREADS; n++)
155 threads[n].id = THREAD_ID_INIT(n);
156
157 /* Slot 0 is reserved for the main thread - initialize it here and
158 then create the SDL thread - it is possible to have a quick, early
159 shutdown try to access the structure. */
160 thread = &threads[0];
161 thread->stack = (uintptr_t *)" ";
162 thread->stack_size = 8;
163 thread->name = "main";
164 thread->state = STATE_RUNNING;
165 thread->context.s = SDL_CreateSemaphore(0);
166 thread->context.t = NULL; /* NULL for the implicit main thread */
167 cores[CURRENT_CORE].running = thread;
168
169 if (thread->context.s == NULL)
170 {
171 fprintf(stderr, "Failed to create main semaphore\n");
172 return;
173 }
174
175 THREAD_SDL_DEBUGF("Main thread: %p\n", thread);
176
177 return;
178}
179
180void sim_thread_exception_wait(void)
181{
182 while (1)
183 {
184 SDL_Delay(HZ/10);
185 if (threads_exit)
186 thread_exit();
187 }
188}
189
190/* A way to yield and leave the threading system for extended periods */
191void sim_thread_lock(void *me)
192{
193 SDL_LockMutex(m);
194 cores[CURRENT_CORE].running = (struct thread_entry *)me;
195
196 if (threads_exit)
197 thread_exit();
198}
199
200void * sim_thread_unlock(void)
201{
202 struct thread_entry *current = cores[CURRENT_CORE].running;
203 SDL_UnlockMutex(m);
204 return current;
205}
206
207struct thread_entry * thread_id_entry(unsigned int thread_id)
208{
209 return (thread_id == THREAD_ID_CURRENT) ?
210 cores[CURRENT_CORE].running :
211 &threads[thread_id & THREAD_ID_SLOT_MASK];
212}
213
214static void add_to_list_l(struct thread_entry **list,
215 struct thread_entry *thread)
216{
217 if (*list == NULL)
218 {
219 /* Insert into unoccupied list */
220 thread->l.next = thread;
221 thread->l.prev = thread;
222 *list = thread;
223 }
224 else
225 {
226 /* Insert last */
227 thread->l.next = *list;
228 thread->l.prev = (*list)->l.prev;
229 thread->l.prev->l.next = thread;
230 (*list)->l.prev = thread;
231 }
232}
233
234static void remove_from_list_l(struct thread_entry **list,
235 struct thread_entry *thread)
236{
237 if (thread == thread->l.next)
238 {
239 /* The only item */
240 *list = NULL;
241 return;
242 }
243
244 if (thread == *list)
245 {
246 /* List becomes next item */
247 *list = thread->l.next;
248 }
249
250 /* Fix links to jump over the removed entry. */
251 thread->l.prev->l.next = thread->l.next;
252 thread->l.next->l.prev = thread->l.prev;
253}
254
255unsigned int thread_get_current(void)
256{
257 return cores[CURRENT_CORE].running->id;
258}
259
260void switch_thread(void)
261{
262 struct thread_entry *current = cores[CURRENT_CORE].running;
263
264 enable_irq();
265
266 switch (current->state)
267 {
268 case STATE_RUNNING:
269 {
270 SDL_UnlockMutex(m);
271 /* Any other thread waiting already will get it first */
272 SDL_LockMutex(m);
273 break;
274 } /* STATE_RUNNING: */
275
276 case STATE_BLOCKED:
277 {
278 int oldlevel;
279
280 SDL_UnlockMutex(m);
281 SDL_SemWait(current->context.s);
282 SDL_LockMutex(m);
283
284 oldlevel = disable_irq_save();
285 current->state = STATE_RUNNING;
286 restore_irq(oldlevel);
287 break;
288 } /* STATE_BLOCKED: */
289
290 case STATE_BLOCKED_W_TMO:
291 {
292 int result, oldlevel;
293
294 SDL_UnlockMutex(m);
295 result = SDL_SemWaitTimeout(current->context.s, current->tmo_tick);
296 SDL_LockMutex(m);
297
298 oldlevel = disable_irq_save();
299
300 if (current->state == STATE_BLOCKED_W_TMO)
301 {
302 /* Timed out */
303 remove_from_list_l(current->bqp, current);
304
305#ifdef HAVE_WAKEUP_EXT_CB
306 if (current->wakeup_ext_cb != NULL)
307 current->wakeup_ext_cb(current);
308#endif
309 current->state = STATE_RUNNING;
310 }
311
312 if (result == SDL_MUTEX_TIMEDOUT)
313 {
314 /* Other signals from an explicit wake could have been made before
315 * arriving here if we timed out waiting for the semaphore. Make
316 * sure the count is reset. */
317 while (SDL_SemValue(current->context.s) > 0)
318 SDL_SemTryWait(current->context.s);
319 }
320
321 restore_irq(oldlevel);
322 break;
323 } /* STATE_BLOCKED_W_TMO: */
324
325 case STATE_SLEEPING:
326 {
327 SDL_UnlockMutex(m);
328 SDL_SemWaitTimeout(current->context.s, current->tmo_tick);
329 SDL_LockMutex(m);
330 current->state = STATE_RUNNING;
331 break;
332 } /* STATE_SLEEPING: */
333 }
334
335 cores[CURRENT_CORE].running = current;
336
337 if (threads_exit)
338 thread_exit();
339}
340
341void sleep_thread(int ticks)
342{
343 struct thread_entry *current = cores[CURRENT_CORE].running;
344 int rem;
345
346 current->state = STATE_SLEEPING;
347
348 rem = (SDL_GetTicks() - start_tick) % (1000/HZ);
349 if (rem < 0)
350 rem = 0;
351
352 current->tmo_tick = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
353}
354
355void block_thread(struct thread_entry *current)
356{
357 current->state = STATE_BLOCKED;
358 add_to_list_l(current->bqp, current);
359}
360
361void block_thread_w_tmo(struct thread_entry *current, int ticks)
362{
363 current->state = STATE_BLOCKED_W_TMO;
364 current->tmo_tick = (1000/HZ)*ticks;
365 add_to_list_l(current->bqp, current);
366}
367
368unsigned int wakeup_thread(struct thread_entry **list)
369{
370 struct thread_entry *thread = *list;
371
372 if (thread != NULL)
373 {
374 switch (thread->state)
375 {
376 case STATE_BLOCKED:
377 case STATE_BLOCKED_W_TMO:
378 remove_from_list_l(list, thread);
379 thread->state = STATE_RUNNING;
380 SDL_SemPost(thread->context.s);
381 return THREAD_OK;
382 }
383 }
384
385 return THREAD_NONE;
386}
387
388unsigned int thread_queue_wake(struct thread_entry **list)
389{
390 unsigned int result = THREAD_NONE;
391
392 for (;;)
393 {
394 unsigned int rc = wakeup_thread(list);
395
396 if (rc == THREAD_NONE)
397 break;
398
399 result |= rc;
400 }
401
402 return result;
403}
404
405void thread_thaw(unsigned int thread_id)
406{
407 struct thread_entry *thread = thread_id_entry(thread_id);
408
409 if (thread->id == thread_id && thread->state == STATE_FROZEN)
410 {
411 thread->state = STATE_RUNNING;
412 SDL_SemPost(thread->context.s);
413 }
414}
415
416int runthread(void *data)
417{
418 struct thread_entry *current;
419 jmp_buf *current_jmpbuf;
420
421 /* Cannot access thread variables before locking the mutex as the
422 data structures may not be filled-in yet. */
423 SDL_LockMutex(m);
424 cores[CURRENT_CORE].running = (struct thread_entry *)data;
425 current = cores[CURRENT_CORE].running;
426 current_jmpbuf = &thread_jmpbufs[current - threads];
427
428 /* Setup jump for exit */
429 if (setjmp(*current_jmpbuf) == 0)
430 {
431 /* Run the thread routine */
432 if (current->state == STATE_FROZEN)
433 {
434 SDL_UnlockMutex(m);
435 SDL_SemWait(current->context.s);
436 SDL_LockMutex(m);
437 cores[CURRENT_CORE].running = current;
438 }
439
440 if (!threads_exit)
441 {
442 current->context.start();
443 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
444 current - threads, THREAD_SDL_GET_NAME(current));
445 /* Thread routine returned - suicide */
446 }
447
448 thread_exit();
449 }
450 else
451 {
452 /* Unlock and exit */
453 SDL_UnlockMutex(m);
454 }
455
456 return 0;
457}
458
459unsigned int create_thread(void (*function)(void),
460 void* stack, size_t stack_size,
461 unsigned flags, const char *name)
462{
463 struct thread_entry *thread;
464 SDL_Thread* t;
465 SDL_sem *s;
466
467 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
468
469 thread = find_empty_thread_slot();
470 if (thread == NULL)
471 {
472 DEBUGF("Failed to find thread slot\n");
473 return 0;
474 }
475
476 s = SDL_CreateSemaphore(0);
477 if (s == NULL)
478 {
479 DEBUGF("Failed to create semaphore\n");
480 return 0;
481 }
482
483 t = SDL_CreateThread(runthread, thread);
484 if (t == NULL)
485 {
486 DEBUGF("Failed to create SDL thread\n");
487 SDL_DestroySemaphore(s);
488 return 0;
489 }
490
491 thread->stack = stack;
492 thread->stack_size = stack_size;
493 thread->name = name;
494 thread->state = (flags & CREATE_THREAD_FROZEN) ?
495 STATE_FROZEN : STATE_RUNNING;
496 thread->context.start = function;
497 thread->context.t = t;
498 thread->context.s = s;
499
500 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
501 thread - threads, THREAD_SDL_GET_NAME(thread));
502
503 return thread->id;
504}
505
506#ifndef ALLOW_REMOVE_THREAD
507static void remove_thread(unsigned int thread_id)
508#else
509void remove_thread(unsigned int thread_id)
510#endif
511{
512 struct thread_entry *current = cores[CURRENT_CORE].running;
513 struct thread_entry *thread = thread_id_entry(thread_id);
514
515 SDL_Thread *t;
516 SDL_sem *s;
517
518 if (thread_id != THREAD_ID_CURRENT && thread->id != thread_id)
519 return;
520
521 int oldlevel = disable_irq_save();
522
523 t = thread->context.t;
524 s = thread->context.s;
525 thread->context.t = NULL;
526
527 if (thread != current)
528 {
529 switch (thread->state)
530 {
531 case STATE_BLOCKED:
532 case STATE_BLOCKED_W_TMO:
533 /* Remove thread from object it's waiting on */
534 remove_from_list_l(thread->bqp, thread);
535
536#ifdef HAVE_WAKEUP_EXT_CB
537 if (thread->wakeup_ext_cb != NULL)
538 thread->wakeup_ext_cb(thread);
539#endif
540 break;
541 }
542
543 SDL_SemPost(s);
544 }
545
546 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
547 thread - threads, THREAD_SDL_GET_NAME(thread));
548
549 new_thread_id(thread->id, thread);
550 thread->state = STATE_KILLED;
551 thread_queue_wake(&thread->queue);
552
553 SDL_DestroySemaphore(s);
554
555 if (thread == current)
556 {
557 /* Do a graceful exit - perform the longjmp back into the thread
558 function to return */
559 restore_irq(oldlevel);
560 longjmp(thread_jmpbufs[current - threads], 1);
561 }
562
563 SDL_KillThread(t);
564 restore_irq(oldlevel);
565}
566
567void thread_exit(void)
568{
569 remove_thread(THREAD_ID_CURRENT);
570}
571
572void thread_wait(unsigned int thread_id)
573{
574 struct thread_entry *current = cores[CURRENT_CORE].running;
575 struct thread_entry *thread = thread_id_entry(thread_id);
576
577 if (thread_id == THREAD_ID_CURRENT ||
578 (thread->id == thread_id && thread->state != STATE_KILLED))
579 {
580 current->bqp = &thread->queue;
581 block_thread(current);
582 switch_thread();
583 }
584}
585
586int thread_stack_usage(const struct thread_entry *thread)
587{
588 return 50;
589 (void)thread;
590}
591
592/* Return name if one or ID if none */
593void thread_get_name(char *buffer, int size,
594 struct thread_entry *thread)
595{
596 if (size <= 0)
597 return;
598
599 *buffer = '\0';
600
601 if (thread)
602 {
603 /* Display thread name if one or ID if none */
604 bool named = thread->name && *thread->name;
605 const char *fmt = named ? "%s" : "%08lX";
606 intptr_t name = named ?
607 (intptr_t)thread->name : (intptr_t)thread;
608 snprintf(buffer, size, fmt, name);
609 }
610}