summaryrefslogtreecommitdiff
path: root/firmware/target/hosted
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/hosted')
-rw-r--r--firmware/target/hosted/sdl/button-sdl.c7
-rw-r--r--firmware/target/hosted/sdl/system-sdl.c32
-rw-r--r--firmware/target/hosted/sdl/system-sdl.h2
-rw-r--r--firmware/target/hosted/sdl/thread-sdl.c78
4 files changed, 90 insertions, 29 deletions
diff --git a/firmware/target/hosted/sdl/button-sdl.c b/firmware/target/hosted/sdl/button-sdl.c
index e9fc03792c..15633eede5 100644
--- a/firmware/target/hosted/sdl/button-sdl.c
+++ b/firmware/target/hosted/sdl/button-sdl.c
@@ -86,7 +86,7 @@ static void button_event(int key, bool pressed);
86extern bool debug_wps; 86extern bool debug_wps;
87extern bool mapping; 87extern bool mapping;
88 88
89void gui_message_loop(void) 89bool gui_message_loop(void)
90{ 90{
91 SDL_Event event; 91 SDL_Event event;
92 static int x,y,xybutton = 0; 92 static int x,y,xybutton = 0;
@@ -176,8 +176,7 @@ void gui_message_loop(void)
176 case SDL_QUIT: 176 case SDL_QUIT:
177 { 177 {
178 sim_exit_irq_handler(); 178 sim_exit_irq_handler();
179 exit(EXIT_SUCCESS); 179 return false;
180 break;
181 } 180 }
182 default: 181 default:
183 /*printf("Unhandled event\n"); */ 182 /*printf("Unhandled event\n"); */
@@ -185,6 +184,8 @@ void gui_message_loop(void)
185 } 184 }
186 sim_exit_irq_handler(); 185 sim_exit_irq_handler();
187 } 186 }
187
188 return true;
188} 189}
189 190
190static void button_event(int key, bool pressed) 191static void button_event(int key, bool pressed)
diff --git a/firmware/target/hosted/sdl/system-sdl.c b/firmware/target/hosted/sdl/system-sdl.c
index d6c0dfa345..d56f1d7874 100644
--- a/firmware/target/hosted/sdl/system-sdl.c
+++ b/firmware/target/hosted/sdl/system-sdl.c
@@ -54,6 +54,8 @@ bool sim_alarm_wakeup = false;
54const char *sim_root_dir = NULL; 54const char *sim_root_dir = NULL;
55extern int display_zoom; 55extern int display_zoom;
56 56
57static SDL_Thread *evt_thread = NULL;
58
57#ifdef DEBUG 59#ifdef DEBUG
58bool debug_audio = false; 60bool debug_audio = false;
59#endif 61#endif
@@ -64,16 +66,11 @@ int wps_verbose_level = 3;
64 66
65void sys_poweroff(void) 67void sys_poweroff(void)
66{ 68{
67 /* Order here is relevent to prevent deadlocks and use of destroyed
68 sync primitives by kernel threads */
69 sim_thread_shutdown();
70 sim_kernel_shutdown();
71 SDL_Quit();
72} 69}
73 70
74/* 71/*
75 * Button read loop */ 72 * Button read loop */
76void gui_message_loop(void); 73bool gui_message_loop(void);
77 74
78/* 75/*
79 * This thread will read the buttons in an interrupt like fashion, and 76 * This thread will read the buttons in an interrupt like fashion, and
@@ -137,12 +134,28 @@ static int sdl_event_thread(void * param)
137 134
138 /* 135 /*
139 * finally enter the button loop */ 136 * finally enter the button loop */
140 while(1) 137 while(gui_message_loop());
141 gui_message_loop(); 138
139 /* Order here is relevent to prevent deadlocks and use of destroyed
140 sync primitives by kernel threads */
141 sim_thread_shutdown();
142 sim_kernel_shutdown();
142 143
143 return 0; 144 return 0;
144} 145}
145 146
147void sim_do_exit(SDL_mutex *m)
148{
149 /* wait for event thread to finish */
150 SDL_WaitThread(evt_thread, NULL);
151
152 /* cleanup */
153 SDL_DestroyMutex(m);
154
155 SDL_Quit();
156 exit(EXIT_SUCCESS);
157 while(1);
158}
146 159
147void system_init(void) 160void system_init(void)
148{ 161{
@@ -150,11 +163,10 @@ void system_init(void)
150 163
151 if (SDL_Init(SDL_INIT_TIMER)) 164 if (SDL_Init(SDL_INIT_TIMER))
152 panicf("%s", SDL_GetError()); 165 panicf("%s", SDL_GetError());
153 atexit(sys_poweroff);
154 166
155 s = SDL_CreateSemaphore(0); /* 0-count so it blocks */ 167 s = SDL_CreateSemaphore(0); /* 0-count so it blocks */
156 168
157 SDL_CreateThread(sdl_event_thread, s); 169 evt_thread = SDL_CreateThread(sdl_event_thread, s);
158 170
159 /* wait for sdl_event_thread to run so that it can initialize the surfaces 171 /* wait for sdl_event_thread to run so that it can initialize the surfaces
160 * and video subsystem needed for SDL events */ 172 * and video subsystem needed for SDL events */
diff --git a/firmware/target/hosted/sdl/system-sdl.h b/firmware/target/hosted/sdl/system-sdl.h
index e87f096617..53af7b8355 100644
--- a/firmware/target/hosted/sdl/system-sdl.h
+++ b/firmware/target/hosted/sdl/system-sdl.h
@@ -44,7 +44,7 @@ void sim_exit_irq_handler(void);
44void sim_kernel_shutdown(void); 44void sim_kernel_shutdown(void);
45void sys_poweroff(void); 45void sys_poweroff(void);
46void sys_handle_argv(int argc, char *argv[]); 46void sys_handle_argv(int argc, char *argv[]);
47void gui_message_loop(void); 47bool gui_message_loop(void);
48 48
49extern bool background; /* True if the background image is enabled */ 49extern bool background; /* True if the background image is enabled */
50extern bool showremote; 50extern bool showremote;
diff --git a/firmware/target/hosted/sdl/thread-sdl.c b/firmware/target/hosted/sdl/thread-sdl.c
index da43d6ea9f..ec6718d6f8 100644
--- a/firmware/target/hosted/sdl/thread-sdl.c
+++ b/firmware/target/hosted/sdl/thread-sdl.c
@@ -64,10 +64,15 @@ static volatile bool threads_exit = false;
64 64
65extern long start_tick; 65extern long start_tick;
66 66
67void sim_do_exit(SDL_mutex *m);
68
67void sim_thread_shutdown(void) 69void sim_thread_shutdown(void)
68{ 70{
69 int i; 71 int i;
70 72
73 /* This *has* to be a push operation from a thread not in the pool
74 so that they may be dislodged from their blocking calls. */
75
71 /* Tell all threads jump back to their start routines, unlock and exit 76 /* Tell all threads jump back to their start routines, unlock and exit
72 gracefully - we'll check each one in turn for it's status. Threads 77 gracefully - we'll check each one in turn for it's status. Threads
73 _could_ terminate via remove_thread or multiple threads could exit 78 _could_ terminate via remove_thread or multiple threads could exit
@@ -79,25 +84,44 @@ void sim_thread_shutdown(void)
79 /* Take control */ 84 /* Take control */
80 SDL_LockMutex(m); 85 SDL_LockMutex(m);
81 86
87 /* Signal all threads on delay or block */
82 for (i = 0; i < MAXTHREADS; i++) 88 for (i = 0; i < MAXTHREADS; i++)
83 { 89 {
84 struct thread_entry *thread = &threads[i]; 90 struct thread_entry *thread = &threads[i];
85 /* exit all current threads, except the main one */ 91 if (thread->context.s == NULL)
86 if (thread->context.t != NULL) 92 continue;
93 SDL_SemPost(thread->context.s);
94 }
95
96 /* Wait for all threads to finish and cleanup old ones. */
97 for (i = 0; i < MAXTHREADS; i++)
98 {
99 struct thread_entry *thread = &threads[i];
100 SDL_Thread *t = thread->context.t;
101
102 if (t != NULL)
87 { 103 {
88 /* Signal thread on delay or block */
89 SDL_Thread *t = thread->context.t;
90 SDL_SemPost(thread->context.s);
91 SDL_UnlockMutex(m); 104 SDL_UnlockMutex(m);
92 /* Wait for it to finish */ 105 /* Wait for it to finish */
93 SDL_WaitThread(t, NULL); 106 SDL_WaitThread(t, NULL);
94 /* Relock for next thread signal */ 107 /* Relock for next thread signal */
95 SDL_LockMutex(m); 108 SDL_LockMutex(m);
96 } 109 /* Already waited and exiting thread would have waited .told,
110 * replacing it with t. */
111 thread->context.told = NULL;
112 }
113 else
114 {
115 /* Wait on any previous thread in this location-- could be one not quite
116 * finished exiting but has just unlocked the mutex. If it's NULL, the
117 * call returns immediately.
118 *
119 * See remove_thread below for more information. */
120 SDL_WaitThread(thread->context.told, NULL);
121 }
97 } 122 }
98 123
99 SDL_UnlockMutex(m); 124 SDL_UnlockMutex(m);
100 SDL_DestroyMutex(m);
101} 125}
102 126
103static void new_thread_id(unsigned int slot_num, 127static void new_thread_id(unsigned int slot_num,
@@ -172,9 +196,22 @@ void init_threads(void)
172 return; 196 return;
173 } 197 }
174 198
175 THREAD_SDL_DEBUGF("Main thread: %p\n", thread); 199 /* Tell all threads jump back to their start routines, unlock and exit
200 gracefully - we'll check each one in turn for it's status. Threads
201 _could_ terminate via remove_thread or multiple threads could exit
202 on each unlock but that is safe. */
176 203
177 return; 204 /* Setup jump for exit */
205 if (setjmp(thread_jmpbufs[0]) == 0)
206 {
207 THREAD_SDL_DEBUGF("Main thread: %p\n", thread);
208 return;
209 }
210
211 SDL_UnlockMutex(m);
212
213 /* doesn't return */
214 sim_do_exit(m);
178} 215}
179 216
180void sim_thread_exception_wait(void) 217void sim_thread_exception_wait(void)
@@ -522,7 +559,20 @@ void remove_thread(unsigned int thread_id)
522 559
523 t = thread->context.t; 560 t = thread->context.t;
524 s = thread->context.s; 561 s = thread->context.s;
562
563 /* Wait the last thread here and keep this one or SDL will leak it since
564 * it doesn't free its own library allocations unless a wait is performed.
565 * Such behavior guards against the memory being invalid by the time
566 * SDL_WaitThread is reached and also against two different threads having
567 * the same pointer. It also makes SDL_WaitThread a non-concurrent function.
568 *
569 * However, see more below about SDL_KillThread.
570 */
571 SDL_WaitThread(thread->context.told, NULL);
572
525 thread->context.t = NULL; 573 thread->context.t = NULL;
574 thread->context.s = NULL;
575 thread->context.told = t;
526 576
527 if (thread != current) 577 if (thread != current)
528 { 578 {
@@ -560,18 +610,16 @@ void remove_thread(unsigned int thread_id)
560 longjmp(thread_jmpbufs[current - threads], 1); 610 longjmp(thread_jmpbufs[current - threads], 1);
561 } 611 }
562 612
613 /* SDL_KillThread frees the old pointer too because it uses SDL_WaitThread
614 * to wait for the host to remove it. */
615 thread->context.told = NULL;
563 SDL_KillThread(t); 616 SDL_KillThread(t);
564 restore_irq(oldlevel); 617 restore_irq(oldlevel);
565} 618}
566 619
567void thread_exit(void) 620void thread_exit(void)
568{ 621{
569 struct thread_entry *t = thread_id_entry(THREAD_ID_CURRENT); 622 remove_thread(THREAD_ID_CURRENT);
570 /* the main thread cannot be removed since it's created implicitely
571 * by starting the program;
572 * it has no valid jumpbuf to exit, do nothing for now */
573 if (t != &threads[0])
574 remove_thread(t->id);
575} 623}
576 624
577void thread_wait(unsigned int thread_id) 625void thread_wait(unsigned int thread_id)