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"
33 /* Define this as 1 to show informational messages that are not errors. */
34 #define THREAD_SDL_DEBUGF_ENABLED 0
36 #if THREAD_SDL_DEBUGF_ENABLED
37 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
38 static char __name
[32];
39 #define THREAD_SDL_GET_NAME(thread) \
40 ({ thread_get_name(__name, sizeof(__name)/sizeof(__name[0]), thread); __name; })
42 #define THREAD_SDL_DEBUGF(...)
43 #define THREAD_SDL_GET_NAME(thread)
46 #define THREAD_PANICF(str...) \
47 ({ fprintf(stderr, str); exit(-1); })
49 /* Thread/core entries as in rockbox core */
50 struct core_entry cores
[NUM_CORES
];
51 struct thread_entry threads
[MAXTHREADS
];
52 /* Jump buffers for graceful exit - kernel threads don't stay neatly
53 * in their start routines responding to messages so this is the only
54 * way to get them back in there so they may exit */
55 static jmp_buf thread_jmpbufs
[MAXTHREADS
];
57 static struct thread_entry
*running
;
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_CondSignal(thread
->context
.c
);
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 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 memset(threads
, 0, sizeof(threads
));
121 m
= SDL_CreateMutex();
123 if (SDL_LockMutex(m
) == -1)
125 fprintf(stderr
, "Couldn't lock mutex\n");
129 /* Slot 0 is reserved for the main thread - initialize it here and
130 then create the SDL thread - it is possible to have a quick, early
131 shutdown try to access the structure. */
132 running
= &threads
[0];
133 running
->stack
= " ";
134 running
->stack_size
= 8;
135 running
->name
= "main";
136 running
->state
= STATE_RUNNING
;
137 running
->context
.c
= SDL_CreateCond();
139 if (running
->context
.c
== NULL
)
141 fprintf(stderr
, "Failed to create main condition variable\n");
145 running
->context
.t
= SDL_CreateThread(thread_sdl_app_main
, param
);
147 if (running
->context
.t
== NULL
)
149 fprintf(stderr
, "Failed to create main thread\n");
153 THREAD_SDL_DEBUGF("Main thread: %p\n", running
);
159 /* A way to yield and leave the threading system for extended periods */
160 void thread_sdl_thread_lock(void *me
)
163 running
= (struct thread_entry
*)me
;
169 void * thread_sdl_thread_unlock(void)
171 struct thread_entry
*current
= running
;
176 static int find_empty_thread_slot(void)
180 for (n
= 0; n
< MAXTHREADS
; n
++)
182 int state
= threads
[n
].state
;
184 if (state
== STATE_KILLED
)
191 static void add_to_list_l(struct thread_entry
**list
,
192 struct thread_entry
*thread
)
196 /* Insert into unoccupied list */
197 thread
->l
.next
= thread
;
198 thread
->l
.prev
= thread
;
204 thread
->l
.next
= *list
;
205 thread
->l
.prev
= (*list
)->l
.prev
;
206 thread
->l
.prev
->l
.next
= thread
;
207 (*list
)->l
.prev
= thread
;
211 static void remove_from_list_l(struct thread_entry
**list
,
212 struct thread_entry
*thread
)
214 if (thread
== thread
->l
.next
)
223 /* List becomes next item */
224 *list
= thread
->l
.next
;
227 /* Fix links to jump over the removed entry. */
228 thread
->l
.prev
->l
.next
= thread
->l
.next
;
229 thread
->l
.next
->l
.prev
= thread
->l
.prev
;
232 static inline void run_blocking_ops(void)
237 struct thread_entry
*thread_get_current(void)
242 void switch_thread(struct thread_entry
*old
)
244 struct thread_entry
*current
= running
;
247 /* Any other thread waiting already will get it first */
257 void sleep_thread(int ticks
)
259 struct thread_entry
*current
;
263 current
->state
= STATE_SLEEPING
;
265 rem
= (SDL_GetTicks() - start_tick
) % (1000/HZ
);
269 rem
= (1000/HZ
) * ticks
+ ((1000/HZ
)-1) - rem
;
273 /* Unlock and give up rest of quantum */
280 /* These sleeps must be signalable for thread exit */
281 SDL_CondWaitTimeout(current
->context
.c
, m
, rem
);
286 current
->state
= STATE_RUNNING
;
292 int runthread(void *data
)
294 struct thread_entry
*current
;
295 jmp_buf *current_jmpbuf
;
297 /* Cannot access thread variables before locking the mutex as the
298 data structures may not be filled-in yet. */
300 running
= (struct thread_entry
*)data
;
302 current_jmpbuf
= &thread_jmpbufs
[running
- threads
];
304 /* Setup jump for exit */
305 if (setjmp(*current_jmpbuf
) == 0)
307 /* Run the thread routine */
308 if (current
->state
== STATE_FROZEN
)
310 SDL_CondWait(current
->context
.c
, m
);
317 current
->context
.start();
318 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
319 current
- threads
, THREAD_SDL_GET_NAME(current
));
320 /* Thread routine returned - suicide */
327 /* Unlock and exit */
335 create_thread(void (*function
)(void), void* stack
, int stack_size
,
336 unsigned flags
, const char *name
)
338 /** Avoid compiler warnings */
343 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name
? name
: "");
345 slot
= find_empty_thread_slot();
346 if (slot
>= MAXTHREADS
)
348 DEBUGF("Failed to find thread slot\n");
352 cond
= SDL_CreateCond();
355 DEBUGF("Failed to create condition variable\n");
359 t
= SDL_CreateThread(runthread
, &threads
[slot
]);
362 DEBUGF("Failed to create SDL thread\n");
363 SDL_DestroyCond(cond
);
367 threads
[slot
].stack
= stack
;
368 threads
[slot
].stack_size
= stack_size
;
369 threads
[slot
].name
= name
;
370 threads
[slot
].state
= (flags
& CREATE_THREAD_FROZEN
) ?
371 STATE_FROZEN
: STATE_RUNNING
;
372 threads
[slot
].context
.start
= function
;
373 threads
[slot
].context
.t
= t
;
374 threads
[slot
].context
.c
= cond
;
376 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
377 slot
, THREAD_SDL_GET_NAME(&threads
[slot
]));
379 return &threads
[slot
];
382 void _block_thread(struct thread_queue
*tq
)
384 struct thread_entry
*thread
= running
;
386 thread
->state
= STATE_BLOCKED
;
388 add_to_list_l(&tq
->queue
, thread
);
392 SDL_CondWait(thread
->context
.c
, m
);
399 void block_thread_w_tmo(struct thread_queue
*tq
, int ticks
)
401 struct thread_entry
*thread
= running
;
403 thread
->state
= STATE_BLOCKED_W_TMO
;
405 add_to_list_l(&tq
->queue
, thread
);
409 SDL_CondWaitTimeout(thread
->context
.c
, m
, (1000/HZ
) * ticks
);
412 if (thread
->state
== STATE_BLOCKED_W_TMO
)
415 remove_from_list_l(&tq
->queue
, thread
);
416 thread
->state
= STATE_RUNNING
;
423 struct thread_entry
* _wakeup_thread(struct thread_queue
*tq
)
425 struct thread_entry
*thread
= tq
->queue
;
432 switch (thread
->state
)
435 case STATE_BLOCKED_W_TMO
:
436 remove_from_list_l(&tq
->queue
, thread
);
437 thread
->state
= STATE_RUNNING
;
438 SDL_CondSignal(thread
->context
.c
);
445 void thread_thaw(struct thread_entry
*thread
)
447 if (thread
->state
== STATE_FROZEN
)
449 thread
->state
= STATE_RUNNING
;
450 SDL_CondSignal(thread
->context
.c
);
454 void init_threads(void)
456 /* Main thread is already initialized */
457 if (running
!= &threads
[0])
459 THREAD_PANICF("Wrong main thread in init_threads: %p\n", running
);
462 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
463 0, THREAD_SDL_GET_NAME(&threads
[0]));
466 void remove_thread(struct thread_entry
*thread
)
468 struct thread_entry
*current
= running
;
472 int oldlevel
= set_irq_level(HIGHEST_IRQ_LEVEL
);
479 t
= thread
->context
.t
;
480 c
= thread
->context
.c
;
481 thread
->context
.t
= NULL
;
483 if (thread
!= current
)
485 switch (thread
->state
)
488 case STATE_BLOCKED_W_TMO
:
489 /* Remove thread from object it's waiting on */
490 remove_from_list_l(&thread
->bqp
->queue
, thread
);
497 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
498 thread
- threads
, THREAD_SDL_GET_NAME(thread
));
500 thread_queue_wake_no_listlock(&thread
->queue
);
501 thread
->state
= STATE_KILLED
;
505 if (thread
== current
)
507 /* Do a graceful exit - perform the longjmp back into the thread
508 function to return */
509 set_irq_level(oldlevel
);
510 longjmp(thread_jmpbufs
[current
- threads
], 1);
514 set_irq_level(oldlevel
);
517 void thread_wait(struct thread_entry
*thread
)
522 if (thread
->state
!= STATE_KILLED
)
524 block_thread_no_listlock(&thread
->queue
);
528 int thread_stack_usage(const struct thread_entry
*thread
)
534 unsigned thread_get_status(const struct thread_entry
*thread
)
536 return thread
->state
;
539 /* Return name if one or ID if none */
540 void thread_get_name(char *buffer
, int size
,
541 struct thread_entry
*thread
)
550 /* Display thread name if one or ID if none */
551 bool named
= thread
->name
&& *thread
->name
;
552 const char *fmt
= named
? "%s" : "%08lX";
553 intptr_t name
= named
?
554 (intptr_t)thread
->name
: (intptr_t)thread
;
555 snprintf(buffer
, size
, fmt
, name
);