summaryrefslogtreecommitdiff
path: root/uisimulator/sdl/thread-sdl.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-09-10 03:49:12 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-09-10 03:49:12 +0000
commitc4a7631eb9235f72de569f5e578620c6e2bc6818 (patch)
tree1e67525787351b2637fab8a4fe0ae140398c9219 /uisimulator/sdl/thread-sdl.c
parent2e305c6381c72aaabc6d0f92459b32d8939691fb (diff)
downloadrockbox-c4a7631eb9235f72de569f5e578620c6e2bc6818.tar.gz
rockbox-c4a7631eb9235f72de569f5e578620c6e2bc6818.zip
UISIMULATOR: Do a graceful shutdown of all threads and avoid (mostly lockup) problems caused by not worrying about states. Have rockbox objects initialized only by rockbox threads save for the main 'gui' thread which is a needed exception.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14660 a1c6a512-1295-4272-9138-f99709370657
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);