summaryrefslogtreecommitdiff
path: root/uisimulator/sdl/thread-sdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'uisimulator/sdl/thread-sdl.c')
-rw-r--r--uisimulator/sdl/thread-sdl.c343
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
29SDL_Thread *threads[256]; 30/* Define this as 1 to show informational messages that are not errors. */
30int threadCount = 0; 31#define THREAD_SDL_DEBUGF_ENABLED 0
31volatile long current_tick = 0;
32SDL_mutex *m;
33 32
34void yield(void) 33#if THREAD_SDL_DEBUGF_ENABLED
34#define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
35static 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
46struct thread_entry threads[MAXTHREADS]; /* Thread entries as in core */
47static SDL_mutex *m;
48static SDL_sem *s;
49static struct thread_entry *running;
50
51void 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
73static 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
86static 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
106static 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
47void sim_sleep(int ticks) 127struct 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
132void 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
148void 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
54int runthread(void *data) 161int 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
225void 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
236void 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
254void 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
87void init_threads(void) 274void 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
97void remove_thread(struct thread_entry *thread) 310void 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
348int thread_stack_usage(const struct thread_entry *thread)
349{
350 return 50;
351 (void)thread;
352}
353
354int thread_get_status(const struct thread_entry *thread)
355{
356 return thread->statearg;
357}
358
359/* Return name if one or ID if none */
360void 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}