summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/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 /firmware/target/hosted/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 '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}