diff --git a/firmware/SOURCES b/firmware/SOURCES index 349155f..fe0d2d9 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -37,7 +37,11 @@ target/hosted/sdl/lcd-remote-bitmap.c #endif target/hosted/sdl/lcd-sdl.c target/hosted/sdl/system-sdl.c +#ifdef HAVE_SDL_THREADS target/hosted/sdl/thread-sdl.c +#else +thread.c +#endif target/hosted/sdl/timer-sdl.c #ifdef HAVE_TOUCHSCREEN target/hosted/sdl/key_to_touch-sdl.c diff --git a/firmware/export/config.h b/firmware/export/config.h index 2618cf5..169221c 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -701,8 +701,10 @@ Lyre prototype 1 */ #define HAVE_WAKEUP_EXT_CB -#if (CONFIG_PLATFORM & PLATFORM_ANDROID) +#if (CONFIG_PLATFORM & PLATFORM_ANDROID) || defined(WIN32) || defined(HAVE_UCONTEXT) #define HAVE_PRIORITY_SCHEDULING +#elif (CONFIG_PLATFORM & PLATFORM_SDL) +#define HAVE_SDL_THREADS #endif #if (CONFIG_PLATFORM & PLATFORM_NATIVE) diff --git a/firmware/export/thread.h b/firmware/export/thread.h index c778f2c..bffcb0f 100644 --- a/firmware/export/thread.h +++ b/firmware/export/thread.h @@ -84,11 +84,13 @@ * We need more stack when we run under a host * maybe more expensive C lib functions? * - * simulator doesn't simulate stack usage anyway but well ... */ -#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SIMULATOR)) + * simulator (possibly) doesn't simulate stack usage anyway but well ... */ +#if ((CONFIG_PLATFORM & PLATFORM_NATIVE)) #define DEFAULT_STACK_SIZE 0x400 /* Bytes */ -#else +#elif (CONFIG_PLATFORM & PLATFORM_ANDROID) #define DEFAULT_STACK_SIZE 0x1000 /* Bytes */ +#elif (CONFIG_PLATFORM & PLATFORM_SDL) +#define DEFAULT_STACK_SIZE 0x3000 /* Bytes */ #endif @@ -147,6 +149,16 @@ struct regs }; #endif /* CONFIG_CPU */ #elif (CONFIG_PLATFORM & PLATFORM_HOSTED) +#if defined(WIN32) || defined(HAVE_UCONTEXT) +struct regs +{ + void (*start)(void); /* thread's entry point, or NULL when started */ + void* uc; /* ucontext_t or Fiber handle */ + uintptr_t sp; /* Stack pointer, unused */ + size_t stack_size; /* stack size, not always used */ + uintptr_t stack; /* pointer to start of the stack buffer */ +}; +#else /* SDL threads */ struct regs { void *t; /* OS thread */ @@ -154,6 +166,7 @@ struct regs void *s; /* Semaphore for blocking and wakeup */ void (*start)(void); /* Start function */ }; +#endif #endif /* PLATFORM_NATIVE */ /* NOTE: The use of the word "queue" may also refer to a linked list of diff --git a/firmware/target/hosted/sdl/kernel-sdl.c b/firmware/target/hosted/sdl/kernel-sdl.c index c4d4ee2..15934ee 100644 --- a/firmware/target/hosted/sdl/kernel-sdl.c +++ b/firmware/target/hosted/sdl/kernel-sdl.c @@ -34,6 +34,12 @@ static SDL_TimerID tick_timer_id; long start_tick; +#ifndef HAVE_SDL_THREADS +/* for the wait_for_interrupt function */ +static bool do_exit; +static SDL_cond *wfi_cond; +static SDL_mutex *wfi_mutex; +#endif /* Condition to signal that "interrupts" may proceed */ static SDL_cond *sim_thread_cond; /* Mutex to serialize changing levels and exclude other threads while @@ -89,6 +95,9 @@ void sim_exit_irq_handler(void) status_reg = 0; SDL_UnlockMutex(sim_irq_mtx); +#ifndef HAVE_SDL_THREADS + SDL_CondSignal(wfi_cond); +#endif } static bool sim_kernel_init(void) @@ -106,15 +115,33 @@ static bool sim_kernel_init(void) panicf("Cannot create sim_thread_cond\n"); return false; } - +#ifndef HAVE_SDL_THREADS + wfi_cond = SDL_CreateCond(); + if (wfi_cond == NULL) + { + panicf("Cannot create wfi\n"); + return false; + } + wfi_mutex = SDL_CreateMutex(); + if (wfi_mutex == NULL) + { + panicf("Cannot create wfi mutex\n"); + return false; + } +#endif return true; } void sim_kernel_shutdown(void) { + disable_irq(); SDL_RemoveTimer(tick_timer_id); SDL_DestroyMutex(sim_irq_mtx); SDL_DestroyCond(sim_thread_cond); +#ifndef HAVE_SDL_THREADS + do_exit = true; + SDL_CondSignal(wfi_cond); +#endif } Uint32 tick_timer(Uint32 interval, void *param) @@ -159,4 +186,21 @@ void tick_start(unsigned int interval_in_ms) } tick_timer_id = SDL_AddTimer(interval_in_ms, tick_timer, NULL); +#ifndef HAVE_SDL_THREADS + SDL_LockMutex(wfi_mutex); +#endif +} + +#ifndef HAVE_SDL_THREADS +void wait_for_interrupt(void) +{ + SDL_CondWait(wfi_cond, wfi_mutex); + if (UNLIKELY(do_exit)) + { + SDL_DestroyCond(wfi_cond); + SDL_UnlockMutex(wfi_mutex); + SDL_DestroyMutex(wfi_mutex); + sim_do_exit(); + } } +#endif diff --git a/firmware/target/hosted/sdl/system-sdl.c b/firmware/target/hosted/sdl/system-sdl.c index 6937c37..64b3c5b 100644 --- a/firmware/target/hosted/sdl/system-sdl.c +++ b/firmware/target/hosted/sdl/system-sdl.c @@ -138,7 +138,9 @@ static int sdl_event_thread(void * param) /* Order here is relevent to prevent deadlocks and use of destroyed sync primitives by kernel threads */ - sim_thread_shutdown(); +#ifdef HAVE_SDL_THREADS + sim_thread_shutdown(); /* not needed for native threads */ +#endif sim_kernel_shutdown(); return 0; @@ -153,9 +155,13 @@ void sim_do_exit(void) exit(EXIT_SUCCESS); } +uintptr_t *stackbegin; +uintptr_t *stackend; void system_init(void) { SDL_sem *s; + /* fake stack, OS manages size (and growth) */ + stackbegin = stackend = (uintptr_t*)&s; if (SDL_Init(SDL_INIT_TIMER)) panicf("%s", SDL_GetError()); @@ -167,21 +173,24 @@ void system_init(void) /* wait for sdl_event_thread to run so that it can initialize the surfaces * and video subsystem needed for SDL events */ SDL_SemWait(s); - /* cleanup */ SDL_DestroySemaphore(s); } -void system_exception_wait(void) -{ - sim_thread_exception_wait(); -} void system_reboot(void) { +#ifdef HAVE_SDL_THREADS sim_thread_exception_wait(); +#else + sim_do_exit(); +#endif } +void system_exception_wait(void) +{ + system_reboot(); +} void sys_handle_argv(int argc, char *argv[]) { diff --git a/firmware/target/hosted/sdl/thread-sdl.h b/firmware/target/hosted/sdl/thread-sdl.h index 9384e60..9edb3ee 100644 --- a/firmware/target/hosted/sdl/thread-sdl.h +++ b/firmware/target/hosted/sdl/thread-sdl.h @@ -22,11 +22,13 @@ #ifndef __THREADSDL_H__ #define __THREADSDL_H__ +#ifdef HAVE_SDL_THREADS /* extra thread functions that only apply when running on hosting platforms */ void sim_thread_lock(void *me); void * sim_thread_unlock(void); void sim_thread_exception_wait(void); void sim_thread_shutdown(void); /* Shut down all kernel threads gracefully */ +#endif #endif /* #ifndef __THREADSDL_H__ */ diff --git a/firmware/target/hosted/sdl/thread-unix.c b/firmware/target/hosted/sdl/thread-unix.c new file mode 100644 index 0000000..d7b31c2 --- /dev/null +++ b/firmware/target/hosted/sdl/thread-unix.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Thomas Martitz + * + * Generic ARM threading support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include + +static ucontext_t *target_context; /* cached target context between + store_context and load_context calls */ +static ucontext_t finalize_uc; /* the thread_exit() context */ +static ucontext_t ucs[MAXTHREADS]; /* our thread pool */ +static char finalize_uc_stack[0x200]; /* hopefully sufficient */ +static void setup_thread(struct regs *); +static int curr_uc; + +#define INIT_MAIN_THREAD +static void init_main_thread(void *addr) +{ + /* get a context for the main thread so that we can jump to it from + * other threads */ + struct regs *context = (struct regs*)addr; + context->uc = &ucs[curr_uc++]; + getcontext(context->uc); + + /* initialize the final context + * we need to chain a thread_exit() call, since otherwise + * when the thread function returns it would end the entire process */ + getcontext(&finalize_uc); + finalize_uc.uc_stack.ss_size = sizeof(finalize_uc_stack); + finalize_uc.uc_stack.ss_sp = finalize_uc_stack; + finalize_uc.uc_link = NULL; + finalize_uc.uc_flags = 0; + makecontext(&finalize_uc, thread_exit, 0); +} + +#define THREAD_STARTUP_INIT(core, thread, function) \ + ({ (thread)->context.stack_size = (thread)->stack_size, \ + (thread)->context.stack = (uintptr_t)(thread)->stack; \ + (thread)->context.start = function; }) + + +/* + * Save the ucontext_t pointer for later use in swapcontext() + * + * Cannot do getcontext() here, because jumping back to the context + * resumes after the getcontext call (i.e. store_context), but we need + * to resume from load_context() + */ +static inline void store_context(void* addr) +{ + struct regs *r = (struct regs*)addr; + target_context = r->uc; +} + +/* + * Perform context switch + */ +static inline void load_context(const void* addr) +{ + struct regs *r = (struct regs*)addr; + if (UNLIKELY(r->start)) + { + setup_thread(r); + r->start = NULL; + } + if (swapcontext(target_context, r->uc) < 0) + panicf("load_context error!\n"); +} + +/* + * Prepare context to make the thread runnable by calling swapcontext on it + */ +static void setup_thread(struct regs *context) +{ + void (*fn)(void) = context->start; + context->start = NULL; + char *stack = (char*)context->stack; + ucontext_t *uc = &ucs[curr_uc++]; + context->uc = uc; + getcontext(context->uc); /* initialize with current thread */ + uc->uc_stack.ss_sp = stack; + uc->uc_stack.ss_size = context->stack_size; + /* link with thread_exit() to prevent process termination */ + uc->uc_link = &finalize_uc; + uc->uc_flags = 0; + makecontext(context->uc, fn, 0); +} + +/* + * play nice with the host and sleep while waiting for the tick */ +extern void wait_for_interrupt(void); +static inline void core_sleep(void) +{ + enable_irq(); + wait_for_interrupt(); +} + + diff --git a/firmware/target/hosted/sdl/thread-win32.c b/firmware/target/hosted/sdl/thread-win32.c new file mode 100644 index 0000000..09634f9 --- /dev/null +++ b/firmware/target/hosted/sdl/thread-win32.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Thomas Martitz + * + * Generic ARM threading support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#include + + +#define INIT_MAIN_THREAD + +#define THREAD_STARTUP_INIT(core, thread, function) \ + ({ (thread)->context.stack_size = (thread)->stack_size, \ + (thread)->context.stack = (uintptr_t)(thread)->stack; \ + (thread)->context.start = function; }) + +static void init_main_thread(void *addr) +{ + struct regs *context = (struct regs*)addr; + /* we must convert the current main thread to a fiber to be able to + * schedule other fibers */ + context->uc = ConvertThreadToFiber(NULL); + context->stack_size = 0; +} + +static inline void store_context(void* addr) +{ + (void)addr; + /* nothing to do here, Fibers continue after the SwitchToFiber call */ +} + +static void start_thread(void) +{ + void (*func)(void) = GetFiberData(); + func(); + /* go out if thread function returns */ + thread_exit(); +} + +/* + * Load context and run it + * + * Resume execution from the last load_context call for the thread + */ + +static inline void load_context(const void* addr) +{ + struct regs *context = (struct regs*)addr; + if (UNLIKELY(context->start)) + { /* need setup before switching to it */ + context->uc = CreateFiber(context->stack_size, + (LPFIBER_START_ROUTINE)start_thread, context->start); + context->start = NULL; + } + SwitchToFiber(context->uc); +} + +/* + * play nice with the host and sleep while waiting for the tick */ +extern void wait_for_interrupt(void); +static inline void core_sleep(void) +{ + enable_irq(); + wait_for_interrupt(); +} + diff --git a/firmware/thread.c b/firmware/thread.c index 655af1a..6b914aa 100644 --- a/firmware/thread.c +++ b/firmware/thread.c @@ -174,10 +174,15 @@ void switch_thread(void) __attribute__((noinline)); /**************************************************************************** - * Processor-specific section - include necessary core support + * Processor/OS-specific section - include necessary core support */ + #if defined(ANDROID) #include "thread-android-arm.c" +#elif defined(WIN32) +#include "thread-win32.c" +#elif defined(HAVE_UCONTEXT) +#include "thread-unix.c" #elif defined(CPU_ARM) #include "thread-arm.c" #if defined (CPU_PP) @@ -2304,6 +2309,9 @@ void init_threads(void) thread_exit(); #endif /* NUM_CORES */ } +#ifdef INIT_MAIN_THREAD + init_main_thread(&thread->context); +#endif } /* Shared stack scan helper for thread_stack_usage and idle_stack_usage */ diff --git a/tools/configure b/tools/configure index b8cabe7..4a6ef39 100755 --- a/tools/configure +++ b/tools/configure @@ -194,6 +194,19 @@ appcc () { fi } +check_ucontext() { + cat >$tmpdir/uctxtest.c < +int main(int argc, char **argv) +{ + return 0; +} +EOF + $CC -o $tmpdir/uctxtest $tmpdir/uctxtest.c 2>/dev/null + result=$? + rm -rf $tmpdir/uctxtest* + echo $? +} simcc () { # default tool setup for native building @@ -206,6 +219,7 @@ simcc () { GCCOPTS="$GCCOPTS -fno-builtin -g" GCCOPTIMIZE='' LDOPTS='-lm' # button-sdl.c uses sqrt() + UCONTEXT="" # default output binary name, don't override app_get_platform() if [ "$app_type" != "sdl-app" ]; then @@ -238,23 +252,26 @@ simcc () { ;; Linux) + UCONTEXT=`check_ucontext` echo "Linux host detected" LDOPTS="$LDOPTS -ldl" ;; FreeBSD) + UCONTEXT=`check_ucontext` echo "FreeBSD host detected" LDOPTS="$LDOPTS -ldl" ;; Darwin) + UCONTEXT=`check_ucontext` echo "Darwin host detected" LDOPTS="$LDOPTS -ldl" - SHARED_FLAG="-dynamiclib -Wl\,-single_module" ;; SunOS) + UCONTEXT=`check_ucontext` echo "*Solaris host detected" GCCOPTS="$GCCOPTS -fPIC" @@ -355,6 +372,10 @@ EOF echo "Enabling MMX support" GCCOPTS="$GCCOPTS -mmmx" fi + + if [ "$UCONTEXT" = "0" ]; then + extradefines="$extradefines -DHAVE_UCONTEXT" + fi } # diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c index 3591905..1dc8a9d 100644 --- a/uisimulator/common/io.c +++ b/uisimulator/common/io.c @@ -45,9 +45,12 @@ #endif #include -#include -#include +#ifdef HAVE_SDL_THREADS #include "thread-sdl.h" +#else +#define sim_thread_unlock() NULL +#define sim_thread_lock(a) +#endif #include "thread.h" #include "kernel.h" #include "debug.h"