From d6af28739747099f98f541d1b76ba501882e113c Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Fri, 26 Oct 2007 23:11:18 +0000 Subject: [PATCH] Implement as genuine a set_irq_level function for the sim as possible. The yield added earlier is still nescessary since other threads won't run anyway while viewing the database screen on either sim or target. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15321 a1c6a512-1295-4272-9138-f99709370657 --- apps/gui/list.c | 8 +- apps/main.c | 5 ++ uisimulator/sdl/button.c | 25 +++--- uisimulator/sdl/kernel.c | 147 ++++++++++++++++++++++++++++++----- uisimulator/sdl/thread-sdl.c | 34 +++++--- uisimulator/sdl/uisdl.c | 17 +++- 6 files changed, 184 insertions(+), 52 deletions(-) diff --git a/apps/gui/list.c b/apps/gui/list.c index 4d19bbdf96..38cecaefcc 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -1285,11 +1285,11 @@ bool simplelist_show_list(struct simplelist_info *info) gui_syncstatusbar_draw(&statusbars, true); list_do_action(CONTEXT_STD, info->timeout, &lists, &action, LIST_WRAP_UNLESS_HELD); -#ifdef SIMULATOR - /* Sim has no interrupts, so this is needed for buttons to be recognised */ + + /* We must yield in this case or no other thread can run */ if (info->timeout == TIMEOUT_NOBLOCK) - yield(); -#endif + yield(); + if (info->action_callback) { action = info->action_callback(action, &lists); diff --git a/apps/main.c b/apps/main.c index 366fed9585..d75bb726cf 100644 --- a/apps/main.c +++ b/apps/main.c @@ -113,6 +113,10 @@ #include "cuesheet.h" +#ifdef SIMULATOR +#include "system-sdl.h" +#endif + /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */ const char appsversion[]=APPSVERSION; @@ -267,6 +271,7 @@ static void init(void) { init_threads(); buffer_init(); + set_irq_level(0); lcd_init(); #ifdef HAVE_REMOTE_LCD lcd_remote_init(); diff --git a/uisimulator/sdl/button.c b/uisimulator/sdl/button.c index 62c2c33150..04475122b5 100644 --- a/uisimulator/sdl/button.c +++ b/uisimulator/sdl/button.c @@ -30,11 +30,6 @@ static intptr_t button_data; /* data value from last message dequeued */ -/* Special thread-synced queue_post for button driver or any other preemptive sim thread */ -extern void queue_syncpost(struct event_queue *q, long id, intptr_t data); -/* Special thread-synced queue_broadcast for button driver or any other preemptive sim thread */ -extern int queue_syncbroadcast(long id, intptr_t data); - /* how long until repeat kicks in */ #define REPEAT_START 6 @@ -115,9 +110,9 @@ void button_event(int key, bool pressed) { usb_connected = !usb_connected; if (usb_connected) - queue_syncpost(&button_queue, SYS_USB_CONNECTED, 0); + queue_post(&button_queue, SYS_USB_CONNECTED, 0); else - queue_syncpost(&button_queue, SYS_USB_DISCONNECTED, 0); + queue_post(&button_queue, SYS_USB_DISCONNECTED, 0); } return; @@ -634,7 +629,7 @@ void button_event(int key, bool pressed) case SDLK_F5: if(pressed) { - queue_syncbroadcast(SYS_SCREENDUMP, 0); + queue_broadcast(SYS_SCREENDUMP, 0); return; } break; @@ -655,17 +650,17 @@ void button_event(int key, bool pressed) #ifdef HAVE_REMOTE_LCD if(diff & BUTTON_REMOTE) if(!skip_remote_release) - queue_syncpost(&button_queue, BUTTON_REL | diff, 0); + queue_post(&button_queue, BUTTON_REL | diff, 0); else skip_remote_release = false; else #endif if(!skip_release) - queue_syncpost(&button_queue, BUTTON_REL | diff, 0); + queue_post(&button_queue, BUTTON_REL | diff, 0); else skip_release = false; #else - queue_syncpost(&button_queue, BUTTON_REL | diff, 0); + queue_post(&button_queue, BUTTON_REL | diff, 0); #endif } @@ -717,7 +712,7 @@ void button_event(int key, bool pressed) { if (queue_empty(&button_queue)) { - queue_syncpost(&button_queue, BUTTON_REPEAT | btn, 0); + queue_post(&button_queue, BUTTON_REPEAT | btn, 0); #ifdef HAVE_BACKLIGHT #ifdef HAVE_REMOTE_LCD if(btn & BUTTON_REMOTE) @@ -739,18 +734,18 @@ void button_event(int key, bool pressed) #ifdef HAVE_REMOTE_LCD if (btn & BUTTON_REMOTE) { if (!remote_filter_first_keypress || is_remote_backlight_on()) - queue_syncpost(&button_queue, btn, 0); + queue_post(&button_queue, btn, 0); else skip_remote_release = true; } else #endif if (!filter_first_keypress || is_backlight_on()) - queue_syncpost(&button_queue, btn, 0); + queue_post(&button_queue, btn, 0); else skip_release = true; #else /* no backlight, nothing to skip */ - queue_syncpost(&button_queue, btn, 0); + queue_post(&button_queue, btn, 0); #endif post = false; } diff --git a/uisimulator/sdl/kernel.c b/uisimulator/sdl/kernel.c index 6a8c9e4538..4e0a508f74 100644 --- a/uisimulator/sdl/kernel.c +++ b/uisimulator/sdl/kernel.c @@ -18,13 +18,93 @@ ****************************************************************************/ #include +#include +#include #include "memory.h" +#include "system-sdl.h" #include "uisdl.h" #include "kernel.h" #include "thread-sdl.h" #include "thread.h" #include "debug.h" +/* Prevent "irq handler" from thread concurrent access as well as current + * access on multiple handlers */ +static SDL_cond *sim_thread_cond; +/* Protect sim irq object when it is being changed */ +static SDL_mutex *sim_irq_mtx; +static int interrupt_level = HIGHEST_IRQ_LEVEL; +static int status_reg = 0; + +extern struct core_entry cores[NUM_CORES]; + +/* Nescessary logic: + * 1) All threads must pass unblocked + * 2) Current handler must always pass unblocked + * 3) Threads must be excluded when irq routine is running + * 4) No more than one handler routine should execute at a time + */ +int set_irq_level(int level) +{ + SDL_LockMutex(sim_irq_mtx); + + int oldlevel = interrupt_level; + + if (status_reg == 0 && level == 0 && oldlevel != 0) + { + /* Not in a handler and "interrupts" are being reenabled */ + SDL_CondSignal(sim_thread_cond); + } + + interrupt_level = level; /* save new level */ + + SDL_UnlockMutex(sim_irq_mtx); + return oldlevel; +} + +void sim_enter_irq_handler(void) +{ + SDL_LockMutex(sim_irq_mtx); + if(interrupt_level != 0) + { + /* "Interrupts" are disabled. Wait for reenable */ + SDL_CondWait(sim_thread_cond, sim_irq_mtx); + } + status_reg = 1; +} + +void sim_exit_irq_handler(void) +{ + status_reg = 0; + SDL_UnlockMutex(sim_irq_mtx); +} + +bool sim_kernel_init(void) +{ + sim_irq_mtx = SDL_CreateMutex(); + if (sim_irq_mtx == NULL) + { + fprintf(stderr, "Cannot create sim_handler_mtx\n"); + return false; + } + + /* Create with a count of 0 to have interrupts disabled by default */ + sim_thread_cond = SDL_CreateCond(); + if (sim_thread_cond == NULL) + { + fprintf(stderr, "Cannot create sim_thread_cond\n"); + return false; + } + + return true; +} + +void sim_kernel_shutdown(void) +{ + SDL_DestroyMutex(sim_irq_mtx); + SDL_DestroyCond(sim_thread_cond); +} + volatile long current_tick = 0; static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void); @@ -85,17 +165,21 @@ static void queue_release_all_senders(struct event_queue *q) void queue_enable_queue_send(struct event_queue *q, struct queue_sender_list *send) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); q->send = NULL; if(send) { q->send = send; memset(send, 0, sizeof(*send)); } + set_irq_level(oldlevel); } #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */ void queue_init(struct event_queue *q, bool register_queue) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + q->read = 0; q->write = 0; thread_queue_init(&q->queue); @@ -113,6 +197,8 @@ void queue_init(struct event_queue *q, bool register_queue) /* Add it to the all_queues array */ all_queues[num_queues++] = q; } + + set_irq_level(oldlevel); } void queue_delete(struct event_queue *q) @@ -120,6 +206,8 @@ void queue_delete(struct event_queue *q) int i; bool found = false; + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + /* Find the queue to be deleted */ for(i = 0;i < num_queues;i++) { @@ -153,11 +241,14 @@ void queue_delete(struct event_queue *q) q->read = 0; q->write = 0; + + set_irq_level(oldlevel); } void queue_wait(struct event_queue *q, struct queue_event *ev) { unsigned int rd; + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME if (q->send && q->send->curr_sender) @@ -171,7 +262,9 @@ void queue_wait(struct event_queue *q, struct queue_event *ev) { do { + cores[CURRENT_CORE].irq_level = oldlevel; block_thread(&q->queue); + oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); } while (q->read == q->write); } @@ -186,10 +279,14 @@ void queue_wait(struct event_queue *q, struct queue_event *ev) queue_fetch_sender(q->send, rd); } #endif + + set_irq_level(oldlevel); } void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME if (q->send && q->send->curr_sender) { @@ -200,7 +297,9 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks) if (q->read == q->write && ticks > 0) { + cores[CURRENT_CORE].irq_level = oldlevel; block_thread_w_tmo(&q->queue, ticks); + oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); } if(q->read != q->write) @@ -220,10 +319,14 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks) { ev->id = SYS_TIMEOUT; } + + set_irq_level(oldlevel); } void queue_post(struct event_queue *q, long id, intptr_t data) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + unsigned int wr = q->write++ & QUEUE_LENGTH_MASK; q->events[wr].id = id; @@ -243,20 +346,15 @@ void queue_post(struct event_queue *q, long id, intptr_t data) #endif wakeup_thread(&q->queue); -} -/* Special thread-synced queue_post for button driver or any other preemptive sim thread */ -void queue_syncpost(struct event_queue *q, long id, intptr_t data) -{ - thread_sdl_lock(); - /* No rockbox threads can be running here */ - queue_post(q, id, data); - thread_sdl_unlock(); + set_irq_level(oldlevel); } #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME intptr_t queue_send(struct event_queue *q, long id, intptr_t data) { + int oldlevel = set_irq_level(oldlevel); + unsigned int wr = q->write++ & QUEUE_LENGTH_MASK; q->events[wr].id = id; @@ -274,11 +372,14 @@ intptr_t queue_send(struct event_queue *q, long id, intptr_t data) wakeup_thread(&q->queue); + cores[CURRENT_CORE].irq_level = oldlevel; block_thread_no_listlock(spp); return thread_get_current()->retval; } /* Function as queue_post if sending is not enabled */ + wakeup_thread(&q->queue); + set_irq_level(oldlevel); return 0; } @@ -307,6 +408,8 @@ bool queue_empty(const struct event_queue* q) void queue_clear(struct event_queue* q) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + /* fixme: This is potentially unsafe in case we do interrupt-like processing */ #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME /* Release all thread waiting in the queue for a reply - @@ -315,10 +418,14 @@ void queue_clear(struct event_queue* q) #endif q->read = 0; q->write = 0; + + set_irq_level(oldlevel); } void queue_remove_from_head(struct event_queue *q, long id) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + while(q->read != q->write) { unsigned int rd = q->read & QUEUE_LENGTH_MASK; @@ -342,6 +449,8 @@ 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) @@ -351,25 +460,16 @@ int queue_count(const struct event_queue *q) int queue_broadcast(long id, intptr_t data) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); int i; for(i = 0;i < num_queues;i++) { queue_post(all_queues[i], id, data); } - - return num_queues; -} -/* Special thread-synced queue_broadcast for button driver or any other preemptive sim thread */ -int queue_syncbroadcast(long id, intptr_t data) -{ - int i; - thread_sdl_lock(); - /* No rockbox threads can be running here */ - i = queue_broadcast(id, data); - thread_sdl_unlock(); - return i; + set_irq_level(oldlevel); + return num_queues; } void yield(void) @@ -398,6 +498,7 @@ void sim_tick_tasks(void) int tick_add_task(void (*f)(void)) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); int i; /* Add a task if there is room */ @@ -406,6 +507,7 @@ int tick_add_task(void (*f)(void)) if(tick_funcs[i] == NULL) { tick_funcs[i] = f; + set_irq_level(oldlevel); return 0; } } @@ -416,6 +518,7 @@ int tick_add_task(void (*f)(void)) int tick_remove_task(void (*f)(void)) { + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); int i; /* Remove a task if it is there */ @@ -424,10 +527,12 @@ int tick_remove_task(void (*f)(void)) if(tick_funcs[i] == f) { tick_funcs[i] = NULL; + set_irq_level(oldlevel); return 0; } } - + + set_irq_level(oldlevel); return -1; } diff --git a/uisimulator/sdl/thread-sdl.c b/uisimulator/sdl/thread-sdl.c index 6a3c4af9eb..a07ac29738 100644 --- a/uisimulator/sdl/thread-sdl.c +++ b/uisimulator/sdl/thread-sdl.c @@ -24,6 +24,7 @@ #include #include #include +#include "system-sdl.h" #include "thread-sdl.h" #include "kernel.h" #include "thread.h" @@ -45,7 +46,8 @@ static char __name[32]; #define THREAD_PANICF(str...) \ ({ fprintf(stderr, str); exit(-1); }) -/* Thread entries as in core */ +/* Thread/core entries as in rockbox core */ +struct core_entry cores[NUM_CORES]; struct thread_entry threads[MAXTHREADS]; /* Jump buffers for graceful exit - kernel threads don't stay neatly * in their start routines responding to messages so this is the only @@ -133,6 +135,7 @@ bool thread_sdl_init(void *param) running->name = "main"; running->state = STATE_RUNNING; running->context.c = SDL_CreateCond(); + cores[CURRENT_CORE].irq_level = STAY_IRQ_LEVEL; if (running->context.c == NULL) { @@ -154,16 +157,6 @@ bool thread_sdl_init(void *param) return true; } -void thread_sdl_lock(void) -{ - SDL_LockMutex(m); -} - -void thread_sdl_unlock(void) -{ - SDL_UnlockMutex(m); -} - static int find_empty_thread_slot(void) { int n; @@ -220,6 +213,17 @@ static void remove_from_list_l(struct thread_entry **list, thread->l.next->l.prev = thread->l.prev; } +static void run_blocking_ops(void) +{ + int level = cores[CURRENT_CORE].irq_level; + + if (level != STAY_IRQ_LEVEL) + { + cores[CURRENT_CORE].irq_level = STAY_IRQ_LEVEL; + set_irq_level(level); + } +} + struct thread_entry *thread_get_current(void) { return running; @@ -373,6 +377,8 @@ void _block_thread(struct thread_queue *tq) thread->bqp = tq; add_to_list_l(&tq->queue, thread); + run_blocking_ops(); + SDL_CondWait(thread->context.c, m); running = thread; @@ -388,6 +394,8 @@ void block_thread_w_tmo(struct thread_queue *tq, int ticks) thread->bqp = tq; add_to_list_l(&tq->queue, thread); + run_blocking_ops(); + SDL_CondWaitTimeout(thread->context.c, m, (1000/HZ) * ticks); running = thread; @@ -451,6 +459,8 @@ void remove_thread(struct thread_entry *thread) SDL_Thread *t; SDL_cond *c; + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + if (thread == NULL) { thread = current; @@ -486,10 +496,12 @@ void remove_thread(struct thread_entry *thread) { /* Do a graceful exit - perform the longjmp back into the thread function to return */ + set_irq_level(oldlevel); longjmp(thread_jmpbufs[current - threads], 1); } SDL_KillThread(t); + set_irq_level(oldlevel); } void thread_wait(struct thread_entry *thread) diff --git a/uisimulator/sdl/uisdl.c b/uisimulator/sdl/uisdl.c index 9dcdbc7e26..d9c64abce2 100644 --- a/uisimulator/sdl/uisdl.c +++ b/uisimulator/sdl/uisdl.c @@ -22,6 +22,7 @@ #include #include "autoconf.h" #include "button.h" +#include "system-sdl.h" #include "thread.h" #include "kernel.h" #include "uisdl.h" @@ -75,7 +76,11 @@ Uint32 tick_timer(Uint32 interval, void *param) if (new_tick != current_tick) { long i; for (i = new_tick - current_tick; i > 0; i--) + { + sim_enter_irq_handler(); sim_tick_tasks(); + sim_exit_irq_handler(); + } current_tick = new_tick; } @@ -92,10 +97,14 @@ void gui_message_loop(void) switch(event.type) { case SDL_KEYDOWN: + sim_enter_irq_handler(); button_event(event.key.keysym.sym, true); + sim_exit_irq_handler(); break; case SDL_KEYUP: + sim_enter_irq_handler(); button_event(event.key.keysym.sym, false); + sim_exit_irq_handler(); break; case SDL_QUIT: done = true; @@ -170,11 +179,12 @@ bool gui_startup(void) bool gui_shutdown(void) { - SDL_RemoveTimer(tick_timer_id); /* Order here is relevent to prevent deadlocks and use of destroyed sync primitives by kernel threads */ thread_sdl_shutdown(); + SDL_RemoveTimer(tick_timer_id); sim_io_shutdown(); + sim_kernel_shutdown(); return true; } @@ -228,6 +238,11 @@ int main(int argc, char *argv[]) background = false; } + if (!sim_kernel_init()) { + fprintf(stderr, "sim_kernel_init failed\n"); + return -1; + } + if (!sim_io_init()) { fprintf(stderr, "sim_io_init failed\n"); return -1;