summaryrefslogtreecommitdiffstats
path: root/uisimulator/sdl/thread-sdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'uisimulator/sdl/thread-sdl.c')
-rw-r--r--uisimulator/sdl/thread-sdl.c188
1 files changed, 145 insertions, 43 deletions
diff --git a/uisimulator/sdl/thread-sdl.c b/uisimulator/sdl/thread-sdl.c
index d146cfb64b..ea8bb39360 100644
--- a/uisimulator/sdl/thread-sdl.c
+++ b/uisimulator/sdl/thread-sdl.c
@@ -17,11 +17,13 @@
*
****************************************************************************/
+#include <stdbool.h>
#include <time.h>
#include <SDL.h>
#include <SDL_thread.h>
#include <stdlib.h>
#include <memory.h>
+#include <setjmp.h>
#include "thread-sdl.h"
#include "kernel.h"
#include "thread.h"
@@ -43,32 +45,115 @@ static char __name[32];
#define THREAD_PANICF(str...) \
({ fprintf(stderr, str); exit(-1); })
-struct thread_entry threads[MAXTHREADS]; /* Thread entries as in core */
+/* Thread entries as in core */
+struct thread_entry threads[MAXTHREADS];
+/* Jump buffers for graceful exit - kernel threads don't stay neatly
+ * in their start routines responding to messages so this is the only
+ * way to get them back in there so they may exit */
+static jmp_buf thread_jmpbufs[MAXTHREADS];
static SDL_mutex *m;
static struct thread_entry *running;
+static bool threads_exit = false;
extern long start_tick;
-void kill_sim_threads(void)
+void thread_sdl_shutdown(void)
{
int i;
+ /* Take control */
SDL_LockMutex(m);
+
+ /* Tell all threads jump back to their start routines, unlock and exit
+ gracefully - we'll check each one in turn for it's status. Threads
+ _could_ terminate via remove_thread or multiple threads could exit
+ on each unlock but that is safe. */
+ threads_exit = true;
+
for (i = 0; i < MAXTHREADS; i++)
{
struct thread_entry *thread = &threads[i];
if (thread->context.t != NULL)
{
+ /* Signal thread on delay or block */
+ SDL_Thread *t = thread->context.t;
+ SDL_CondSignal(thread->context.c);
+ SDL_UnlockMutex(m);
+ /* Wait for it to finish */
+ SDL_WaitThread(t, NULL);
+ /* Relock for next thread signal */
SDL_LockMutex(m);
- if (thread->statearg != STATE_RUNNING)
- SDL_CondSignal(thread->context.c);
- SDL_Delay(10);
- SDL_KillThread(thread->context.t);
- SDL_DestroyCond(thread->context.c);
- }
+ }
}
+
+ SDL_UnlockMutex(m);
SDL_DestroyMutex(m);
}
+/* Do main thread creation in this file scope to avoid the need to double-
+ return to a prior call-level which would be unaware of the fact setjmp
+ was used */
+extern void app_main(void *param);
+static int thread_sdl_app_main(void *param)
+{
+ SDL_LockMutex(m);
+ running = &threads[0];
+
+ /* Set the jump address for return */
+ if (setjmp(thread_jmpbufs[0]) == 0)
+ {
+ app_main(param);
+ /* should not ever be reached but... */
+ THREAD_PANICF("app_main returned!\n");
+ }
+
+ /* Unlock and exit */
+ SDL_UnlockMutex(m);
+ return 0;
+}
+
+/* Initialize SDL threading */
+bool thread_sdl_init(void *param)
+{
+ memset(threads, 0, sizeof(threads));
+
+ m = SDL_CreateMutex();
+
+ if (SDL_LockMutex(m) == -1)
+ {
+ fprintf(stderr, "Couldn't lock mutex\n");
+ return false;
+ }
+
+ /* Slot 0 is reserved for the main thread - initialize it here and
+ then create the SDL thread - it is possible to have a quick, early
+ shutdown try to access the structure. */
+ running = &threads[0];
+ running->stack = " ";
+ running->stack_size = 8;
+ running->name = "main";
+ running->statearg = STATE_RUNNING;
+ running->context.c = SDL_CreateCond();
+
+ if (running->context.c == NULL)
+ {
+ fprintf(stderr, "Failed to create main condition variable\n");
+ return false;
+ }
+
+ running->context.t = SDL_CreateThread(thread_sdl_app_main, param);
+
+ if (running->context.t == NULL)
+ {
+ fprintf(stderr, "Failed to create main thread\n");
+ return false;
+ }
+
+ THREAD_SDL_DEBUGF("Main thread: %p\n", running);
+
+ SDL_UnlockMutex(m);
+ return true;
+}
+
static int find_empty_thread_slot(void)
{
int n;
@@ -154,6 +239,9 @@ void switch_thread(bool save_context, struct thread_entry **blocked_list)
SDL_LockMutex(m);
running = current;
+ if (threads_exit)
+ remove_thread(NULL);
+
(void)save_context; (void)blocked_list;
}
@@ -169,29 +257,57 @@ void sleep_thread(int ticks)
if (rem < 0)
rem = 0;
- SDL_UnlockMutex(m);
- SDL_Delay((1000/HZ) * ticks + ((1000/HZ)-1) - rem);
- SDL_LockMutex(m);
+ rem = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
+
+ if (rem == 0)
+ {
+ /* Unlock and give up rest of quantum */
+ SDL_UnlockMutex(m);
+ SDL_Delay(0);
+ SDL_LockMutex(m);
+ }
+ else
+ {
+ /* These sleeps must be signalable for thread exit */
+ SDL_CondWaitTimeout(current->context.c, m, rem);
+ }
running = current;
current->statearg = STATE_RUNNING;
+
+ if (threads_exit)
+ remove_thread(NULL);
}
int runthread(void *data)
{
struct thread_entry *current;
+ jmp_buf *current_jmpbuf;
/* Cannot access thread variables before locking the mutex as the
data structures may not be filled-in yet. */
SDL_LockMutex(m);
running = (struct thread_entry *)data;
current = running;
- current->context.start();
+ current_jmpbuf = &thread_jmpbufs[running - threads];
+
+ /* Setup jump for exit */
+ if (setjmp(*current_jmpbuf) == 0)
+ {
+ /* Run the thread routine */
+ current->context.start();
+ THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
+ current - threads, THREAD_SDL_GET_NAME(current));
+ /* Thread routine returned - suicide */
+ remove_thread(NULL);
+ }
+ else
+ {
+ /* Unlock and exit */
+ SDL_UnlockMutex(m);
+ }
- THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
- current - threads, THREAD_SDL_GET_NAME(current));
- remove_thread(NULL);
return 0;
}
@@ -251,6 +367,9 @@ void block_thread(struct thread_entry **list)
SDL_CondWait(thread->context.c, m);
running = thread;
+
+ if (threads_exit)
+ remove_thread(NULL);
}
void block_thread_w_tmo(struct thread_entry **list, int ticks)
@@ -269,6 +388,9 @@ void block_thread_w_tmo(struct thread_entry **list, int ticks)
remove_from_list(list, thread);
thread->statearg = STATE_RUNNING;
}
+
+ if (threads_exit)
+ remove_thread(NULL);
}
void wakeup_thread(struct thread_entry **list)
@@ -293,33 +415,14 @@ void wakeup_thread(struct thread_entry **list)
void init_threads(void)
{
- int slot;
-
- m = SDL_CreateMutex();
-
- memset(threads, 0, sizeof(threads));
-
- slot = find_empty_thread_slot();
- if (slot >= MAXTHREADS)
+ /* Main thread is already initialized */
+ if (running != &threads[0])
{
- THREAD_PANICF("Couldn't find slot for main thread.\n");
+ THREAD_PANICF("Wrong main thread in init_threads: %p\n", running);
}
- threads[slot].stack = " ";
- threads[slot].stack_size = 8;
- threads[slot].name = "main";
- threads[slot].statearg = STATE_RUNNING;
- threads[slot].context.t = gui_thread;
- threads[slot].context.c = SDL_CreateCond();
-
- running = &threads[slot];
-
THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
- slot, THREAD_SDL_GET_NAME(&threads[slot]));
-
- if (SDL_LockMutex(m) == -1) {
- THREAD_PANICF("Couldn't lock mutex\n");
- }
+ 0, THREAD_SDL_GET_NAME(&threads[0]));
}
void remove_thread(struct thread_entry *thread)
@@ -338,10 +441,7 @@ void remove_thread(struct thread_entry *thread)
thread->context.t = NULL;
if (thread != current)
- {
- if (thread->statearg != STATE_RUNNING)
- SDL_CondSignal(c);
- }
+ SDL_CondSignal(c);
THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
thread - threads, THREAD_SDL_GET_NAME(thread));
@@ -352,7 +452,9 @@ void remove_thread(struct thread_entry *thread)
if (thread == current)
{
- SDL_UnlockMutex(m);
+ /* Do a graceful exit - perform the longjmp back into the thread
+ function to return */
+ longjmp(thread_jmpbufs[current - threads], 1);
}
SDL_KillThread(t);