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