1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
23 #include <SDL_thread.h>
27 #include "system-sdl.h"
28 #include "thread-sdl.h"
34 /* Define this as 1 to show informational messages that are not errors. */
35 #define THREAD_SDL_DEBUGF_ENABLED 0
37 #if THREAD_SDL_DEBUGF_ENABLED
38 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
39 static char __name
[32];
40 #define THREAD_SDL_GET_NAME(thread) \
41 ({ thread_get_name(__name, ARRAYLEN(__name), thread); __name; })
43 #define THREAD_SDL_DEBUGF(...)
44 #define THREAD_SDL_GET_NAME(thread)
47 #define THREAD_PANICF(str...) \
48 ({ fprintf(stderr, str); exit(-1); })
50 /* Thread/core entries as in rockbox core */
51 struct core_entry cores
[NUM_CORES
];
52 struct thread_entry threads
[MAXTHREADS
];
53 /* Jump buffers for graceful exit - kernel threads don't stay neatly
54 * in their start routines responding to messages so this is the only
55 * way to get them back in there so they may exit */
56 static jmp_buf thread_jmpbufs
[MAXTHREADS
];
58 static bool threads_exit
= false;
60 extern long start_tick
;
62 void thread_sdl_shutdown(void)
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. */
74 for (i
= 0; i
< MAXTHREADS
; i
++)
76 struct thread_entry
*thread
= &threads
[i
];
77 if (thread
->context
.t
!= NULL
)
79 /* Signal thread on delay or block */
80 SDL_Thread
*t
= thread
->context
.t
;
81 SDL_SemPost(thread
->context
.s
);
83 /* Wait for it to finish */
84 SDL_WaitThread(t
, NULL
);
85 /* Relock for next thread signal */
94 /* Do main thread creation in this file scope to avoid the need to double-
95 return to a prior call-level which would be unaware of the fact setjmp
97 extern void app_main(void *param
);
98 static int thread_sdl_app_main(void *param
)
101 cores
[CURRENT_CORE
].running
= &threads
[0];
103 /* Set the jump address for return */
104 if (setjmp(thread_jmpbufs
[0]) == 0)
107 /* should not ever be reached but... */
108 THREAD_PANICF("app_main returned!\n");
111 /* Unlock and exit */
116 /* Initialize SDL threading */
117 bool thread_sdl_init(void *param
)
119 struct thread_entry
*thread
;
120 memset(cores
, 0, sizeof(cores
));
121 memset(threads
, 0, sizeof(threads
));
123 m
= SDL_CreateMutex();
125 if (SDL_LockMutex(m
) == -1)
127 fprintf(stderr
, "Couldn't lock mutex\n");
131 /* Slot 0 is reserved for the main thread - initialize it here and
132 then create the SDL thread - it is possible to have a quick, early
133 shutdown try to access the structure. */
134 thread
= &threads
[0];
135 thread
->stack
= (uintptr_t *)" ";
136 thread
->stack_size
= 8;
137 thread
->name
= "main";
138 thread
->state
= STATE_RUNNING
;
139 thread
->context
.s
= SDL_CreateSemaphore(0);
140 cores
[CURRENT_CORE
].running
= thread
;
142 if (thread
->context
.s
== NULL
)
144 fprintf(stderr
, "Failed to create main semaphore\n");
148 thread
->context
.t
= SDL_CreateThread(thread_sdl_app_main
, param
);
150 if (thread
->context
.t
== NULL
)
152 SDL_DestroySemaphore(thread
->context
.s
);
153 fprintf(stderr
, "Failed to create main thread\n");
157 THREAD_SDL_DEBUGF("Main thread: %p\n", thread
);
163 /* A way to yield and leave the threading system for extended periods */
164 void thread_sdl_thread_lock(void *me
)
167 cores
[CURRENT_CORE
].running
= (struct thread_entry
*)me
;
173 void * thread_sdl_thread_unlock(void)
175 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
180 static struct thread_entry
* find_empty_thread_slot(void)
182 struct thread_entry
*thread
= NULL
;
185 for (n
= 0; n
< MAXTHREADS
; n
++)
187 int state
= threads
[n
].state
;
189 if (state
== STATE_KILLED
)
191 thread
= &threads
[n
];
199 static void add_to_list_l(struct thread_entry
**list
,
200 struct thread_entry
*thread
)
204 /* Insert into unoccupied list */
205 thread
->l
.next
= thread
;
206 thread
->l
.prev
= thread
;
212 thread
->l
.next
= *list
;
213 thread
->l
.prev
= (*list
)->l
.prev
;
214 thread
->l
.prev
->l
.next
= thread
;
215 (*list
)->l
.prev
= thread
;
219 static void remove_from_list_l(struct thread_entry
**list
,
220 struct thread_entry
*thread
)
222 if (thread
== thread
->l
.next
)
231 /* List becomes next item */
232 *list
= thread
->l
.next
;
235 /* Fix links to jump over the removed entry. */
236 thread
->l
.prev
->l
.next
= thread
->l
.next
;
237 thread
->l
.next
->l
.prev
= thread
->l
.prev
;
240 struct thread_entry
*thread_get_current(void)
242 return cores
[CURRENT_CORE
].running
;
245 void switch_thread(void)
247 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
251 switch (current
->state
)
256 /* Any other thread waiting already will get it first */
259 } /* STATE_RUNNING: */
266 SDL_SemWait(current
->context
.s
);
269 oldlevel
= disable_irq_save();
270 current
->state
= STATE_RUNNING
;
271 restore_irq(oldlevel
);
273 } /* STATE_BLOCKED: */
275 case STATE_BLOCKED_W_TMO
:
277 int result
, oldlevel
;
280 result
= SDL_SemWaitTimeout(current
->context
.s
, current
->tmo_tick
);
283 oldlevel
= disable_irq_save();
285 if (current
->state
== STATE_BLOCKED_W_TMO
)
288 remove_from_list_l(current
->bqp
, current
);
290 #ifdef HAVE_WAKEUP_EXT_CB
291 if (current
->wakeup_ext_cb
!= NULL
)
292 current
->wakeup_ext_cb(current
);
294 current
->state
= STATE_RUNNING
;
297 if (result
== SDL_MUTEX_TIMEDOUT
)
299 /* Other signals from an explicit wake could have been made before
300 * arriving here if we timed out waiting for the semaphore. Make
301 * sure the count is reset. */
302 while (SDL_SemValue(current
->context
.s
) > 0)
303 SDL_SemTryWait(current
->context
.s
);
306 restore_irq(oldlevel
);
308 } /* STATE_BLOCKED_W_TMO: */
313 SDL_SemWaitTimeout(current
->context
.s
, current
->tmo_tick
);
315 current
->state
= STATE_RUNNING
;
317 } /* STATE_SLEEPING: */
320 cores
[CURRENT_CORE
].running
= current
;
326 void sleep_thread(int ticks
)
328 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
331 current
->state
= STATE_SLEEPING
;
333 rem
= (SDL_GetTicks() - start_tick
) % (1000/HZ
);
337 current
->tmo_tick
= (1000/HZ
) * ticks
+ ((1000/HZ
)-1) - rem
;
340 void block_thread(struct thread_entry
*current
)
342 current
->state
= STATE_BLOCKED
;
343 add_to_list_l(current
->bqp
, current
);
346 void block_thread_w_tmo(struct thread_entry
*current
, int ticks
)
348 current
->state
= STATE_BLOCKED_W_TMO
;
349 current
->tmo_tick
= (1000/HZ
)*ticks
;
350 add_to_list_l(current
->bqp
, current
);
353 unsigned int wakeup_thread(struct thread_entry
**list
)
355 struct thread_entry
*thread
= *list
;
359 switch (thread
->state
)
362 case STATE_BLOCKED_W_TMO
:
363 remove_from_list_l(list
, thread
);
364 thread
->state
= STATE_RUNNING
;
365 SDL_SemPost(thread
->context
.s
);
373 unsigned int thread_queue_wake(struct thread_entry
**list
)
375 unsigned int result
= THREAD_NONE
;
379 unsigned int rc
= wakeup_thread(list
);
381 if (rc
== THREAD_NONE
)
390 void thread_thaw(struct thread_entry
*thread
)
392 if (thread
->state
== STATE_FROZEN
)
394 thread
->state
= STATE_RUNNING
;
395 SDL_SemPost(thread
->context
.s
);
399 int runthread(void *data
)
401 struct thread_entry
*current
;
402 jmp_buf *current_jmpbuf
;
404 /* Cannot access thread variables before locking the mutex as the
405 data structures may not be filled-in yet. */
407 cores
[CURRENT_CORE
].running
= (struct thread_entry
*)data
;
408 current
= cores
[CURRENT_CORE
].running
;
409 current_jmpbuf
= &thread_jmpbufs
[current
- threads
];
411 /* Setup jump for exit */
412 if (setjmp(*current_jmpbuf
) == 0)
414 /* Run the thread routine */
415 if (current
->state
== STATE_FROZEN
)
418 SDL_SemWait(current
->context
.s
);
420 cores
[CURRENT_CORE
].running
= current
;
425 current
->context
.start();
426 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
427 current
- threads
, THREAD_SDL_GET_NAME(current
));
428 /* Thread routine returned - suicide */
435 /* Unlock and exit */
443 create_thread(void (*function
)(void), void* stack
, size_t stack_size
,
444 unsigned flags
, const char *name
)
446 struct thread_entry
*thread
;
450 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name
? name
: "");
452 thread
= find_empty_thread_slot();
455 DEBUGF("Failed to find thread slot\n");
459 s
= SDL_CreateSemaphore(0);
462 DEBUGF("Failed to create semaphore\n");
466 t
= SDL_CreateThread(runthread
, thread
);
469 DEBUGF("Failed to create SDL thread\n");
470 SDL_DestroySemaphore(s
);
474 thread
->stack
= stack
;
475 thread
->stack_size
= stack_size
;
477 thread
->state
= (flags
& CREATE_THREAD_FROZEN
) ?
478 STATE_FROZEN
: STATE_RUNNING
;
479 thread
->context
.start
= function
;
480 thread
->context
.t
= t
;
481 thread
->context
.s
= s
;
483 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
484 thread
- threads
, THREAD_SDL_GET_NAME(thread
));
489 void init_threads(void)
491 /* Main thread is already initialized */
492 if (cores
[CURRENT_CORE
].running
!= &threads
[0])
494 THREAD_PANICF("Wrong main thread in init_threads: %p\n",
495 cores
[CURRENT_CORE
].running
);
498 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
499 0, THREAD_SDL_GET_NAME(&threads
[0]));
502 void remove_thread(struct thread_entry
*thread
)
504 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
508 int oldlevel
= disable_irq_save();
515 t
= thread
->context
.t
;
516 s
= thread
->context
.s
;
517 thread
->context
.t
= NULL
;
519 if (thread
!= current
)
521 switch (thread
->state
)
524 case STATE_BLOCKED_W_TMO
:
525 /* Remove thread from object it's waiting on */
526 remove_from_list_l(thread
->bqp
, thread
);
528 #ifdef HAVE_WAKEUP_EXT_CB
529 if (thread
->wakeup_ext_cb
!= NULL
)
530 thread
->wakeup_ext_cb(thread
);
538 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
539 thread
- threads
, THREAD_SDL_GET_NAME(thread
));
541 thread
->state
= STATE_KILLED
;
542 thread_queue_wake(&thread
->queue
);
544 SDL_DestroySemaphore(s
);
546 if (thread
== current
)
548 /* Do a graceful exit - perform the longjmp back into the thread
549 function to return */
550 restore_irq(oldlevel
);
551 longjmp(thread_jmpbufs
[current
- threads
], 1);
555 restore_irq(oldlevel
);
558 void thread_exit(void)
563 void thread_wait(struct thread_entry
*thread
)
565 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
570 if (thread
->state
!= STATE_KILLED
)
572 current
->bqp
= &thread
->queue
;
573 block_thread(current
);
578 int thread_stack_usage(const struct thread_entry
*thread
)
584 unsigned thread_get_status(const struct thread_entry
*thread
)
586 return thread
->state
;
589 /* Return name if one or ID if none */
590 void thread_get_name(char *buffer
, int size
,
591 struct thread_entry
*thread
)
600 /* Display thread name if one or ID if none */
601 bool named
= thread
->name
&& *thread
->name
;
602 const char *fmt
= named
? "%s" : "%08lX";
603 intptr_t name
= named
?
604 (intptr_t)thread
->name
: (intptr_t)thread
;
605 snprintf(buffer
, size
, fmt
, name
);