From f64ebb1c1f10e8d15fcc4879d781703c86c5fb8b Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 8 Sep 2007 12:20:53 +0000 Subject: 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 --- uisimulator/sdl/kernel.c | 126 ++++++++-------- uisimulator/sdl/thread-sdl.c | 343 ++++++++++++++++++++++++++++++++++++++----- uisimulator/sdl/thread-sdl.h | 5 +- uisimulator/sdl/uisdl.c | 15 +- 4 files changed, 386 insertions(+), 103 deletions(-) (limited to 'uisimulator/sdl') 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 #include #include +#include #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; -- cgit v1.2.3