diff options
Diffstat (limited to 'uisimulator')
-rw-r--r-- | uisimulator/common/io.c | 19 | ||||
-rw-r--r-- | uisimulator/sdl/thread-sdl.c | 188 | ||||
-rw-r--r-- | uisimulator/sdl/thread-sdl.h | 3 | ||||
-rw-r--r-- | uisimulator/sdl/uisdl.c | 33 |
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 | ||
236 | void sim_io_init(void) | 236 | bool 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 | |||
273 | int 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 | ||
273 | void sim_io_shutdown(void) | 280 | void 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 | ||
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); |
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 | ||
25 | extern SDL_Thread *gui_thread; /* The "main" thread */ | 25 | extern SDL_Thread *gui_thread; /* The "main" thread */ |
26 | void kill_sim_threads(); /* Kill all the rockbox sim threads */ | 26 | bool thread_sdl_init(void *param); /* Init the sim threading API - thread created calls app_main */ |
27 | void thread_sdl_shutdown(void); /* Shut down all kernel threads gracefully */ | ||
27 | void thread_sdl_lock(void); /* Sync with SDL threads */ | 28 | void thread_sdl_lock(void); /* Sync with SDL threads */ |
28 | void thread_sdl_unlock(void); /* Sync with SDL threads */ | 29 | void 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 @@ | |||
41 | extern void app_main (void *); /* mod entry point */ | 42 | extern void app_main (void *); /* mod entry point */ |
42 | extern void new_key(int key); | 43 | extern void new_key(int key); |
43 | extern void sim_tick_tasks(void); | 44 | extern void sim_tick_tasks(void); |
44 | extern void sim_io_init(void); | 45 | extern bool sim_io_init(void); |
45 | extern void sim_io_shutdown(void); | 46 | extern void sim_io_shutdown(void); |
46 | 47 | ||
47 | void button_event(int key, bool pressed); | 48 | void button_event(int key, bool pressed); |
@@ -49,7 +50,6 @@ void button_event(int key, bool pressed); | |||
49 | SDL_Surface *gui_surface; | 50 | SDL_Surface *gui_surface; |
50 | bool background = false; /* Don't use backgrounds by default */ | 51 | bool background = false; /* Don't use backgrounds by default */ |
51 | 52 | ||
52 | SDL_Thread *gui_thread; | ||
53 | SDL_TimerID tick_timer_id; | 53 | SDL_TimerID tick_timer_id; |
54 | 54 | ||
55 | bool lcd_display_redraw = true; /* Used for player simulator */ | 55 | bool lcd_display_redraw = true; /* Used for player simulator */ |
@@ -170,21 +170,13 @@ bool gui_startup(void) | |||
170 | bool gui_shutdown(void) | 170 | bool 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 | */ | ||
181 | int 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 | ||