summaryrefslogtreecommitdiff
path: root/uisimulator/sdl/thread-sdl.c
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2010-05-15 21:02:47 +0000
committerThomas Martitz <kugel@rockbox.org>2010-05-15 21:02:47 +0000
commit3d0cee8abbaf764958743e8a7851eee94e60a913 (patch)
treea96b1ec825003a71643a7da4707c300f64824f82 /uisimulator/sdl/thread-sdl.c
parentdcf442e61f21fb2aef5ce7de0547f733557b156e (diff)
downloadrockbox-3d0cee8abbaf764958743e8a7851eee94e60a913.tar.gz
rockbox-3d0cee8abbaf764958743e8a7851eee94e60a913.zip
- Move uisimulator/sdl/*.[ch] into the target tree, under firmware/target/hosted/sdl, uisdl.c is split up across button-sdl.c and system-sdl.c.
- Refactor the program startup. main() is now in main.c like on target, and the implicit application thread will now act as our main thread (previously a separate one was created for this in thread initialization). This is part of Rockbox as an application and is the first step to make an application port from the uisimulator. In a further step the sim bits from the sdl build will be separated out. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26065 a1c6a512-1295-4272-9138-f99709370657
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}