summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-09-08 12:20:53 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-09-08 12:20:53 +0000
commitf64ebb1c1f10e8d15fcc4879d781703c86c5fb8b (patch)
tree065072709c699ac6dc3eb640368bd3f4106144e4
parent69b4654ea28049c7e8637d521327ba10ae405f8b (diff)
downloadrockbox-f64ebb1c1f10e8d15fcc4879d781703c86c5fb8b.tar.gz
rockbox-f64ebb1c1f10e8d15fcc4879d781703c86c5fb8b.zip
Sim I/O and threading that runs more like on target. Tweakable if any genuine slowness imitation is required for any one of them. One point of concern is the sim shutdown on an OS other than Linux just because terminating threads in a manner other than having the do it themselves is kind of dirty IMHO.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14639 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c4
-rw-r--r--apps/plugin.c4
-rw-r--r--apps/plugin.h4
-rw-r--r--apps/plugins/doom/rockmacros.h8
-rw-r--r--apps/plugins/rockboy/rockmacros.h8
-rw-r--r--firmware/export/thread.h10
-rw-r--r--firmware/include/file.h2
-rw-r--r--uisimulator/common/io.c169
-rw-r--r--uisimulator/sdl/kernel.c126
-rw-r--r--uisimulator/sdl/thread-sdl.c343
-rw-r--r--uisimulator/sdl/thread-sdl.h5
-rw-r--r--uisimulator/sdl/uisdl.c15
12 files changed, 579 insertions, 119 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index f8492d34b6..d5c0b8171d 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -181,7 +181,6 @@ static bool dbg_list(struct action_callback_info *info)
extern struct thread_entry threads[MAXTHREADS];
-#ifndef SIMULATOR
static char thread_status_char(int status)
{
switch (status)
@@ -256,7 +255,6 @@ static bool dbg_os(void)
info.dbg_getname = threads_getname;
return dbg_list(&info);
}
-#endif /* !SIMULATOR */
#ifdef HAVE_LCD_BITMAP
#if CONFIG_CODEC != SWCODEC
@@ -2270,9 +2268,7 @@ static const struct the_menu_item menuitems[] = {
#if CONFIG_CPU == SH7034 || defined(CPU_COLDFIRE)
{ "Catch mem accesses", dbg_set_memory_guard },
#endif
-#ifndef SIMULATOR
{ "View OS stacks", dbg_os },
-#endif
#ifdef HAVE_LCD_BITMAP
#ifndef SIMULATOR
{ "View battery", view_battery },
diff --git a/apps/plugin.c b/apps/plugin.c
index 9d46ea4510..bdb59e6005 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -209,10 +209,10 @@ static const struct plugin_api rockbox_api = {
/* file */
(open_func)PREFIX(open),
close,
- (read_func)read,
+ (read_func)PREFIX(read),
PREFIX(lseek),
(creat_func)PREFIX(creat),
- (write_func)write,
+ (write_func)PREFIX(write),
PREFIX(remove),
PREFIX(rename),
PREFIX(ftruncate),
diff --git a/apps/plugin.h b/apps/plugin.h
index c3e5a5b714..2580d43eb0 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -296,10 +296,10 @@ struct plugin_api {
/* file */
int (*PREFIX(open))(const char* pathname, int flags);
int (*close)(int fd);
- ssize_t (*read)(int fd, void* buf, size_t count);
+ ssize_t (*PREFIX(read))(int fd, void* buf, size_t count);
off_t (*PREFIX(lseek))(int fd, off_t offset, int whence);
int (*PREFIX(creat))(const char *pathname);
- ssize_t (*write)(int fd, const void* buf, size_t count);
+ ssize_t (*PREFIX(write))(int fd, const void* buf, size_t count);
int (*PREFIX(remove))(const char* pathname);
int (*PREFIX(rename))(const char* path, const char* newname);
int (*PREFIX(ftruncate))(int fd, off_t length);
diff --git a/apps/plugins/doom/rockmacros.h b/apps/plugins/doom/rockmacros.h
index 1541ef48fd..b73c965c92 100644
--- a/apps/plugins/doom/rockmacros.h
+++ b/apps/plugins/doom/rockmacros.h
@@ -43,20 +43,24 @@ char *my_strtok( char * s, const char * delim );
#undef open
#undef lseek
#undef filesize
+#undef read
+#undef write
#define open(a,b) rb->sim_open((a),(b))
#define lseek(a,b,c) rb->sim_lseek((a),(b),(c))
#define filesize(a) rb->sim_filesize((a))
+#define read(a,b,c) rb->sim_read((a),(b),(c))
+#define write(a,b,c) rb->sim_write((a),(b),(c))
#else /* !SIMULATOR */
#define open(a,b) my_open((a),(b))
#define close(a) my_close((a))
#define lseek(a,b,c) rb->lseek((a),(b),(c))
#define filesize(a) rb->filesize((a))
+#define read(a,b,c) rb->read((a),(b),(c))
+#define write(a,b,c) rb->write((a),(b),(c))
#endif /* !SIMULATOR */
#define strtok(a,b) my_strtok((a),(b))
#define strcat(a,b) rb->strcat((a),(b))
-#define read(a,b,c) rb->read((a),(b),(c))
-#define write(a,b,c) rb->write((a),(b),(c))
#define memset(a,b,c) rb->memset((a),(b),(c))
#define memmove(a,b,c) rb->memmove((a),(b),(c))
#define memcmp(a,b,c) rb->memcmp((a),(b),(c))
diff --git a/apps/plugins/rockboy/rockmacros.h b/apps/plugins/rockboy/rockmacros.h
index f5223b01df..83b599897e 100644
--- a/apps/plugins/rockboy/rockmacros.h
+++ b/apps/plugins/rockboy/rockmacros.h
@@ -76,15 +76,19 @@ void dynamic_recompile (struct dynarec_block *newblock);
#define lseek(a,b,c) rb->sim_lseek((a),(b),(c))
#undef close
#define close(a) rb->close((a))
+#undef read
+#define read(a,b,c) rb->sim_read((a),(b),(c))
+#undef write
+#define write(a,b,c) rb->sim_write((a),(b),(c))
#else /* !SIMULATOR */
#define open(a,b) rb->open((a),(b))
#define lseek(a,b,c) rb->lseek((a),(b),(c))
#define close(a) rb->close((a))
+#define read(a,b,c) rb->read((a),(b),(c))
+#define write(a,b,c) rb->write((a),(b),(c))
#endif /* !SIMULATOR */
#define strcat(a,b) rb->strcat((a),(b))
-#define read(a,b,c) rb->read((a),(b),(c))
-#define write(a,b,c) rb->write((a),(b),(c))
#define memset(a,b,c) rb->memset((a),(b),(c))
#define strcpy(a,b) rb->strcpy((a),(b))
#define strncpy(a,b,c) rb->strncpy((a),(b),(c))
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index 2915d23207..19bf9e12fc 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -76,7 +76,13 @@ struct regs
void *start; /* Thread start address, or NULL when started */
};
# endif
-
+#else
+struct regs
+{
+ void *t; /* Simulator OS thread */
+ void *c; /* Condition for blocking and sync */
+ void (*start)(void); /* Start function */
+};
#endif /* !SIMULATOR */
#define STATE_RUNNING 0x00000000
@@ -97,9 +103,7 @@ struct regs
#define SET_BOOST_STATE(var) (var |= STATE_BOOSTED)
struct thread_entry {
-#ifndef SIMULATOR
struct regs context;
-#endif
const char *name;
void *stack;
unsigned long statearg;
diff --git a/firmware/include/file.h b/firmware/include/file.h
index 989f50a283..9a94e91263 100644
--- a/firmware/include/file.h
+++ b/firmware/include/file.h
@@ -55,6 +55,8 @@
#define fsync(x) sim_fsync(x)
#define ftruncate(x,y) sim_ftruncate(x,y)
#define lseek(x,y,z) sim_lseek(x,y,z)
+#define read(x,y,z) sim_read(x,y,z)
+#define write(x,y,z) sim_write(x,y,z)
#endif
typedef int (*open_func)(const char* pathname, int flags);
diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c
index 3f7087876a..127a1c36f1 100644
--- a/uisimulator/common/io.c
+++ b/uisimulator/common/io.c
@@ -47,7 +47,10 @@
#define MAX_PATH 260
#include <fcntl.h>
-
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "thread.h"
+#include "kernel.h"
#include "debug.h"
#include "config.h"
@@ -175,6 +178,131 @@ static unsigned int rockbox2sim(int opt)
}
#endif
+/** Simulator I/O engine routines **/
+enum
+{
+ IO_QUIT = -1,
+ IO_OPEN,
+ IO_CLOSE,
+ IO_READ,
+ IO_WRITE,
+};
+
+struct sim_io
+{
+ SDL_mutex *m; /* Mutex for condition */
+ SDL_cond *c; /* Condition for synchronizing threads */
+ SDL_Thread *t; /* The I/O thread */
+ struct mutex sim_mutex; /* Rockbox mutex */
+ volatile int cmd; /* The command to perform */
+ volatile int ready; /* I/O ready flag - 1= ready */
+ volatile int fd; /* The file to read/write */
+ void* volatile buf; /* The buffer to read/write */
+ volatile size_t count; /* Number of bytes to read/write */
+ ssize_t result; /* Result of operation */
+};
+
+static struct sim_io io;
+
+static int io_thread(void *data)
+{
+ SDL_LockMutex(io.m);
+
+ io.ready = 1; /* Indication mutex has been locked */
+
+ for (;;)
+ {
+ SDL_CondWait(io.c, io.m); /* unlock mutex and wait */
+
+ switch (io.cmd)
+ {
+ case IO_READ:
+ io.result = read(io.fd, io.buf, io.count);
+ io.ready = 1;
+ break;
+ case IO_WRITE:
+ io.result = write(io.fd, io.buf, io.count);
+ io.ready = 1;
+ break;
+ case IO_QUIT:
+ SDL_UnlockMutex(io.m);
+ return 0;
+ }
+ }
+
+ (void)data;
+}
+
+void sim_io_init(void)
+{
+ mutex_init(&io.sim_mutex);
+
+ io.ready = 0;
+
+ io.m = SDL_CreateMutex();
+ if (io.m == NULL)
+ {
+ fprintf(stderr, "Failed to create IO mutex\n");
+ exit(-1);
+ }
+
+ io.c = SDL_CreateCond();
+ if (io.c == NULL)
+ {
+ fprintf(stderr, "Failed to create IO cond\n");
+ exit(-1);
+ }
+
+ io.t = SDL_CreateThread(io_thread, NULL);
+ if (io.t == NULL)
+ {
+ fprintf(stderr, "Failed to create IO thread\n");
+ exit(-1);
+ }
+
+ /* Wait for IO thread to lock mutex */
+ while (!io.ready);
+
+ /* Wait for it to unlock */
+ SDL_LockMutex(io.m);
+ /* Free it for another thread */
+ SDL_UnlockMutex(io.m);
+}
+
+void sim_io_shutdown(void)
+{
+ SDL_LockMutex(io.m);
+
+ io.cmd = IO_QUIT;
+
+ SDL_CondSignal(io.c);
+ SDL_UnlockMutex(io.m);
+
+ SDL_WaitThread(io.t, NULL);
+
+ SDL_DestroyMutex(io.m);
+ SDL_DestroyCond(io.c);
+}
+
+static void io_trigger_and_wait(int cmd)
+{
+ /* Lock mutex before setting up new params and signaling condition */
+ SDL_LockMutex(io.m);
+
+ io.cmd = cmd;
+ io.ready = 0;
+
+ /* Get thread started */
+ SDL_CondSignal(io.c);
+
+ /* Let it run */
+ SDL_UnlockMutex(io.m);
+
+ /* Wait for IO to complete */
+ while (!io.ready)
+ yield();
+}
+
MYDIR *sim_opendir(const char *name)
{
char buffer[MAX_PATH]; /* sufficiently big */
@@ -287,6 +415,45 @@ int sim_creat(const char *name)
#endif
}
+ssize_t sim_read(int fd, void *buf, size_t count)
+{
+ ssize_t result;
+
+ mutex_lock(&io.sim_mutex);
+
+ /* Setup parameters */
+ io.fd = fd;
+ io.buf = buf;
+ io.count = count;
+
+ io_trigger_and_wait(IO_READ);
+
+ result = io.result;
+
+ mutex_unlock(&io.sim_mutex);
+
+ return result;
+}
+
+ssize_t sim_write(int fd, const void *buf, size_t count)
+{
+ ssize_t result;
+
+ mutex_lock(&io.sim_mutex);
+
+ io.fd = fd;
+ io.buf = (void*)buf;
+ io.count = count;
+
+ io_trigger_and_wait(IO_WRITE);
+
+ result = io.result;
+
+ mutex_unlock(&io.sim_mutex);
+
+ return result;
+}
+
int sim_mkdir(const char *name)
{
#ifdef __PCTOOL__
diff --git a/uisimulator/sdl/kernel.c b/uisimulator/sdl/kernel.c
index 2b194a24ae..91d1afa1b9 100644
--- a/uisimulator/sdl/kernel.c
+++ b/uisimulator/sdl/kernel.c
@@ -25,25 +25,19 @@
#include "thread.h"
#include "debug.h"
+volatile long current_tick = 0;
static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
/* This array holds all queues that are initiated. It is used for broadcast. */
static struct event_queue *all_queues[32];
static int num_queues = 0;
-int set_irq_level (int level)
-{
- static int _lv = 0;
- return (_lv = level);
-}
-
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
/* Moves waiting thread's descriptor to the current sender when a
message is dequeued */
static void queue_fetch_sender(struct queue_sender_list *send,
unsigned int i)
{
- int old_level = set_irq_level(15<<4);
struct thread_entry **spp = &send->senders[i];
if(*spp)
@@ -51,8 +45,6 @@ static void queue_fetch_sender(struct queue_sender_list *send,
send->curr_sender = *spp;
*spp = NULL;
}
-
- set_irq_level(old_level);
}
/* Puts the specified return value in the waiting thread's return value
@@ -61,7 +53,12 @@ static void queue_release_sender(struct thread_entry **sender,
intptr_t retval)
{
(*sender)->retval = retval;
- *sender = NULL;
+ wakeup_thread(sender);
+ if(*sender != NULL)
+ {
+ fprintf(stderr, "queue->send slot ovf: %08X\n", (int)*sender);
+ exit(-1);
+ }
}
/* Releases any waiting threads that are queued with queue_send -
@@ -88,8 +85,12 @@ static void queue_release_all_senders(struct event_queue *q)
void queue_enable_queue_send(struct event_queue *q,
struct queue_sender_list *send)
{
- q->send = send;
- memset(send, 0, sizeof(*send));
+ q->send = NULL;
+ if(send)
+ {
+ q->send = send;
+ memset(send, 0, sizeof(*send));
+ }
}
#endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
@@ -104,6 +105,11 @@ void queue_init(struct event_queue *q, bool register_queue)
if(register_queue)
{
+ if(num_queues >= 32)
+ {
+ fprintf(stderr, "queue_init->out of queues");
+ exit(-1);
+ }
/* Add it to the all_queues array */
all_queues[num_queues++] = q;
}
@@ -114,13 +120,6 @@ void queue_delete(struct event_queue *q)
int i;
bool found = false;
-#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
- /* Release waiting threads and reply to any dequeued message
- waiting for one. */
- queue_release_all_senders(q);
- queue_reply(q, 0);
-#endif
-
/* Find the queue to be deleted */
for(i = 0;i < num_queues;i++)
{
@@ -141,15 +140,28 @@ void queue_delete(struct event_queue *q)
num_queues--;
}
+
+ /* Release threads waiting on queue head */
+ wakeup_thread(&q->thread);
+
+#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
+ /* Release waiting threads and reply to any dequeued message
+ waiting for one. */
+ queue_release_all_senders(q);
+ queue_reply(q, 0);
+#endif
+
+ q->read = 0;
+ q->write = 0;
}
void queue_wait(struct event_queue *q, struct event *ev)
{
unsigned int rd;
- while(q->read == q->write)
+ if (q->read == q->write)
{
- switch_thread(true, NULL);
+ block_thread(&q->thread);
}
rd = q->read++ & QUEUE_LENGTH_MASK;
@@ -166,11 +178,9 @@ void queue_wait(struct event_queue *q, struct event *ev)
void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
{
- unsigned int timeout = current_tick + ticks;
-
- while(q->read == q->write && TIME_BEFORE( current_tick, timeout ))
+ if (q->read == q->write && ticks > 0)
{
- sim_sleep(1);
+ block_thread_w_tmo(&q->thread, ticks);
}
if(q->read != q->write)
@@ -194,7 +204,6 @@ void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
void queue_post(struct event_queue *q, long id, intptr_t data)
{
- int oldlevel = set_irq_level(15<<4);
unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
q->events[wr].id = id;
@@ -213,13 +222,12 @@ void queue_post(struct event_queue *q, long id, intptr_t data)
}
#endif
- set_irq_level(oldlevel);
+ wakeup_thread(&q->thread);
}
#ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
intptr_t queue_send(struct event_queue *q, long id, intptr_t data)
{
- int oldlevel = set_irq_level(15<<4);
unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
q->events[wr].id = id;
@@ -228,7 +236,6 @@ intptr_t queue_send(struct event_queue *q, long id, intptr_t data)
if(q->send)
{
struct thread_entry **spp = &q->send->senders[wr];
- static struct thread_entry sender;
if(*spp)
{
@@ -236,19 +243,13 @@ intptr_t queue_send(struct event_queue *q, long id, intptr_t data)
queue_release_sender(spp, 0);
}
- *spp = &sender;
-
- set_irq_level(oldlevel);
- while (*spp != NULL)
- {
- switch_thread(true, NULL);
- }
+ wakeup_thread(&q->thread);
- return sender.retval;
+ block_thread(spp);
+ return thread_get_current()->retval;
}
/* Function as queue_post if sending is not enabled */
- set_irq_level(oldlevel);
return 0;
}
@@ -289,8 +290,6 @@ void queue_clear(struct event_queue* q)
void queue_remove_from_head(struct event_queue *q, long id)
{
- int oldlevel = set_irq_level(15<<4);
-
while(q->read != q->write)
{
unsigned int rd = q->read & QUEUE_LENGTH_MASK;
@@ -314,8 +313,6 @@ void queue_remove_from_head(struct event_queue *q, long id)
#endif
q->read++;
}
-
- set_irq_level(oldlevel);
}
int queue_count(const struct event_queue *q)
@@ -335,12 +332,14 @@ int queue_broadcast(long id, intptr_t data)
return num_queues;
}
-void switch_thread(bool save_context, struct thread_entry **blocked_list)
+void yield(void)
{
- (void)save_context;
- (void)blocked_list;
-
- yield ();
+ switch_thread(true, NULL);
+}
+
+void sleep(int ticks)
+{
+ sleep_thread(ticks);
}
void sim_tick_tasks(void)
@@ -370,7 +369,8 @@ int tick_add_task(void (*f)(void))
return 0;
}
}
- DEBUGF("Error! tick_add_task(): out of tasks");
+ fprintf(stderr, "Error! tick_add_task(): out of tasks");
+ exit(-1);
return -1;
}
@@ -395,29 +395,39 @@ int tick_remove_task(void (*f)(void))
multitasking, but is better than nothing at all */
void mutex_init(struct mutex *m)
{
- m->locked = false;
+ m->thread = NULL;
+ m->locked = 0;
}
void mutex_lock(struct mutex *m)
{
- while(m->locked)
- switch_thread(true, NULL);
- m->locked = true;
+ if (test_and_set(&m->locked, 1))
+ {
+ block_thread(&m->thread);
+ }
}
void mutex_unlock(struct mutex *m)
{
- m->locked = false;
+ if (m->thread != NULL)
+ {
+ wakeup_thread(&m->thread);
+ }
+ else
+ {
+ m->locked = 0;
+ }
}
-void spinlock_lock(struct mutex *m)
+void spinlock_lock(struct mutex *l)
{
- while(m->locked)
+ while(test_and_set(&l->locked, 1))
+ {
switch_thread(true, NULL);
- m->locked = true;
+ }
}
-void spinlock_unlock(struct mutex *m)
+void spinlock_unlock(struct mutex *l)
{
- m->locked = false;
+ l->locked = 0;
}
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 @@
#include <SDL.h>
#include <SDL_thread.h>
#include <stdlib.h>
+#include <memory.h>
#include "thread-sdl.h"
#include "kernel.h"
#include "thread.h"
#include "debug.h"
-SDL_Thread *threads[256];
-int threadCount = 0;
-volatile long current_tick = 0;
-SDL_mutex *m;
+/* Define this as 1 to show informational messages that are not errors. */
+#define THREAD_SDL_DEBUGF_ENABLED 0
-void yield(void)
+#if THREAD_SDL_DEBUGF_ENABLED
+#define THREAD_SDL_DEBUGF(...) DEBUGF(__VA_ARGS__)
+static char __name[32];
+#define THREAD_SDL_GET_NAME(thread) \
+ ({ thread_get_name(__name, sizeof(__name)/sizeof(__name[0]), thread); __name; })
+#else
+#define THREAD_SDL_DEBUGF(...)
+#define THREAD_SDL_GET_NAME(thread)
+#endif
+
+#define THREAD_PANICF(str...) \
+ ({ fprintf(stderr, str); exit(-1); })
+
+struct thread_entry threads[MAXTHREADS]; /* Thread entries as in core */
+static SDL_mutex *m;
+static SDL_sem *s;
+static struct thread_entry *running;
+
+void kill_sim_threads(void)
+{
+ int i;
+ SDL_LockMutex(m);
+ for (i = 0; i < MAXTHREADS; i++)
+ {
+ struct thread_entry *thread = &threads[i];
+ if (thread->context.t != NULL)
+ {
+ SDL_LockMutex(m);
+ if (thread->statearg == STATE_RUNNING)
+ SDL_SemPost(s);
+ else
+ SDL_CondSignal(thread->context.c);
+ SDL_KillThread(thread->context.t);
+ SDL_DestroyCond(thread->context.c);
+ }
+ }
+ SDL_DestroyMutex(m);
+ SDL_DestroySemaphore(s);
+}
+
+static int find_empty_thread_slot(void)
+{
+ int n;
+
+ for (n = 0; n < MAXTHREADS; n++)
+ {
+ if (threads[n].name == NULL)
+ break;
+ }
+
+ return n;
+}
+
+static void add_to_list(struct thread_entry **list,
+ struct thread_entry *thread)
+{
+ if (*list == NULL)
+ {
+ /* Insert into unoccupied list */
+ thread->next = thread;
+ thread->prev = thread;
+ *list = thread;
+ }
+ else
+ {
+ /* Insert last */
+ thread->next = *list;
+ thread->prev = (*list)->prev;
+ thread->prev->next = thread;
+ (*list)->prev = thread;
+ }
+}
+
+static void remove_from_list(struct thread_entry **list,
+ struct thread_entry *thread)
{
- static int counter = 0;
+ if (thread == thread->next)
+ {
+ /* The only item */
+ *list = NULL;
+ return;
+ }
- SDL_mutexV(m);
- if (counter++ >= 50)
+ if (thread == *list)
{
- SDL_Delay(1);
- counter = 0;
+ /* List becomes next item */
+ *list = thread->next;
}
- SDL_mutexP(m);
+
+ /* Fix links to jump over the removed entry. */
+ thread->prev->next = thread->next;
+ thread->next->prev = thread->prev;
}
-void sim_sleep(int ticks)
+struct thread_entry *thread_get_current(void)
{
- SDL_mutexV(m);
- SDL_Delay((1000/HZ) * ticks);
- SDL_mutexP(m);
+ return running;
+}
+
+void switch_thread(bool save_context, struct thread_entry **blocked_list)
+{
+ struct thread_entry *current = running;
+
+ SDL_UnlockMutex(m);
+
+ SDL_SemWait(s);
+
+ SDL_LockMutex(m);
+ running = current;
+
+ SDL_SemPost(s);
+
+ (void)save_context; (void)blocked_list;
+}
+
+void sleep_thread(int ticks)
+{
+ struct thread_entry *current;
+
+ current = running;
+ current->statearg = STATE_SLEEPING;
+
+ SDL_CondWaitTimeout(current->context.c, m, (1000/HZ) * ticks);
+ running = current;
+
+ current->statearg = STATE_RUNNING;
}
int runthread(void *data)
{
- SDL_mutexP(m);
- ((void(*)())data) ();
- SDL_mutexV(m);
+ struct thread_entry *current;
+
+ /* Cannot access thread variables before locking the mutex as the
+ data structures may not be filled-in yet. */
+ SDL_LockMutex(m);
+ running = (struct thread_entry *)data;
+ current = running;
+ current->context.start();
+
+ THREAD_SDL_DEBUGF("Thread Done: %d (%s)\n",
+ current - threads, THREAD_SDL_GET_NAME(current));
+ remove_thread(NULL);
return 0;
}
@@ -64,37 +180,198 @@ struct thread_entry*
const char *name)
{
/** Avoid compiler warnings */
- (void)stack;
- (void)stack_size;
- (void)name;
SDL_Thread* t;
+ SDL_cond *cond;
+ int slot;
+
+ THREAD_SDL_DEBUGF("Creating thread: (%s)\n", name ? name : "");
- if (threadCount == 256) {
+ slot = find_empty_thread_slot();
+ if (slot >= MAXTHREADS)
+ {
+ DEBUGF("Failed to find thread slot\n");
return NULL;
}
- t = SDL_CreateThread(runthread, function);
- threads[threadCount++] = t;
+ cond = SDL_CreateCond();
+ if (cond == NULL)
+ {
+ DEBUGF("Failed to create condition variable\n");
+ return NULL;
+ }
+
+ t = SDL_CreateThread(runthread, &threads[slot]);
+ if (t == NULL)
+ {
+ DEBUGF("Failed to create SDL thread\n");
+ SDL_DestroyCond(cond);
+ return NULL;
+ }
+
+ threads[slot].stack = stack;
+ threads[slot].stack_size = stack_size;
+ threads[slot].name = name;
+ threads[slot].statearg = STATE_RUNNING;
+ threads[slot].context.start = function;
+ threads[slot].context.t = t;
+ threads[slot].context.c = cond;
+
+ THREAD_SDL_DEBUGF("New Thread: %d (%s)\n",
+ slot, THREAD_SDL_GET_NAME(&threads[slot]));
+
+ return &threads[slot];
+}
+
+void block_thread(struct thread_entry **list)
+{
+ struct thread_entry *thread = running;
+
+ thread->statearg = STATE_BLOCKED;
+ add_to_list(list, thread);
+
+ SDL_CondWait(thread->context.c, m);
+ running = thread;
+}
+
+void block_thread_w_tmo(struct thread_entry **list, int ticks)
+{
+ struct thread_entry *thread = running;
+
+ thread->statearg = STATE_BLOCKED_W_TMO;
+ add_to_list(list, thread);
+
+ SDL_CondWaitTimeout(thread->context.c, m, (1000/HZ) * ticks);
+ running = thread;
+
+ if (thread->statearg == STATE_BLOCKED_W_TMO)
+ {
+ /* Timed out */
+ remove_from_list(list, thread);
+ thread->statearg = STATE_RUNNING;
+ }
+}
+
+void wakeup_thread(struct thread_entry **list)
+{
+ struct thread_entry *thread = *list;
- yield();
+ if (thread == NULL)
+ {
+ return;
+ }
- /* The return value is never de-referenced outside thread.c so this
- nastiness should be fine. However, a better solution would be nice.
- */
- return (struct thread_entry*)t;
+ switch (thread->statearg)
+ {
+ case STATE_BLOCKED:
+ case STATE_BLOCKED_W_TMO:
+ remove_from_list(list, thread);
+ thread->statearg = STATE_RUNNING;
+ SDL_CondSignal(thread->context.c);
+ break;
+ }
}
void init_threads(void)
{
+ int slot;
+
m = SDL_CreateMutex();
+ s = SDL_CreateSemaphore(0);
+
+ memset(threads, 0, sizeof(threads));
+
+ slot = find_empty_thread_slot();
+ if (slot >= MAXTHREADS)
+ {
+ THREAD_PANICF("Couldn't find slot for main thread.\n");
+ }
+
+ threads[slot].stack = " ";
+ threads[slot].stack_size = 8;
+ threads[slot].name = "main";
+ threads[slot].statearg = STATE_RUNNING;
+ threads[slot].context.t = gui_thread;
+ threads[slot].context.c = SDL_CreateCond();
+
+ running = &threads[slot];
+
+ THREAD_SDL_DEBUGF("First Thread: %d (%s)\n",
+ slot, THREAD_SDL_GET_NAME(&threads[slot]));
+
+ if (SDL_LockMutex(m) == -1) {
+ THREAD_PANICF("Couldn't lock mutex\n");
+ }
- if (SDL_mutexP(m) == -1) {
- fprintf(stderr, "Couldn't lock mutex\n");
- exit(-1);
+ if (SDL_SemPost(s) == -1) {
+ THREAD_PANICF("Couldn't post to semaphore\n");
}
}
void remove_thread(struct thread_entry *thread)
{
- SDL_KillThread((SDL_Thread*) thread);
+ struct thread_entry *current = running;
+ SDL_Thread *t;
+ SDL_cond *c;
+
+ if (thread == NULL)
+ {
+ thread = current;
+ }
+
+ t = thread->context.t;
+ c = thread->context.c;
+ thread->context.t = NULL;
+
+ if (thread != current)
+ {
+ if (thread->statearg == STATE_RUNNING)
+ SDL_SemPost(s);
+ else
+ SDL_CondSignal(c);
+ }
+
+ THREAD_SDL_DEBUGF("Removing thread: %d (%s)\n",
+ thread - threads, THREAD_SDL_GET_NAME(thread));
+
+ thread->name = NULL;
+
+ SDL_DestroyCond(c);
+
+ if (thread == current)
+ {
+ SDL_UnlockMutex(m);
+ }
+
+ SDL_KillThread(t);
+}
+
+int thread_stack_usage(const struct thread_entry *thread)
+{
+ return 50;
+ (void)thread;
+}
+
+int thread_get_status(const struct thread_entry *thread)
+{
+ return thread->statearg;
+}
+
+/* Return name if one or ID if none */
+void thread_get_name(char *buffer, int size,
+ struct thread_entry *thread)
+{
+ if (size <= 0)
+ return;
+
+ *buffer = '\0';
+
+ if (thread)
+ {
+ /* Display thread name if one or ID if none */
+ bool named = thread->name && *thread->name;
+ const char *fmt = named ? "%s" : "%08lX";
+ intptr_t name = named ?
+ (intptr_t)thread->name : (intptr_t)thread;
+ snprintf(buffer, size, fmt, name);
+ }
}
diff --git a/uisimulator/sdl/thread-sdl.h b/uisimulator/sdl/thread-sdl.h
index 0bb6ded0d5..20641fb673 100644
--- a/uisimulator/sdl/thread-sdl.h
+++ b/uisimulator/sdl/thread-sdl.h
@@ -22,9 +22,8 @@
#include "SDL_thread.h"
-extern SDL_Thread* threads[256];
-extern int threadCount;
-extern SDL_mutex* mutex;
+extern SDL_Thread *gui_thread; /* The "main" thread */
+void kill_sim_threads(); /* Kill all the rockbox sim threads */
#endif /* #ifndef __THREADSDL_H__ */
diff --git a/uisimulator/sdl/uisdl.c b/uisimulator/sdl/uisdl.c
index 052fd4af83..37a0e6fe7a 100644
--- a/uisimulator/sdl/uisdl.c
+++ b/uisimulator/sdl/uisdl.c
@@ -41,6 +41,8 @@
extern void app_main (void *); /* mod entry point */
extern void new_key(int key);
extern void sim_tick_tasks(void);
+extern void sim_io_init(void);
+extern void sim_io_shutdown(void);
void button_event(int key, bool pressed);
@@ -167,16 +169,9 @@ bool gui_startup(void)
bool gui_shutdown(void)
{
- int i;
-
- SDL_KillThread(gui_thread);
SDL_RemoveTimer(tick_timer_id);
-
- for (i = 0; i < threadCount; i++)
- {
- SDL_KillThread(threads[i]);
- }
-
+ kill_sim_threads();
+ sim_io_shutdown();
return true;
}
@@ -236,6 +231,8 @@ int main(int argc, char *argv[])
background = false;
}
+ sim_io_init();
+
if (!gui_startup())
return -1;