diff options
Diffstat (limited to 'uisimulator/sdl/thread-sdl.c')
-rw-r--r-- | uisimulator/sdl/thread-sdl.c | 343 |
1 files changed, 310 insertions, 33 deletions
diff --git a/uisimulator/sdl/thread-sdl.c b/uisimulator/sdl/thread-sdl.c index 90a4ecf4a7..5d2fe0b522 100644 --- a/uisimulator/sdl/thread-sdl.c +++ b/uisimulator/sdl/thread-sdl.c | |||
@@ -21,41 +21,157 @@ | |||
21 | #include <SDL.h> | 21 | #include <SDL.h> |
22 | #include <SDL_thread.h> | 22 | #include <SDL_thread.h> |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <memory.h> | ||
24 | #include "thread-sdl.h" | 25 | #include "thread-sdl.h" |
25 | #include "kernel.h" | 26 | #include "kernel.h" |
26 | #include "thread.h" | 27 | #include "thread.h" |
27 | #include "debug.h" | 28 | #include "debug.h" |
28 | 29 | ||
29 | SDL_Thread *threads[256]; | 30 | /* Define this as 1 to show informational messages that are not errors. */ |
30 | int threadCount = 0; | 31 | #define THREAD_SDL_DEBUGF_ENABLED 0 |
31 | volatile long current_tick = 0; | ||
32 | SDL_mutex *m; | ||
33 | 32 | ||
34 | void yield(void) | 33 | #if THREAD_SDL_DEBUGF_ENABLED |
34 | #define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__) | ||
35 | static char __name[32]; | ||
36 | #define THREAD_SDL_GET_NAME(thread) \ | ||
37 | ({ thread_get_name(__name, sizeof(__name)/sizeof(__name[0]), thread); __name; }) | ||
38 | #else | ||
39 | #define THREAD_SDL_DEBUGF(...) | ||
40 | #define THREAD_SDL_GET_NAME(thread) | ||
41 | #endif | ||
42 | |||
43 | #define THREAD_PANICF(str...) \ | ||
44 | ({ fprintf(stderr, str); exit(-1); }) | ||
45 | |||
46 | struct thread_entry threads[MAXTHREADS]; /* Thread entries as in core */ | ||
47 | static SDL_mutex *m; | ||
48 | static SDL_sem *s; | ||
49 | static struct thread_entry *running; | ||
50 | |||
51 | void kill_sim_threads(void) | ||
52 | { | ||
53 | int i; | ||
54 | SDL_LockMutex(m); | ||
55 | for (i = 0; i < MAXTHREADS; i++) | ||
56 | { | ||
57 | struct thread_entry *thread = &threads[i]; | ||
58 | if (thread->context.t != NULL) | ||
59 | { | ||
60 | SDL_LockMutex(m); | ||
61 | if (thread->statearg == STATE_RUNNING) | ||
62 | SDL_SemPost(s); | ||
63 | else | ||
64 | SDL_CondSignal(thread->context.c); | ||
65 | SDL_KillThread(thread->context.t); | ||
66 | SDL_DestroyCond(thread->context.c); | ||
67 | } | ||
68 | } | ||
69 | SDL_DestroyMutex(m); | ||
70 | SDL_DestroySemaphore(s); | ||
71 | } | ||
72 | |||
73 | static int find_empty_thread_slot(void) | ||
74 | { | ||
75 | int n; | ||
76 | |||
77 | for (n = 0; n < MAXTHREADS; n++) | ||
78 | { | ||
79 | if (threads[n].name == NULL) | ||
80 | break; | ||
81 | } | ||
82 | |||
83 | return n; | ||
84 | } | ||
85 | |||
86 | static void add_to_list(struct thread_entry **list, | ||
87 | struct thread_entry *thread) | ||
88 | { | ||
89 | if (*list == NULL) | ||
90 | { | ||
91 | /* Insert into unoccupied list */ | ||
92 | thread->next = thread; | ||
93 | thread->prev = thread; | ||
94 | *list = thread; | ||
95 | } | ||
96 | else | ||
97 | { | ||
98 | /* Insert last */ | ||
99 | thread->next = *list; | ||
100 | thread->prev = (*list)->prev; | ||
101 | thread->prev->next = thread; | ||
102 | (*list)->prev = thread; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static void remove_from_list(struct thread_entry **list, | ||
107 | struct thread_entry *thread) | ||
35 | { | 108 | { |
36 | static int counter = 0; | 109 | if (thread == thread->next) |
110 | { | ||
111 | /* The only item */ | ||
112 | *list = NULL; | ||
113 | return; | ||
114 | } | ||
37 | 115 | ||
38 | SDL_mutexV(m); | 116 | if (thread == *list) |
39 | if (counter++ >= 50) | ||
40 | { | 117 | { |
41 | SDL_Delay(1); | 118 | /* List becomes next item */ |
42 | counter = 0; | 119 | *list = thread->next; |
43 | } | 120 | } |
44 | SDL_mutexP(m); | 121 | |
122 | /* Fix links to jump over the removed entry. */ | ||
123 | thread->prev->next = thread->next; | ||
124 | thread->next->prev = thread->prev; | ||
45 | } | 125 | } |
46 | 126 | ||
47 | void sim_sleep(int ticks) | 127 | struct thread_entry *thread_get_current(void) |
48 | { | 128 | { |
49 | SDL_mutexV(m); | 129 | return running; |
50 | SDL_Delay((1000/HZ) * ticks); | 130 | } |
51 | SDL_mutexP(m); | 131 | |
132 | void switch_thread(bool save_context, struct thread_entry **blocked_list) | ||
133 | { | ||
134 | struct thread_entry *current = running; | ||
135 | |||
136 | SDL_UnlockMutex(m); | ||
137 | |||
138 | SDL_SemWait(s); | ||
139 | |||
140 | SDL_LockMutex(m); | ||
141 | running = current; | ||
142 | |||
143 | SDL_SemPost(s); | ||
144 | |||
145 | (void)save_context; (void)blocked_list; | ||
146 | } | ||
147 | |||
148 | void sleep_thread(int ticks) | ||
149 | { | ||
150 | struct thread_entry *current; | ||
151 | |||
152 | current = running; | ||
153 | current->statearg = STATE_SLEEPING; | ||
154 | |||
155 | SDL_CondWaitTimeout(current->context.c, m, (1000/HZ) * ticks); | ||
156 | running = current; | ||
157 | |||
158 | current->statearg = STATE_RUNNING; | ||
52 | } | 159 | } |
53 | 160 | ||
54 | int runthread(void *data) | 161 | int runthread(void *data) |
55 | { | 162 | { |
56 | SDL_mutexP(m); | 163 | struct thread_entry *current; |
57 | ((void(*)())data) (); | 164 | |
58 | SDL_mutexV(m); | 165 | /* Cannot access thread variables before locking the mutex as the |
166 | data structures may not be filled-in yet. */ | ||
167 | SDL_LockMutex(m); | ||
168 | running = (struct thread_entry *)data; | ||
169 | current = running; | ||
170 | current->context.start(); | ||
171 | |||
172 | THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n", | ||
173 | current - threads, THREAD_SDL_GET_NAME(current)); | ||
174 | remove_thread(NULL); | ||
59 | return 0; | 175 | return 0; |
60 | } | 176 | } |
61 | 177 | ||
@@ -64,37 +180,198 @@ struct thread_entry* | |||
64 | const char *name) | 180 | const char *name) |
65 | { | 181 | { |
66 | /** Avoid compiler warnings */ | 182 | /** Avoid compiler warnings */ |
67 | (void)stack; | ||
68 | (void)stack_size; | ||
69 | (void)name; | ||
70 | SDL_Thread* t; | 183 | SDL_Thread* t; |
184 | SDL_cond *cond; | ||
185 | int slot; | ||
186 | |||
187 | THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : ""); | ||
71 | 188 | ||
72 | if (threadCount == 256) { | 189 | slot = find_empty_thread_slot(); |
190 | if (slot >= MAXTHREADS) | ||
191 | { | ||
192 | DEBUGF("Failed to find thread slot\n"); | ||
73 | return NULL; | 193 | return NULL; |
74 | } | 194 | } |
75 | 195 | ||
76 | t = SDL_CreateThread(runthread, function); | 196 | cond = SDL_CreateCond(); |
77 | threads[threadCount++] = t; | 197 | if (cond == NULL) |
198 | { | ||
199 | DEBUGF("Failed to create condition variable\n"); | ||
200 | return NULL; | ||
201 | } | ||
202 | |||
203 | t = SDL_CreateThread(runthread, &threads[slot]); | ||
204 | if (t == NULL) | ||
205 | { | ||
206 | DEBUGF("Failed to create SDL thread\n"); | ||
207 | SDL_DestroyCond(cond); | ||
208 | return NULL; | ||
209 | } | ||
210 | |||
211 | threads[slot].stack = stack; | ||
212 | threads[slot].stack_size = stack_size; | ||
213 | threads[slot].name = name; | ||
214 | threads[slot].statearg = STATE_RUNNING; | ||
215 | threads[slot].context.start = function; | ||
216 | threads[slot].context.t = t; | ||
217 | threads[slot].context.c = cond; | ||
218 | |||
219 | THREAD_SDL_DEBUGF("New Thread: %d (%s)\n", | ||
220 | slot, THREAD_SDL_GET_NAME(&threads[slot])); | ||
221 | |||
222 | return &threads[slot]; | ||
223 | } | ||
224 | |||
225 | void block_thread(struct thread_entry **list) | ||
226 | { | ||
227 | struct thread_entry *thread = running; | ||
228 | |||
229 | thread->statearg = STATE_BLOCKED; | ||
230 | add_to_list(list, thread); | ||
231 | |||
232 | SDL_CondWait(thread->context.c, m); | ||
233 | running = thread; | ||
234 | } | ||
235 | |||
236 | void block_thread_w_tmo(struct thread_entry **list, int ticks) | ||
237 | { | ||
238 | struct thread_entry *thread = running; | ||
239 | |||
240 | thread->statearg = STATE_BLOCKED_W_TMO; | ||
241 | add_to_list(list, thread); | ||
242 | |||
243 | SDL_CondWaitTimeout(thread->context.c, m, (1000/HZ) * ticks); | ||
244 | running = thread; | ||
245 | |||
246 | if (thread->statearg == STATE_BLOCKED_W_TMO) | ||
247 | { | ||
248 | /* Timed out */ | ||
249 | remove_from_list(list, thread); | ||
250 | thread->statearg = STATE_RUNNING; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | void wakeup_thread(struct thread_entry **list) | ||
255 | { | ||
256 | struct thread_entry *thread = *list; | ||
78 | 257 | ||
79 | yield(); | 258 | if (thread == NULL) |
259 | { | ||
260 | return; | ||
261 | } | ||
80 | 262 | ||
81 | /* The return value is never de-referenced outside thread.c so this | 263 | switch (thread->statearg) |
82 | nastiness should be fine. However, a better solution would be nice. | 264 | { |
83 | */ | 265 | case STATE_BLOCKED: |
84 | return (struct thread_entry*)t; | 266 | case STATE_BLOCKED_W_TMO: |
267 | remove_from_list(list, thread); | ||
268 | thread->statearg = STATE_RUNNING; | ||
269 | SDL_CondSignal(thread->context.c); | ||
270 | break; | ||
271 | } | ||
85 | } | 272 | } |
86 | 273 | ||
87 | void init_threads(void) | 274 | void init_threads(void) |
88 | { | 275 | { |
276 | int slot; | ||
277 | |||
89 | m = SDL_CreateMutex(); | 278 | m = SDL_CreateMutex(); |
279 | s = SDL_CreateSemaphore(0); | ||
280 | |||
281 | memset(threads, 0, sizeof(threads)); | ||
282 | |||
283 | slot = find_empty_thread_slot(); | ||
284 | if (slot >= MAXTHREADS) | ||
285 | { | ||
286 | THREAD_PANICF("Couldn't find slot for main thread.\n"); | ||
287 | } | ||
288 | |||
289 | threads[slot].stack = " "; | ||
290 | threads[slot].stack_size = 8; | ||
291 | threads[slot].name = "main"; | ||
292 | threads[slot].statearg = STATE_RUNNING; | ||
293 | threads[slot].context.t = gui_thread; | ||
294 | threads[slot].context.c = SDL_CreateCond(); | ||
295 | |||
296 | running = &threads[slot]; | ||
297 | |||
298 | THREAD_SDL_DEBUGF("First Thread: %d (%s)\n", | ||
299 | slot, THREAD_SDL_GET_NAME(&threads[slot])); | ||
300 | |||
301 | if (SDL_LockMutex(m) == -1) { | ||
302 | THREAD_PANICF("Couldn't lock mutex\n"); | ||
303 | } | ||
90 | 304 | ||
91 | if (SDL_mutexP(m) == -1) { | 305 | if (SDL_SemPost(s) == -1) { |
92 | fprintf(stderr, "Couldn't lock mutex\n"); | 306 | THREAD_PANICF("Couldn't post to semaphore\n"); |
93 | exit(-1); | ||
94 | } | 307 | } |
95 | } | 308 | } |
96 | 309 | ||
97 | void remove_thread(struct thread_entry *thread) | 310 | void remove_thread(struct thread_entry *thread) |
98 | { | 311 | { |
99 | SDL_KillThread((SDL_Thread*) thread); | 312 | struct thread_entry *current = running; |
313 | SDL_Thread *t; | ||
314 | SDL_cond *c; | ||
315 | |||
316 | if (thread == NULL) | ||
317 | { | ||
318 | thread = current; | ||
319 | } | ||
320 | |||
321 | t = thread->context.t; | ||
322 | c = thread->context.c; | ||
323 | thread->context.t = NULL; | ||
324 | |||
325 | if (thread != current) | ||
326 | { | ||
327 | if (thread->statearg == STATE_RUNNING) | ||
328 | SDL_SemPost(s); | ||
329 | else | ||
330 | SDL_CondSignal(c); | ||
331 | } | ||
332 | |||
333 | THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n", | ||
334 | thread - threads, THREAD_SDL_GET_NAME(thread)); | ||
335 | |||
336 | thread->name = NULL; | ||
337 | |||
338 | SDL_DestroyCond(c); | ||
339 | |||
340 | if (thread == current) | ||
341 | { | ||
342 | SDL_UnlockMutex(m); | ||
343 | } | ||
344 | |||
345 | SDL_KillThread(t); | ||
346 | } | ||
347 | |||
348 | int thread_stack_usage(const struct thread_entry *thread) | ||
349 | { | ||
350 | return 50; | ||
351 | (void)thread; | ||
352 | } | ||
353 | |||
354 | int thread_get_status(const struct thread_entry *thread) | ||
355 | { | ||
356 | return thread->statearg; | ||
357 | } | ||
358 | |||
359 | /* Return name if one or ID if none */ | ||
360 | void thread_get_name(char *buffer, int size, | ||
361 | struct thread_entry *thread) | ||
362 | { | ||
363 | if (size <= 0) | ||
364 | return; | ||
365 | |||
366 | *buffer = '\0'; | ||
367 | |||
368 | if (thread) | ||
369 | { | ||
370 | /* Display thread name if one or ID if none */ | ||
371 | bool named = thread->name && *thread->name; | ||
372 | const char *fmt = named ? "%s" : "%08lX"; | ||
373 | intptr_t name = named ? | ||
374 | (intptr_t)thread->name : (intptr_t)thread; | ||
375 | snprintf(buffer, size, fmt, name); | ||
376 | } | ||
100 | } | 377 | } |