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 "thread-sdl.h"
32 /* Define this as 1 to show informational messages that are not errors. */
33 #define THREAD_SDL_DEBUGF_ENABLED 0
35 #if THREAD_SDL_DEBUGF_ENABLED
36 #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
37 static char __name
[32];
38 #define THREAD_SDL_GET_NAME(thread) \
39 ({ thread_get_name(__name, sizeof(__name)/sizeof(__name[0]), thread); __name; })
41 #define THREAD_SDL_DEBUGF(...)
42 #define THREAD_SDL_GET_NAME(thread)
45 #define THREAD_PANICF(str...) \
46 ({ fprintf(stderr, str); exit(-1); })
48 /* Thread entries as in core */
49 struct thread_entry threads
[MAXTHREADS
];
50 /* Jump buffers for graceful exit - kernel threads don't stay neatly
51 * in their start routines responding to messages so this is the only
52 * way to get them back in there so they may exit */
53 static jmp_buf thread_jmpbufs
[MAXTHREADS
];
55 static struct thread_entry
*running
;
56 static bool threads_exit
= false;
58 extern long start_tick
;
60 void thread_sdl_shutdown(void)
66 /* Tell all threads jump back to their start routines, unlock and exit
67 gracefully - we'll check each one in turn for it's status. Threads
68 _could_ terminate via remove_thread or multiple threads could exit
69 on each unlock but that is safe. */
72 for (i
= 0; i
< MAXTHREADS
; i
++)
74 struct thread_entry
*thread
= &threads
[i
];
75 if (thread
->context
.t
!= NULL
)
77 /* Signal thread on delay or block */
78 SDL_Thread
*t
= thread
->context
.t
;
79 SDL_CondSignal(thread
->context
.c
);
81 /* Wait for it to finish */
82 SDL_WaitThread(t
, NULL
);
83 /* Relock for next thread signal */
92 /* Do main thread creation in this file scope to avoid the need to double-
93 return to a prior call-level which would be unaware of the fact setjmp
95 extern void app_main(void *param
);
96 static int thread_sdl_app_main(void *param
)
99 running
= &threads
[0];
101 /* Set the jump address for return */
102 if (setjmp(thread_jmpbufs
[0]) == 0)
105 /* should not ever be reached but... */
106 THREAD_PANICF("app_main returned!\n");
109 /* Unlock and exit */
114 /* Initialize SDL threading */
115 bool thread_sdl_init(void *param
)
117 memset(threads
, 0, sizeof(threads
));
119 m
= SDL_CreateMutex();
121 if (SDL_LockMutex(m
) == -1)
123 fprintf(stderr
, "Couldn't lock mutex\n");
127 /* Slot 0 is reserved for the main thread - initialize it here and
128 then create the SDL thread - it is possible to have a quick, early
129 shutdown try to access the structure. */
130 running
= &threads
[0];
131 running
->stack
= " ";
132 running
->stack_size
= 8;
133 running
->name
= "main";
134 running
->statearg
= STATE_RUNNING
;
135 running
->context
.c
= SDL_CreateCond();
137 if (running
->context
.c
== NULL
)
139 fprintf(stderr
, "Failed to create main condition variable\n");
143 running
->context
.t
= SDL_CreateThread(thread_sdl_app_main
, param
);
145 if (running
->context
.t
== NULL
)
147 fprintf(stderr
, "Failed to create main thread\n");
151 THREAD_SDL_DEBUGF("Main thread: %p\n", running
);
157 static int find_empty_thread_slot(void)
161 for (n
= 0; n
< MAXTHREADS
; n
++)
163 if (threads
[n
].name
== NULL
)
170 static void add_to_list(struct thread_entry
**list
,
171 struct thread_entry
*thread
)
175 /* Insert into unoccupied list */
176 thread
->next
= thread
;
177 thread
->prev
= thread
;
183 thread
->next
= *list
;
184 thread
->prev
= (*list
)->prev
;
185 thread
->prev
->next
= thread
;
186 (*list
)->prev
= thread
;
190 static void remove_from_list(struct thread_entry
**list
,
191 struct thread_entry
*thread
)
193 if (thread
== thread
->next
)
202 /* List becomes next item */
203 *list
= thread
->next
;
206 /* Fix links to jump over the removed entry. */
207 thread
->prev
->next
= thread
->next
;
208 thread
->next
->prev
= thread
->prev
;
211 struct thread_entry
*thread_get_current(void)
216 void thread_sdl_lock(void)
221 void thread_sdl_unlock(void)
226 void switch_thread(bool save_context
, struct thread_entry
**blocked_list
)
228 struct thread_entry
*current
= running
;
231 /* Any other thread waiting already will get it first */
238 (void)save_context
; (void)blocked_list
;
241 void sleep_thread(int ticks
)
243 struct thread_entry
*current
;
247 current
->statearg
= STATE_SLEEPING
;
249 rem
= (SDL_GetTicks() - start_tick
) % (1000/HZ
);
253 rem
= (1000/HZ
) * ticks
+ ((1000/HZ
)-1) - rem
;
257 /* Unlock and give up rest of quantum */
264 /* These sleeps must be signalable for thread exit */
265 SDL_CondWaitTimeout(current
->context
.c
, m
, rem
);
270 current
->statearg
= STATE_RUNNING
;
276 int runthread(void *data
)
278 struct thread_entry
*current
;
279 jmp_buf *current_jmpbuf
;
281 /* Cannot access thread variables before locking the mutex as the
282 data structures may not be filled-in yet. */
284 running
= (struct thread_entry
*)data
;
286 current_jmpbuf
= &thread_jmpbufs
[running
- threads
];
288 /* Setup jump for exit */
289 if (setjmp(*current_jmpbuf
) == 0)
291 /* Run the thread routine */
292 current
->context
.start();
293 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
294 current
- threads
, THREAD_SDL_GET_NAME(current
));
295 /* Thread routine returned - suicide */
300 /* Unlock and exit */
308 create_thread(void (*function
)(void), void* stack
, int stack_size
,
311 /** Avoid compiler warnings */
316 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name
? name
: "");
318 slot
= find_empty_thread_slot();
319 if (slot
>= MAXTHREADS
)
321 DEBUGF("Failed to find thread slot\n");
325 cond
= SDL_CreateCond();
328 DEBUGF("Failed to create condition variable\n");
332 t
= SDL_CreateThread(runthread
, &threads
[slot
]);
335 DEBUGF("Failed to create SDL thread\n");
336 SDL_DestroyCond(cond
);
340 threads
[slot
].stack
= stack
;
341 threads
[slot
].stack_size
= stack_size
;
342 threads
[slot
].name
= name
;
343 threads
[slot
].statearg
= STATE_RUNNING
;
344 threads
[slot
].context
.start
= function
;
345 threads
[slot
].context
.t
= t
;
346 threads
[slot
].context
.c
= cond
;
348 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
349 slot
, THREAD_SDL_GET_NAME(&threads
[slot
]));
351 return &threads
[slot
];
354 void block_thread(struct thread_entry
**list
)
356 struct thread_entry
*thread
= running
;
358 thread
->statearg
= STATE_BLOCKED
;
359 add_to_list(list
, thread
);
361 SDL_CondWait(thread
->context
.c
, m
);
368 void block_thread_w_tmo(struct thread_entry
**list
, int ticks
)
370 struct thread_entry
*thread
= running
;
372 thread
->statearg
= STATE_BLOCKED_W_TMO
;
373 add_to_list(list
, thread
);
375 SDL_CondWaitTimeout(thread
->context
.c
, m
, (1000/HZ
) * ticks
);
378 if (thread
->statearg
== STATE_BLOCKED_W_TMO
)
381 remove_from_list(list
, thread
);
382 thread
->statearg
= STATE_RUNNING
;
389 void wakeup_thread(struct thread_entry
**list
)
391 struct thread_entry
*thread
= *list
;
398 switch (thread
->statearg
)
401 case STATE_BLOCKED_W_TMO
:
402 remove_from_list(list
, thread
);
403 thread
->statearg
= STATE_RUNNING
;
404 SDL_CondSignal(thread
->context
.c
);
409 void init_threads(void)
411 /* Main thread is already initialized */
412 if (running
!= &threads
[0])
414 THREAD_PANICF("Wrong main thread in init_threads: %p\n", running
);
417 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
418 0, THREAD_SDL_GET_NAME(&threads
[0]));
421 void remove_thread(struct thread_entry
*thread
)
423 struct thread_entry
*current
= running
;
432 t
= thread
->context
.t
;
433 c
= thread
->context
.c
;
434 thread
->context
.t
= NULL
;
436 if (thread
!= current
)
439 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
440 thread
- threads
, THREAD_SDL_GET_NAME(thread
));
446 if (thread
== current
)
448 /* Do a graceful exit - perform the longjmp back into the thread
449 function to return */
450 longjmp(thread_jmpbufs
[current
- threads
], 1);
456 int thread_stack_usage(const struct thread_entry
*thread
)
462 int thread_get_status(const struct thread_entry
*thread
)
464 return thread
->statearg
;
467 /* Return name if one or ID if none */
468 void thread_get_name(char *buffer
, int size
,
469 struct thread_entry
*thread
)
478 /* Display thread name if one or ID if none */
479 bool named
= thread
->name
&& *thread
->name
;
480 const char *fmt
= named
? "%s" : "%08lX";
481 intptr_t name
= named
?
482 (intptr_t)thread
->name
: (intptr_t)thread
;
483 snprintf(buffer
, size
, fmt
, name
);