diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-05-27 18:46:09 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-05-27 18:46:09 +0000 |
commit | deb1600bbc4d0e04f4560186fb665dc95b908e14 (patch) | |
tree | 455eb190bca5646be747d3a59d03122aa9c2b9ff /firmware/target/hosted | |
parent | 0dcf93ed18fe972198011246f27f9137fa863dd4 (diff) | |
download | rockbox-deb1600bbc4d0e04f4560186fb665dc95b908e14.tar.gz rockbox-deb1600bbc4d0e04f4560186fb665dc95b908e14.zip |
SDL Simulator: Get thread shutdown and properly handled and fix a minor memory leak that happens when threads exit.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26336 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/hosted')
-rw-r--r-- | firmware/target/hosted/sdl/button-sdl.c | 7 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/system-sdl.c | 32 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/system-sdl.h | 2 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/thread-sdl.c | 78 |
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); | |||
86 | extern bool debug_wps; | 86 | extern bool debug_wps; |
87 | extern bool mapping; | 87 | extern bool mapping; |
88 | 88 | ||
89 | void gui_message_loop(void) | 89 | bool 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 | ||
190 | static void button_event(int key, bool pressed) | 191 | static 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; | |||
54 | const char *sim_root_dir = NULL; | 54 | const char *sim_root_dir = NULL; |
55 | extern int display_zoom; | 55 | extern int display_zoom; |
56 | 56 | ||
57 | static SDL_Thread *evt_thread = NULL; | ||
58 | |||
57 | #ifdef DEBUG | 59 | #ifdef DEBUG |
58 | bool debug_audio = false; | 60 | bool debug_audio = false; |
59 | #endif | 61 | #endif |
@@ -64,16 +66,11 @@ int wps_verbose_level = 3; | |||
64 | 66 | ||
65 | void sys_poweroff(void) | 67 | void 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 */ |
76 | void gui_message_loop(void); | 73 | bool 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 | ||
147 | void 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 | ||
147 | void system_init(void) | 160 | void 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); | |||
44 | void sim_kernel_shutdown(void); | 44 | void sim_kernel_shutdown(void); |
45 | void sys_poweroff(void); | 45 | void sys_poweroff(void); |
46 | void sys_handle_argv(int argc, char *argv[]); | 46 | void sys_handle_argv(int argc, char *argv[]); |
47 | void gui_message_loop(void); | 47 | bool gui_message_loop(void); |
48 | 48 | ||
49 | extern bool background; /* True if the background image is enabled */ | 49 | extern bool background; /* True if the background image is enabled */ |
50 | extern bool showremote; | 50 | extern 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 | ||
65 | extern long start_tick; | 65 | extern long start_tick; |
66 | 66 | ||
67 | void sim_do_exit(SDL_mutex *m); | ||
68 | |||
67 | void sim_thread_shutdown(void) | 69 | void 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 | ||
103 | static void new_thread_id(unsigned int slot_num, | 127 | static 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 | ||
180 | void sim_thread_exception_wait(void) | 217 | void 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 | ||
567 | void thread_exit(void) | 620 | void 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 | ||
577 | void thread_wait(unsigned int thread_id) | 625 | void thread_wait(unsigned int thread_id) |