summaryrefslogtreecommitdiff
path: root/uisimulator
diff options
context:
space:
mode:
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