1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 Dan Everton
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
25 #include <SDL_thread.h>
29 #include "system-sdl.h"
30 #include "thread-sdl.h"
36 /* Define this as 1 to show informational messages that are not errors. */
37 #define THREAD_SDL_DEBUGF_ENABLED 0
39 #if THREAD_SDL_DEBUGF_ENABLED
40 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
41 static char __name
[32];
42 #define THREAD_SDL_GET_NAME(thread) \
43 ({ thread_get_name(__name, ARRAYLEN(__name), thread); __name; })
45 #define THREAD_SDL_DEBUGF(...)
46 #define THREAD_SDL_GET_NAME(thread)
49 #define THREAD_PANICF(str...) \
50 ({ fprintf(stderr, str); exit(-1); })
52 /* Thread/core entries as in rockbox core */
53 static struct core_entry cores
[NUM_CORES
];
54 struct 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 */
58 static jmp_buf thread_jmpbufs
[MAXTHREADS
];
60 static volatile bool threads_exit
= false;
62 extern long start_tick
;
64 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. */
73 /* Do this before trying to acquire lock */
79 for (i
= 0; i
< MAXTHREADS
; i
++)
81 struct thread_entry
*thread
= &threads
[i
];
82 if (thread
->context
.t
!= NULL
)
84 /* Signal thread on delay or block */
85 SDL_Thread
*t
= thread
->context
.t
;
86 SDL_SemPost(thread
->context
.s
);
88 /* Wait for it to finish */
89 SDL_WaitThread(t
, NULL
);
90 /* Relock for next thread signal */
99 static void new_thread_id(unsigned int slot_num
,
100 struct thread_entry
*thread
)
102 unsigned int version
=
103 (thread
->id
+ (1u << THREAD_ID_VERSION_SHIFT
))
104 & THREAD_ID_VERSION_MASK
;
107 version
= 1u << THREAD_ID_VERSION_SHIFT
;
109 thread
->id
= version
| (slot_num
& THREAD_ID_SLOT_MASK
);
112 static struct thread_entry
* find_empty_thread_slot(void)
114 struct thread_entry
*thread
= NULL
;
117 for (n
= 0; n
< MAXTHREADS
; n
++)
119 int state
= threads
[n
].state
;
121 if (state
== STATE_KILLED
)
123 thread
= &threads
[n
];
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
134 extern void app_main(void *param
);
135 static int thread_sdl_app_main(void *param
)
138 cores
[CURRENT_CORE
].running
= &threads
[0];
140 /* Set the jump address for return */
141 if (setjmp(thread_jmpbufs
[0]) == 0)
144 /* should not ever be reached but... */
145 THREAD_PANICF("app_main returned!\n");
148 /* Unlock and exit */
153 /* Initialize SDL threading */
154 bool thread_sdl_init(void *param
)
156 struct thread_entry
*thread
;
159 memset(cores
, 0, sizeof(cores
));
160 memset(threads
, 0, sizeof(threads
));
162 m
= SDL_CreateMutex();
164 if (SDL_LockMutex(m
) == -1)
166 fprintf(stderr
, "Couldn't lock mutex\n");
170 /* Initialize all IDs */
171 for (n
= 0; n
< MAXTHREADS
; n
++)
172 threads
[n
].id
= THREAD_ID_INIT(n
);
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
;
185 if (thread
->context
.s
== NULL
)
187 fprintf(stderr
, "Failed to create main semaphore\n");
191 thread
->context
.t
= SDL_CreateThread(thread_sdl_app_main
, param
);
193 if (thread
->context
.t
== NULL
)
195 SDL_DestroySemaphore(thread
->context
.s
);
196 fprintf(stderr
, "Failed to create main thread\n");
200 THREAD_SDL_DEBUGF("Main thread: %p\n", thread
);
206 void thread_sdl_exception_wait(void)
216 /* A way to yield and leave the threading system for extended periods */
217 void thread_sdl_thread_lock(void *me
)
220 cores
[CURRENT_CORE
].running
= (struct thread_entry
*)me
;
226 void * thread_sdl_thread_unlock(void)
228 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
233 struct thread_entry
* thread_id_entry(unsigned int thread_id
)
235 return (thread_id
== THREAD_ID_CURRENT
) ?
236 cores
[CURRENT_CORE
].running
:
237 &threads
[thread_id
& THREAD_ID_SLOT_MASK
];
240 static void add_to_list_l(struct thread_entry
**list
,
241 struct thread_entry
*thread
)
245 /* Insert into unoccupied list */
246 thread
->l
.next
= thread
;
247 thread
->l
.prev
= thread
;
253 thread
->l
.next
= *list
;
254 thread
->l
.prev
= (*list
)->l
.prev
;
255 thread
->l
.prev
->l
.next
= thread
;
256 (*list
)->l
.prev
= thread
;
260 static void remove_from_list_l(struct thread_entry
**list
,
261 struct thread_entry
*thread
)
263 if (thread
== thread
->l
.next
)
272 /* List becomes next item */
273 *list
= thread
->l
.next
;
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
;
281 unsigned int thread_get_current(void)
283 return cores
[CURRENT_CORE
].running
->id
;
286 void switch_thread(void)
288 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
292 switch (current
->state
)
297 /* Any other thread waiting already will get it first */
300 } /* STATE_RUNNING: */
307 SDL_SemWait(current
->context
.s
);
310 oldlevel
= disable_irq_save();
311 current
->state
= STATE_RUNNING
;
312 restore_irq(oldlevel
);
314 } /* STATE_BLOCKED: */
316 case STATE_BLOCKED_W_TMO
:
318 int result
, oldlevel
;
321 result
= SDL_SemWaitTimeout(current
->context
.s
, current
->tmo_tick
);
324 oldlevel
= disable_irq_save();
326 if (current
->state
== STATE_BLOCKED_W_TMO
)
329 remove_from_list_l(current
->bqp
, current
);
331 #ifdef HAVE_WAKEUP_EXT_CB
332 if (current
->wakeup_ext_cb
!= NULL
)
333 current
->wakeup_ext_cb(current
);
335 current
->state
= STATE_RUNNING
;
338 if (result
== SDL_MUTEX_TIMEDOUT
)
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
);
347 restore_irq(oldlevel
);
349 } /* STATE_BLOCKED_W_TMO: */
354 SDL_SemWaitTimeout(current
->context
.s
, current
->tmo_tick
);
356 current
->state
= STATE_RUNNING
;
358 } /* STATE_SLEEPING: */
361 cores
[CURRENT_CORE
].running
= current
;
367 void sleep_thread(int ticks
)
369 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
372 current
->state
= STATE_SLEEPING
;
374 rem
= (SDL_GetTicks() - start_tick
) % (1000/HZ
);
378 current
->tmo_tick
= (1000/HZ
) * ticks
+ ((1000/HZ
)-1) - rem
;
381 void block_thread(struct thread_entry
*current
)
383 current
->state
= STATE_BLOCKED
;
384 add_to_list_l(current
->bqp
, current
);
387 void block_thread_w_tmo(struct thread_entry
*current
, int ticks
)
389 current
->state
= STATE_BLOCKED_W_TMO
;
390 current
->tmo_tick
= (1000/HZ
)*ticks
;
391 add_to_list_l(current
->bqp
, current
);
394 unsigned int wakeup_thread(struct thread_entry
**list
)
396 struct thread_entry
*thread
= *list
;
400 switch (thread
->state
)
403 case STATE_BLOCKED_W_TMO
:
404 remove_from_list_l(list
, thread
);
405 thread
->state
= STATE_RUNNING
;
406 SDL_SemPost(thread
->context
.s
);
414 unsigned int thread_queue_wake(struct thread_entry
**list
)
416 unsigned int result
= THREAD_NONE
;
420 unsigned int rc
= wakeup_thread(list
);
422 if (rc
== THREAD_NONE
)
431 void thread_thaw(unsigned int thread_id
)
433 struct thread_entry
*thread
= thread_id_entry(thread_id
);
435 if (thread
->id
== thread_id
&& thread
->state
== STATE_FROZEN
)
437 thread
->state
= STATE_RUNNING
;
438 SDL_SemPost(thread
->context
.s
);
442 int runthread(void *data
)
444 struct thread_entry
*current
;
445 jmp_buf *current_jmpbuf
;
447 /* Cannot access thread variables before locking the mutex as the
448 data structures may not be filled-in yet. */
450 cores
[CURRENT_CORE
].running
= (struct thread_entry
*)data
;
451 current
= cores
[CURRENT_CORE
].running
;
452 current_jmpbuf
= &thread_jmpbufs
[current
- threads
];
454 /* Setup jump for exit */
455 if (setjmp(*current_jmpbuf
) == 0)
457 /* Run the thread routine */
458 if (current
->state
== STATE_FROZEN
)
461 SDL_SemWait(current
->context
.s
);
463 cores
[CURRENT_CORE
].running
= current
;
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 */
478 /* Unlock and exit */
485 unsigned int create_thread(void (*function
)(void),
486 void* stack
, size_t stack_size
,
487 unsigned flags
, const char *name
)
489 struct thread_entry
*thread
;
493 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name
? name
: "");
495 thread
= find_empty_thread_slot();
498 DEBUGF("Failed to find thread slot\n");
502 s
= SDL_CreateSemaphore(0);
505 DEBUGF("Failed to create semaphore\n");
509 t
= SDL_CreateThread(runthread
, thread
);
512 DEBUGF("Failed to create SDL thread\n");
513 SDL_DestroySemaphore(s
);
517 thread
->stack
= stack
;
518 thread
->stack_size
= stack_size
;
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
;
526 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
527 thread
- threads
, THREAD_SDL_GET_NAME(thread
));
532 void init_threads(void)
534 /* Main thread is already initialized */
535 if (cores
[CURRENT_CORE
].running
!= &threads
[0])
537 THREAD_PANICF("Wrong main thread in init_threads: %p\n",
538 cores
[CURRENT_CORE
].running
);
541 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
542 0, THREAD_SDL_GET_NAME(&threads
[0]));
545 #ifndef ALLOW_REMOVE_THREAD
546 static void remove_thread(unsigned int thread_id
)
548 void remove_thread(unsigned int thread_id
)
551 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
552 struct thread_entry
*thread
= thread_id_entry(thread_id
);
557 if (thread_id
!= THREAD_ID_CURRENT
&& thread
->id
!= thread_id
)
560 int oldlevel
= disable_irq_save();
562 t
= thread
->context
.t
;
563 s
= thread
->context
.s
;
564 thread
->context
.t
= NULL
;
566 if (thread
!= current
)
568 switch (thread
->state
)
571 case STATE_BLOCKED_W_TMO
:
572 /* Remove thread from object it's waiting on */
573 remove_from_list_l(thread
->bqp
, thread
);
575 #ifdef HAVE_WAKEUP_EXT_CB
576 if (thread
->wakeup_ext_cb
!= NULL
)
577 thread
->wakeup_ext_cb(thread
);
585 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
586 thread
- threads
, THREAD_SDL_GET_NAME(thread
));
588 new_thread_id(thread
->id
, thread
);
589 thread
->state
= STATE_KILLED
;
590 thread_queue_wake(&thread
->queue
);
592 SDL_DestroySemaphore(s
);
594 if (thread
== current
)
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);
603 restore_irq(oldlevel
);
606 void thread_exit(void)
608 remove_thread(THREAD_ID_CURRENT
);
611 void thread_wait(unsigned int thread_id
)
613 struct thread_entry
*current
= cores
[CURRENT_CORE
].running
;
614 struct thread_entry
*thread
= thread_id_entry(thread_id
);
616 if (thread_id
== THREAD_ID_CURRENT
||
617 (thread
->id
== thread_id
&& thread
->state
!= STATE_KILLED
))
619 current
->bqp
= &thread
->queue
;
620 block_thread(current
);
625 int thread_stack_usage(const struct thread_entry
*thread
)
631 /* Return name if one or ID if none */
632 void thread_get_name(char *buffer
, int size
,
633 struct thread_entry
*thread
)
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
);