summaryrefslogtreecommitdiff
path: root/uisimulator
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
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')
-rw-r--r--uisimulator/common/io.c19
-rw-r--r--uisimulator/sdl/thread-sdl.c188
-rw-r--r--uisimulator/sdl/thread-sdl.h3
-rw-r--r--uisimulator/sdl/uisdl.c33
4 files changed, 175 insertions, 68 deletions
diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c
index 463bbc68d9..3ad93dc382 100644
--- a/uisimulator/common/io.c
+++ b/uisimulator/common/io.c
@@ -233,31 +233,29 @@ static int io_thread(void *data)
233 (void)data; 233 (void)data;
234} 234}
235 235
236void sim_io_init(void) 236bool sim_io_init(void)
237{ 237{
238 mutex_init(&io.sim_mutex);
239
240 io.ready = 0; 238 io.ready = 0;
241 239
242 io.m = SDL_CreateMutex(); 240 io.m = SDL_CreateMutex();
243 if (io.m == NULL) 241 if (io.m == NULL)
244 { 242 {
245 fprintf(stderr, "Failed to create IO mutex\n"); 243 fprintf(stderr, "Failed to create IO mutex\n");
246 exit(-1); 244 return false;
247 } 245 }
248 246
249 io.c = SDL_CreateCond(); 247 io.c = SDL_CreateCond();
250 if (io.c == NULL) 248 if (io.c == NULL)
251 { 249 {
252 fprintf(stderr, "Failed to create IO cond\n"); 250 fprintf(stderr, "Failed to create IO cond\n");
253 exit(-1); 251 return false;
254 } 252 }
255 253
256 io.t = SDL_CreateThread(io_thread, NULL); 254 io.t = SDL_CreateThread(io_thread, NULL);
257 if (io.t == NULL) 255 if (io.t == NULL)
258 { 256 {
259 fprintf(stderr, "Failed to create IO thread\n"); 257 fprintf(stderr, "Failed to create IO thread\n");
260 exit(-1); 258 return false;
261 } 259 }
262 260
263 /* Wait for IO thread to lock mutex */ 261 /* Wait for IO thread to lock mutex */
@@ -268,6 +266,15 @@ void sim_io_init(void)
268 SDL_LockMutex(io.m); 266 SDL_LockMutex(io.m);
269 /* Free it for another thread */ 267 /* Free it for another thread */
270 SDL_UnlockMutex(io.m); 268 SDL_UnlockMutex(io.m);
269
270 return true;
271}
272
273int ata_init(void)
274{
275 /* Initialize the rockbox kernel objects on a rockbox thread */
276 mutex_init(&io.sim_mutex);
277 return 1;
271} 278}
272 279
273void sim_io_shutdown(void) 280void sim_io_shutdown(void)
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);
diff --git a/uisimulator/sdl/thread-sdl.h b/uisimulator/sdl/thread-sdl.h
index 90dffd6806..3739130f97 100644
--- a/uisimulator/sdl/thread-sdl.h
+++ b/uisimulator/sdl/thread-sdl.h
@@ -23,7 +23,8 @@
23#include "SDL_thread.h" 23#include "SDL_thread.h"
24 24
25extern SDL_Thread *gui_thread; /* The "main" thread */ 25extern SDL_Thread *gui_thread; /* The "main" thread */
26void kill_sim_threads(); /* Kill all the rockbox sim threads */ 26bool thread_sdl_init(void *param); /* Init the sim threading API - thread created calls app_main */
27void thread_sdl_shutdown(void); /* Shut down all kernel threads gracefully */
27void thread_sdl_lock(void); /* Sync with SDL threads */ 28void thread_sdl_lock(void); /* Sync with SDL threads */
28void thread_sdl_unlock(void); /* Sync with SDL threads */ 29void thread_sdl_unlock(void); /* Sync with SDL threads */
29 30
diff --git a/uisimulator/sdl/uisdl.c b/uisimulator/sdl/uisdl.c
index fdd40beb23..eada01f99a 100644
--- a/uisimulator/sdl/uisdl.c
+++ b/uisimulator/sdl/uisdl.c
@@ -19,6 +19,7 @@
19 19
20#include <stdlib.h> 20#include <stdlib.h>
21#include <string.h> 21#include <string.h>
22#include <setjmp.h>
22#include "autoconf.h" 23#include "autoconf.h"
23#include "button.h" 24#include "button.h"
24#include "thread.h" 25#include "thread.h"
@@ -41,7 +42,7 @@
41extern void app_main (void *); /* mod entry point */ 42extern void app_main (void *); /* mod entry point */
42extern void new_key(int key); 43extern void new_key(int key);
43extern void sim_tick_tasks(void); 44extern void sim_tick_tasks(void);
44extern void sim_io_init(void); 45extern bool sim_io_init(void);
45extern void sim_io_shutdown(void); 46extern void sim_io_shutdown(void);
46 47
47void button_event(int key, bool pressed); 48void button_event(int key, bool pressed);
@@ -49,7 +50,6 @@ void button_event(int key, bool pressed);
49SDL_Surface *gui_surface; 50SDL_Surface *gui_surface;
50bool background = false; /* Don't use backgrounds by default */ 51bool background = false; /* Don't use backgrounds by default */
51 52
52SDL_Thread *gui_thread;
53SDL_TimerID tick_timer_id; 53SDL_TimerID tick_timer_id;
54 54
55bool lcd_display_redraw = true; /* Used for player simulator */ 55bool lcd_display_redraw = true; /* Used for player simulator */
@@ -170,21 +170,13 @@ bool gui_startup(void)
170bool gui_shutdown(void) 170bool gui_shutdown(void)
171{ 171{
172 SDL_RemoveTimer(tick_timer_id); 172 SDL_RemoveTimer(tick_timer_id);
173 kill_sim_threads(); 173 /* Order here is relevent to prevent deadlocks and use of destroyed
174 sync primitives by kernel threads */
175 thread_sdl_shutdown();
174 sim_io_shutdown(); 176 sim_io_shutdown();
175 return true; 177 return true;
176} 178}
177 179
178/**
179 * Thin wrapper around normal app_main() to stop gcc complaining about types.
180 */
181int sim_app_main(void *param)
182{
183 app_main(param);
184
185 return 0;
186}
187
188#if defined(WIN32) && defined(main) 180#if defined(WIN32) && defined(main)
189/* Don't use SDL_main on windows -> no more stdio redirection */ 181/* Don't use SDL_main on windows -> no more stdio redirection */
190#undef main 182#undef main
@@ -231,14 +223,19 @@ int main(int argc, char *argv[])
231 background = false; 223 background = false;
232 } 224 }
233 225
234 sim_io_init(); 226 if (!sim_io_init()) {
227 fprintf(stderr, "sim_io_init failed\n");
228 return -1;
229 }
235 230
236 if (!gui_startup()) 231 if (!gui_startup()) {
232 fprintf(stderr, "gui_startup failed\n");
237 return -1; 233 return -1;
234 }
238 235
239 gui_thread = SDL_CreateThread(sim_app_main, NULL); 236 /* app_main will be called by the new main thread */
240 if (gui_thread == NULL) { 237 if (!thread_sdl_init(NULL)) {
241 printf("Error creating GUI thread!\n"); 238 fprintf(stderr, "thread_sdl_init failed\n");
242 return -1; 239 return -1;
243 } 240 }
244 241