diff options
Diffstat (limited to 'firmware/target/hosted/sdl/thread-sdl.c')
-rw-r--r-- | firmware/target/hosted/sdl/thread-sdl.c | 78 |
1 files changed, 63 insertions, 15 deletions
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) |