diff options
Diffstat (limited to 'uisimulator/sdl/thread-sdl.c')
-rw-r--r-- | uisimulator/sdl/thread-sdl.c | 188 |
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 | ||
46 | struct thread_entry threads[MAXTHREADS]; /* Thread entries as in core */ | 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]; | ||
47 | static SDL_mutex *m; | 54 | static SDL_mutex *m; |
48 | static struct thread_entry *running; | 55 | static struct thread_entry *running; |
56 | static bool threads_exit = false; | ||
49 | 57 | ||
50 | extern long start_tick; | 58 | extern long start_tick; |
51 | 59 | ||
52 | void kill_sim_threads(void) | 60 | void 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 */ | ||
95 | extern void app_main(void *param); | ||
96 | static 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 */ | ||
115 | bool 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 | |||
72 | static int find_empty_thread_slot(void) | 157 | static 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 | ||
181 | int runthread(void *data) | 283 | int 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 | ||
256 | void block_thread_w_tmo(struct thread_entry **list, int ticks) | 375 | 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) | |||
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 | ||
274 | void wakeup_thread(struct thread_entry **list) | 396 | void wakeup_thread(struct thread_entry **list) |
@@ -293,33 +415,14 @@ void wakeup_thread(struct thread_entry **list) | |||
293 | 415 | ||
294 | void init_threads(void) | 416 | void 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 | ||
325 | void remove_thread(struct thread_entry *thread) | 428 | void 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); |