blob: d146cfb64b23ce125f7241a9ce697628727cf5c5 [file] [log] [blame]
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Dan Everton
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <time.h>
21#include <SDL.h>
22#include <SDL_thread.h>
23#include <stdlib.h>
Michael Sevakisf64ebb12007-09-08 12:20:53 +000024#include <memory.h>
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +000025#include "thread-sdl.h"
26#include "kernel.h"
Dave Chapman467651a2007-03-24 01:27:29 +000027#include "thread.h"
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +000028#include "debug.h"
29
Michael Sevakisf64ebb12007-09-08 12:20:53 +000030/* Define this as 1 to show informational messages that are not errors. */
31#define THREAD_SDL_DEBUGF_ENABLED 0
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +000032
Michael Sevakisf64ebb12007-09-08 12:20:53 +000033#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;
Michael Sevakisf64ebb12007-09-08 12:20:53 +000048static struct thread_entry *running;
49
Michael Sevakis0107dfc2007-09-09 01:59:07 +000050extern long start_tick;
51
Michael Sevakisf64ebb12007-09-08 12:20:53 +000052void kill_sim_threads(void)
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +000053{
Michael Sevakisf64ebb12007-09-08 12:20:53 +000054 int i;
55 SDL_LockMutex(m);
56 for (i = 0; i < MAXTHREADS; i++)
Miika Pekkarinen4222f6c2006-07-31 15:02:39 +000057 {
Michael Sevakisf64ebb12007-09-08 12:20:53 +000058 struct thread_entry *thread = &threads[i];
59 if (thread->context.t != NULL)
60 {
61 SDL_LockMutex(m);
Michael Sevakis3a45faa2007-09-09 08:28:42 +000062 if (thread->statearg != STATE_RUNNING)
Michael Sevakisf64ebb12007-09-08 12:20:53 +000063 SDL_CondSignal(thread->context.c);
Michael Sevakis0107dfc2007-09-09 01:59:07 +000064 SDL_Delay(10);
Michael Sevakisf64ebb12007-09-08 12:20:53 +000065 SDL_KillThread(thread->context.t);
66 SDL_DestroyCond(thread->context.c);
67 }
Miika Pekkarinen4222f6c2006-07-31 15:02:39 +000068 }
Michael Sevakisf64ebb12007-09-08 12:20:53 +000069 SDL_DestroyMutex(m);
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +000070}
71
Michael Sevakisf64ebb12007-09-08 12:20:53 +000072static int find_empty_thread_slot(void)
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +000073{
Michael Sevakisf64ebb12007-09-08 12:20:53 +000074 int n;
75
76 for (n = 0; n < MAXTHREADS; n++)
77 {
78 if (threads[n].name == NULL)
79 break;
80 }
81
82 return n;
83}
84
85static void add_to_list(struct thread_entry **list,
86 struct thread_entry *thread)
87{
88 if (*list == NULL)
89 {
90 /* Insert into unoccupied list */
91 thread->next = thread;
92 thread->prev = thread;
93 *list = thread;
94 }
95 else
96 {
97 /* Insert last */
98 thread->next = *list;
99 thread->prev = (*list)->prev;
100 thread->prev->next = thread;
101 (*list)->prev = thread;
102 }
103}
104
105static void remove_from_list(struct thread_entry **list,
106 struct thread_entry *thread)
107{
108 if (thread == thread->next)
109 {
110 /* The only item */
111 *list = NULL;
112 return;
113 }
114
115 if (thread == *list)
116 {
117 /* List becomes next item */
118 *list = thread->next;
119 }
120
121 /* Fix links to jump over the removed entry. */
122 thread->prev->next = thread->next;
123 thread->next->prev = thread->prev;
124}
125
126struct thread_entry *thread_get_current(void)
127{
128 return running;
129}
130
Michael Sevakis0107dfc2007-09-09 01:59:07 +0000131void thread_sdl_lock(void)
132{
133 SDL_LockMutex(m);
134}
135
136void thread_sdl_unlock(void)
137{
138 SDL_UnlockMutex(m);
139}
140
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000141void switch_thread(bool save_context, struct thread_entry **blocked_list)
142{
Michael Sevakis3a45faa2007-09-09 08:28:42 +0000143 static int counter = 0;
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000144 struct thread_entry *current = running;
145
146 SDL_UnlockMutex(m);
147
Michael Sevakis3a45faa2007-09-09 08:28:42 +0000148 if (counter++ >= 50)
149 {
150 SDL_Delay(0);
151 counter = 0;
152 }
Michael Sevakis0107dfc2007-09-09 01:59:07 +0000153
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000154 SDL_LockMutex(m);
155 running = current;
156
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000157 (void)save_context; (void)blocked_list;
158}
159
160void sleep_thread(int ticks)
161{
162 struct thread_entry *current;
Michael Sevakis0107dfc2007-09-09 01:59:07 +0000163 int rem;
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000164
165 current = running;
166 current->statearg = STATE_SLEEPING;
167
Michael Sevakis0107dfc2007-09-09 01:59:07 +0000168 rem = (SDL_GetTicks() - start_tick) % (1000/HZ);
169 if (rem < 0)
170 rem = 0;
171
172 SDL_UnlockMutex(m);
173 SDL_Delay((1000/HZ) * ticks + ((1000/HZ)-1) - rem);
174 SDL_LockMutex(m);
175
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000176 running = current;
177
178 current->statearg = STATE_RUNNING;
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000179}
180
181int runthread(void *data)
182{
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000183 struct thread_entry *current;
184
185 /* Cannot access thread variables before locking the mutex as the
186 data structures may not be filled-in yet. */
187 SDL_LockMutex(m);
188 running = (struct thread_entry *)data;
189 current = running;
190 current->context.start();
191
192 THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
193 current - threads, THREAD_SDL_GET_NAME(current));
194 remove_thread(NULL);
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000195 return 0;
196}
197
Dave Chapman467651a2007-03-24 01:27:29 +0000198struct thread_entry*
199 create_thread(void (*function)(void), void* stack, int stack_size,
200 const char *name)
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000201{
202 /** Avoid compiler warnings */
Dave Chapman467651a2007-03-24 01:27:29 +0000203 SDL_Thread* t;
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000204 SDL_cond *cond;
205 int slot;
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000206
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000207 THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
208
209 slot = find_empty_thread_slot();
210 if (slot >= MAXTHREADS)
211 {
212 DEBUGF("Failed to find thread slot\n");
Dave Chapman467651a2007-03-24 01:27:29 +0000213 return NULL;
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000214 }
215
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000216 cond = SDL_CreateCond();
217 if (cond == NULL)
218 {
219 DEBUGF("Failed to create condition variable\n");
220 return NULL;
221 }
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000222
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000223 t = SDL_CreateThread(runthread, &threads[slot]);
224 if (t == NULL)
225 {
226 DEBUGF("Failed to create SDL thread\n");
227 SDL_DestroyCond(cond);
228 return NULL;
229 }
Dan Evertond66c0e52006-02-12 12:47:20 +0000230
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000231 threads[slot].stack = stack;
232 threads[slot].stack_size = stack_size;
233 threads[slot].name = name;
234 threads[slot].statearg = STATE_RUNNING;
235 threads[slot].context.start = function;
236 threads[slot].context.t = t;
237 threads[slot].context.c = cond;
238
239 THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
240 slot, THREAD_SDL_GET_NAME(&threads[slot]));
241
242 return &threads[slot];
243}
244
245void block_thread(struct thread_entry **list)
246{
247 struct thread_entry *thread = running;
248
249 thread->statearg = STATE_BLOCKED;
250 add_to_list(list, thread);
251
252 SDL_CondWait(thread->context.c, m);
253 running = thread;
254}
255
256void block_thread_w_tmo(struct thread_entry **list, int ticks)
257{
258 struct thread_entry *thread = running;
259
260 thread->statearg = STATE_BLOCKED_W_TMO;
261 add_to_list(list, thread);
262
263 SDL_CondWaitTimeout(thread->context.c, m, (1000/HZ) * ticks);
264 running = thread;
265
266 if (thread->statearg == STATE_BLOCKED_W_TMO)
267 {
268 /* Timed out */
269 remove_from_list(list, thread);
270 thread->statearg = STATE_RUNNING;
271 }
272}
273
274void wakeup_thread(struct thread_entry **list)
275{
276 struct thread_entry *thread = *list;
277
278 if (thread == NULL)
279 {
280 return;
281 }
282
283 switch (thread->statearg)
284 {
285 case STATE_BLOCKED:
286 case STATE_BLOCKED_W_TMO:
287 remove_from_list(list, thread);
288 thread->statearg = STATE_RUNNING;
289 SDL_CondSignal(thread->context.c);
290 break;
291 }
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000292}
293
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000294void init_threads(void)
295{
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000296 int slot;
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000297
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000298 m = SDL_CreateMutex();
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000299
300 memset(threads, 0, sizeof(threads));
301
302 slot = find_empty_thread_slot();
303 if (slot >= MAXTHREADS)
304 {
305 THREAD_PANICF("Couldn't find slot for main thread.\n");
306 }
307
308 threads[slot].stack = " ";
309 threads[slot].stack_size = 8;
310 threads[slot].name = "main";
311 threads[slot].statearg = STATE_RUNNING;
312 threads[slot].context.t = gui_thread;
313 threads[slot].context.c = SDL_CreateCond();
314
315 running = &threads[slot];
316
317 THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
318 slot, THREAD_SDL_GET_NAME(&threads[slot]));
319
320 if (SDL_LockMutex(m) == -1) {
321 THREAD_PANICF("Couldn't lock mutex\n");
322 }
Linus Nielsen Feltzingfc72c532006-02-03 15:19:58 +0000323}
Steve Bavin11fa3a82007-03-30 16:02:42 +0000324
325void remove_thread(struct thread_entry *thread)
326{
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000327 struct thread_entry *current = running;
328 SDL_Thread *t;
329 SDL_cond *c;
330
331 if (thread == NULL)
332 {
333 thread = current;
334 }
335
336 t = thread->context.t;
337 c = thread->context.c;
338 thread->context.t = NULL;
339
340 if (thread != current)
341 {
Michael Sevakis3a45faa2007-09-09 08:28:42 +0000342 if (thread->statearg != STATE_RUNNING)
Michael Sevakisf64ebb12007-09-08 12:20:53 +0000343 SDL_CondSignal(c);
344 }
345
346 THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
347 thread - threads, THREAD_SDL_GET_NAME(thread));
348
349 thread->name = NULL;
350
351 SDL_DestroyCond(c);
352
353 if (thread == current)
354 {
355 SDL_UnlockMutex(m);
356 }
357
358 SDL_KillThread(t);
359}
360
361int thread_stack_usage(const struct thread_entry *thread)
362{
363 return 50;
364 (void)thread;
365}
366
367int thread_get_status(const struct thread_entry *thread)
368{
369 return thread->statearg;
370}
371
372/* Return name if one or ID if none */
373void thread_get_name(char *buffer, int size,
374 struct thread_entry *thread)
375{
376 if (size <= 0)
377 return;
378
379 *buffer = '\0';
380
381 if (thread)
382 {
383 /* Display thread name if one or ID if none */
384 bool named = thread->name && *thread->name;
385 const char *fmt = named ? "%s" : "%08lX";
386 intptr_t name = named ?
387 (intptr_t)thread->name : (intptr_t)thread;
388 snprintf(buffer, size, fmt, name);
389 }
Steve Bavin11fa3a82007-03-30 16:02:42 +0000390}