summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/sdl/thread-sdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/hosted/sdl/thread-sdl.c')
-rw-r--r--firmware/target/hosted/sdl/thread-sdl.c78
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
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)