summaryrefslogtreecommitdiff
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 @@
17 * 17 *
18 ****************************************************************************/ 18 ****************************************************************************/
19 19
20#include <stdbool.h>
20#include <time.h> 21#include <time.h>
21#include <SDL.h> 22#include <SDL.h>
22#include <SDL_thread.h> 23#include <SDL_thread.h>
23#include <stdlib.h> 24#include <stdlib.h>
24#include <memory.h> 25#include <memory.h>
26#include <setjmp.h>
25#include "thread-sdl.h" 27#include "thread-sdl.h"
26#include "kernel.h" 28#include "kernel.h"
27#include "thread.h" 29#include "thread.h"
@@ -43,32 +45,115 @@ static char __name[32];
43#define THREAD_PANICF(str...) \ 45#define THREAD_PANICF(str...) \
44 ({ fprintf(stderr, str); exit(-1); }) 46 ({ fprintf(stderr, str); exit(-1); })
45 47
46struct thread_entry threads[MAXTHREADS]; /* Thread entries as in core */ 48/* Thread entries as in core */
49struct 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 */
53static jmp_buf thread_jmpbufs[MAXTHREADS];
47static SDL_mutex *m; 54static SDL_mutex *m;
48static struct thread_entry *running; 55static struct thread_entry *running;
56static bool threads_exit = false;
49 57
50extern long start_tick; 58extern long start_tick;
51 59
52void kill_sim_threads(void) 60void thread_sdl_shutdown(void)
53{ 61{
54 int i; 62 int i;
63 /* Take control */
55 SDL_LockMutex(m); 64 SDL_LockMutex(m);
65
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. */
70 threads_exit = true;
71
56 for (i = 0; i < MAXTHREADS; i++) 72 for (i = 0; i < MAXTHREADS; i++)
57 { 73 {
58 struct thread_entry *thread = &threads[i]; 74 struct thread_entry *thread = &threads[i];
59 if (thread->context.t != NULL) 75 if (thread->context.t != NULL)
60 { 76 {
77 /* Signal thread on delay or block */
78 SDL_Thread *t = thread->context.t;
79 SDL_CondSignal(thread->context.c);
80 SDL_UnlockMutex(m);
81 /* Wait for it to finish */
82 SDL_WaitThread(t, NULL);
83 /* Relock for next thread signal */
61 SDL_LockMutex(m); 84 SDL_LockMutex(m);
62 if (thread->statearg != STATE_RUNNING) 85 }
63 SDL_CondSignal(thread->context.c);
64 SDL_Delay(10);
65 SDL_KillThread(thread->context.t);
66 SDL_DestroyCond(thread->context.c);
67 }
68 } 86 }
87
88 SDL_UnlockMutex(m);
69 SDL_DestroyMutex(m); 89 SDL_DestroyMutex(m);
70} 90}
71 91
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
94 was used */
95extern void app_main(void *param);
96static int thread_sdl_app_main(void *param)
97{
98 SDL_LockMutex(m);
99 running = &threads[0];
100
101 /* Set the jump address for return */
102 if (setjmp(thread_jmpbufs[0]) == 0)
103 {
104 app_main(param);
105 /* should not ever be reached but... */
106 THREAD_PANICF("app_main returned!\n");
107 }
108
109 /* Unlock and exit */
110 SDL_UnlockMutex(m);
111 return 0;
112}
113
114/* Initialize SDL threading */
115bool thread_sdl_init(void *param)
116{
117 memset(threads, 0, sizeof(threads));
118
119 m = SDL_CreateMutex();
120
121 if (SDL_LockMutex(m) == -1)
122 {
123 fprintf(stderr, "Couldn't lock mutex\n");
124 return false;
125 }
126
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();
136
137 if (running->context.c == NULL)
138 {
139 fprintf(stderr, "Failed to create main condition variable\n");
140 return false;
141 }
142
143 running->context.t = SDL_CreateThread(thread_sdl_app_main, param);
144
145 if (running->context.t == NULL)
146 {
147 fprintf(stderr, "Failed to create main thread\n");
148 return false;
149 }
150
151 THREAD_SDL_DEBUGF("Main thread: %p\n", running);
152
153 SDL_UnlockMutex(m);
154 return true;
155}
156
72static int find_empty_thread_slot(void) 157static int find_empty_thread_slot(void)
73{ 158{
74 int n; 159 int n;
@@ -154,6 +239,9 @@ void switch_thread(bool save_context, struct thread_entry **blocked_list)
154 SDL_LockMutex(m); 239 SDL_LockMutex(m);
155 running = current; 240 running = current;
156 241
242 if (threads_exit)
243 remove_thread(NULL);
244
157 (void)save_context; (void)blocked_list; 245 (void)save_context; (void)blocked_list;
158} 246}
159 247
@@ -169,29 +257,57 @@ void sleep_thread(int ticks)
169 if (rem < 0) 257 if (rem < 0)
170 rem = 0; 258 rem = 0;
171 259
172 SDL_UnlockMutex(m); 260 rem = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
173 SDL_Delay((1000/HZ) * ticks + ((1000/HZ)-1) - rem); 261
174 SDL_LockMutex(m); 262 if (rem == 0)
263 {
264 /* Unlock and give up rest of quantum */
265 SDL_UnlockMutex(m);
266 SDL_Delay(0);
267 SDL_LockMutex(m);
268 }
269 else
270 {
271 /* These sleeps must be signalable for thread exit */
272 SDL_CondWaitTimeout(current->context.c, m, rem);
273 }
175 274
176 running = current; 275 running = current;
177 276
178 current->statearg = STATE_RUNNING; 277 current->statearg = STATE_RUNNING;
278
279 if (threads_exit)
280 remove_thread(NULL);
179} 281}
180 282
181int runthread(void *data) 283int runthread(void *data)
182{ 284{
183 struct thread_entry *current; 285 struct thread_entry *current;
286 jmp_buf *current_jmpbuf;
184 287
185 /* Cannot access thread variables before locking the mutex as the 288 /* Cannot access thread variables before locking the mutex as the
186 data structures may not be filled-in yet. */ 289 data structures may not be filled-in yet. */
187 SDL_LockMutex(m); 290 SDL_LockMutex(m);
188 running = (struct thread_entry *)data; 291 running = (struct thread_entry *)data;
189 current = running; 292 current = running;
190 current->context.start(); 293 current_jmpbuf = &thread_jmpbufs[running - threads];
294
295 /* Setup jump for exit */
296 if (setjmp(*current_jmpbuf) == 0)
297 {
298 /* Run the thread routine */
299 current->context.start();
300 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
301 current - threads, THREAD_SDL_GET_NAME(current));
302 /* Thread routine returned - suicide */
303 remove_thread(NULL);
304 }
305 else
306 {
307 /* Unlock and exit */
308 SDL_UnlockMutex(m);
309 }
191 310
192 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
193 current - threads, THREAD_SDL_GET_NAME(current));
194 remove_thread(NULL);
195 return 0; 311 return 0;
196} 312}
197 313
@@ -251,6 +367,9 @@ void block_thread(struct thread_entry **list)
251 367
252 SDL_CondWait(thread->context.c, m); 368 SDL_CondWait(thread->context.c, m);
253 running = thread; 369 running = thread;
370
371 if (threads_exit)
372 remove_thread(NULL);
254} 373}
255 374
256void block_thread_w_tmo(struct thread_entry **list, int ticks) 375void 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)
269 remove_from_list(list, thread); 388 remove_from_list(list, thread);
270 thread->statearg = STATE_RUNNING; 389 thread->statearg = STATE_RUNNING;
271 } 390 }
391
392 if (threads_exit)
393 remove_thread(NULL);
272} 394}
273 395
274void wakeup_thread(struct thread_entry **list) 396void wakeup_thread(struct thread_entry **list)
@@ -293,33 +415,14 @@ void wakeup_thread(struct thread_entry **list)
293 415
294void init_threads(void) 416void init_threads(void)
295{ 417{
296 int slot; 418 /* Main thread is already initialized */
297 419 if (running != &threads[0])
298 m = SDL_CreateMutex();
299
300 memset(threads, 0, sizeof(threads));
301
302 slot = find_empty_thread_slot();
303 if (slot >= MAXTHREADS)
304 { 420 {
305 THREAD_PANICF("Couldn't find slot for main thread.\n"); 421 THREAD_PANICF("Wrong main thread in init_threads: %p\n", running);
306 } 422 }
307 423
308 threads[slot].stack = " ";
309 threads[slot].stack_size = 8;
310 threads[slot].name = "main";
311 threads[slot].statearg = STATE_RUNNING;
312 threads[slot].context.t = gui_thread;
313 threads[slot].context.c = SDL_CreateCond();
314
315 running = &threads[slot];
316
317 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n", 424 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
318 slot, THREAD_SDL_GET_NAME(&threads[slot])); 425 0, THREAD_SDL_GET_NAME(&threads[0]));
319
320 if (SDL_LockMutex(m) == -1) {
321 THREAD_PANICF("Couldn't lock mutex\n");
322 }
323} 426}
324 427
325void remove_thread(struct thread_entry *thread) 428void remove_thread(struct thread_entry *thread)
@@ -338,10 +441,7 @@ void remove_thread(struct thread_entry *thread)
338 thread->context.t = NULL; 441 thread->context.t = NULL;
339 442
340 if (thread != current) 443 if (thread != current)
341 { 444 SDL_CondSignal(c);
342 if (thread->statearg != STATE_RUNNING)
343 SDL_CondSignal(c);
344 }
345 445
346 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n", 446 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
347 thread - threads, THREAD_SDL_GET_NAME(thread)); 447 thread - threads, THREAD_SDL_GET_NAME(thread));
@@ -352,7 +452,9 @@ void remove_thread(struct thread_entry *thread)
352 452
353 if (thread == current) 453 if (thread == current)
354 { 454 {
355 SDL_UnlockMutex(m); 455 /* Do a graceful exit - perform the longjmp back into the thread
456 function to return */
457 longjmp(thread_jmpbufs[current - threads], 1);
356 } 458 }
357 459
358 SDL_KillThread(t); 460 SDL_KillThread(t);