diff options
author | Thomas Martitz <kugel@rockbox.org> | 2011-02-18 22:46:01 +0000 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2011-02-18 22:46:01 +0000 |
commit | 6d85de341928aef8178465c60122f3cbe76f5dd6 (patch) | |
tree | ff86c384a574ac20d3418c1b904ed4d0de1f6980 | |
parent | 3926c30705cc7235122e2f2e35ab506b53238cdf (diff) | |
download | rockbox-6d85de3.tar.gz rockbox-6d85de3.zip |
Implement cooperative threads on hosted platforms using C code.
This replaces SDL threads with real cooperative threads, which are less cpu intensive and allow priority scheduling.
The backend for context switching is dependant on the host (sigaltstack/longjmp on Unix, Fibers on Windows).
configure has options to force or disallow SDL threads.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29327 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | firmware/SOURCES | 4 | ||||
-rw-r--r-- | firmware/export/config.h | 4 | ||||
-rw-r--r-- | firmware/export/thread.h | 27 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/kernel-sdl.c | 62 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/system-sdl.c | 21 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/system-sdl.h | 3 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/thread-sdl.h | 2 | ||||
-rw-r--r-- | firmware/target/hosted/thread-arm.c (renamed from firmware/target/hosted/android/thread-android-arm.c) | 8 | ||||
-rw-r--r-- | firmware/target/hosted/thread-unix.c | 294 | ||||
-rw-r--r-- | firmware/target/hosted/thread-win32.c | 85 | ||||
-rw-r--r-- | firmware/thread.c | 12 | ||||
-rwxr-xr-x | tools/configure | 78 | ||||
-rw-r--r-- | uisimulator/common/io.c | 7 |
13 files changed, 580 insertions, 27 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES index 9c9ebf53fc..b2b353bda2 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 3063e1f06d..a870e5d815 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -717,7 +717,9 @@ Lyre prototype 1 */ #define HAVE_WAKEUP_EXT_CB -#if (CONFIG_PLATFORM & PLATFORM_ANDROID) +#if defined(ASSEMBLER_THREADS) \ + || defined(HAVE_WIN32_FIBER_THREADS) \ + || defined(HAVE_SIGALTSTACK_THREADS) #define HAVE_PRIORITY_SCHEDULING #endif diff --git a/firmware/export/thread.h b/firmware/export/thread.h index 3cce78444c..4f7631cebd 100644 --- a/firmware/export/thread.h +++ b/firmware/export/thread.h @@ -84,15 +84,19 @@ * 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)) -#define DEFAULT_STACK_SIZE 0x400 /* Bytes */ -#else + * simulator (possibly) doesn't simulate stack usage anyway but well ... */ +#ifdef HAVE_SIGALTSTACK_THREADS +#include <signal.h> +/* MINSIGSTKSZ for the OS to deliver the signal + 0x3000 for us */ +#define DEFAULT_STACK_SIZE (MINSIGSTKSZ+0x3000) /* Bytes */ +#elif (CONFIG_PLATFORM & PLATFORM_ANDROID) || defined(HAVE_WIN32_FIBER_THREADS) #define DEFAULT_STACK_SIZE 0x1000 /* Bytes */ +#else /* native threads, sdl threads */ +#define DEFAULT_STACK_SIZE 0x400 /* Bytes */ #endif -#if (CONFIG_PLATFORM & (PLATFORM_NATIVE|PLATFORM_ANDROID)) +#if defined(ASSEMBLER_THREADS) /* Need to keep structures inside the header file because debug_menu * needs them. */ #ifdef CPU_COLDFIRE @@ -112,7 +116,7 @@ struct regs uint32_t pr; /* 32 - Procedure register */ uint32_t start; /* 36 - Thread start address, or NULL when started */ }; -#elif defined(CPU_ARM) || (CONFIG_PLATFORM & PLATFORM_ANDROID) +#elif defined(CPU_ARM) struct regs { uint32_t r[8]; /* 0-28 - Registers r4-r11 */ @@ -147,6 +151,16 @@ struct regs }; #endif /* CONFIG_CPU */ #elif (CONFIG_PLATFORM & PLATFORM_HOSTED) +#ifndef HAVE_SDL_THREADS +struct regs +{ + void (*start)(void); /* thread's entry point, or NULL when started */ + void* uc; /* host thread 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 +168,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 d933b9097e..4fb1aede0a 100644 --- a/firmware/target/hosted/sdl/kernel-sdl.c +++ b/firmware/target/hosted/sdl/kernel-sdl.c @@ -34,6 +34,14 @@ 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; +#else +#define do_exit false +#endif /* Condition to signal that "interrupts" may proceed */ static SDL_cond *sim_thread_cond; /* Mutex to serialize changing levels and exclude other threads while @@ -98,6 +106,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) @@ -115,15 +126,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) { SDL_RemoveTimer(tick_timer_id); +#ifndef HAVE_SDL_THREADS + do_exit = true; + SDL_CondSignal(wfi_cond); +#endif + disable_irq(); SDL_DestroyMutex(sim_irq_mtx); - SDL_DestroyCond(sim_thread_cond); + SDL_DestroyCond(sim_thread_cond); } Uint32 tick_timer(Uint32 interval, void *param) @@ -135,7 +164,7 @@ Uint32 tick_timer(Uint32 interval, void *param) new_tick = (SDL_GetTicks() - start_tick) / (1000/HZ); - while(new_tick != current_tick) + while(new_tick != current_tick && !do_exit) { sim_enter_irq_handler(); @@ -146,7 +175,7 @@ Uint32 tick_timer(Uint32 interval, void *param) sim_exit_irq_handler(); } - return interval; + return do_exit ? 0 : interval; } void tick_start(unsigned int interval_in_ms) @@ -168,4 +197,29 @@ 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 +static void check_exit(void) +{ + if (UNLIKELY(do_exit)) + { + SDL_DestroyCond(wfi_cond); + SDL_UnlockMutex(wfi_mutex); + SDL_DestroyMutex(wfi_mutex); + sim_do_exit(); + } +} + +void wait_for_interrupt(void) +{ + /* the exit may come at any time, during the CondWait or before, + * so check it twice */ + check_exit(); + SDL_CondWait(wfi_cond, wfi_mutex); + check_exit(); } +#endif diff --git a/firmware/target/hosted/sdl/system-sdl.c b/firmware/target/hosted/sdl/system-sdl.c index 9ea25c1014..1cc9b2c919 100644 --- a/firmware/target/hosted/sdl/system-sdl.c +++ b/firmware/target/hosted/sdl/system-sdl.c @@ -184,7 +184,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; @@ -199,9 +201,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 (CONFIG_PLATFORM & PLATFORM_MAEMO) /* Make glib thread safe */ @@ -219,21 +225,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/system-sdl.h b/firmware/target/hosted/sdl/system-sdl.h index 01952e50a0..bec01ec81d 100644 --- a/firmware/target/hosted/sdl/system-sdl.h +++ b/firmware/target/hosted/sdl/system-sdl.h @@ -46,6 +46,9 @@ void sys_poweroff(void); void sys_handle_argv(int argc, char *argv[]); void gui_message_loop(void); void sim_do_exit(void); +#ifndef HAVE_SDL_THREADS +void wait_for_interrupt(void); +#endif extern bool background; /* True if the background image is enabled */ extern bool showremote; diff --git a/firmware/target/hosted/sdl/thread-sdl.h b/firmware/target/hosted/sdl/thread-sdl.h index 9384e6060d..9edb3ee74e 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/android/thread-android-arm.c b/firmware/target/hosted/thread-arm.c index 0bfd2b6c44..d2fa7d1e5d 100644 --- a/firmware/target/hosted/android/thread-android-arm.c +++ b/firmware/target/hosted/thread-arm.c @@ -22,7 +22,6 @@ * ****************************************************************************/ -#include <jni.h> #include <system.h> /*--------------------------------------------------------------------------- * Start the thread running and terminate it if it returns @@ -84,14 +83,15 @@ static inline void load_context(const void* addr) * this core sleep suspends the OS thread rockbox runs under, which greatly * reduces cpu usage (~100% to <10%) * - * it returns when the RockboxTimer notified us, i.e. at each tick - * (after it called the tick tasks) + * it returns when when the tick timer is called, other interrupt-like + * events occur * - * wait_for_interrupt is implemented in kernel-android.c + * wait_for_interrupt is implemented in kernel-<platform>.c **/ static inline void core_sleep(void) { + enable_irq(); wait_for_interrupt(); } diff --git a/firmware/target/hosted/thread-unix.c b/firmware/target/hosted/thread-unix.c new file mode 100644 index 0000000000..a84ac70d56 --- /dev/null +++ b/firmware/target/hosted/thread-unix.c @@ -0,0 +1,294 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <signal.h> +#include <stdio.h> +#include <setjmp.h> +#include <unistd.h> +#include <pthread.h> +#include <errno.h> +#include "debug.h" + +static volatile bool sig_handler_called; +static volatile jmp_buf tramp_buf; +static volatile jmp_buf bootstrap_buf; +static void (*thread_func)(void); +static const int trampoline_sig = SIGUSR1; +static pthread_t main_thread; + +static struct ctx { + jmp_buf thread_buf; +} thread_bufs[MAXTHREADS]; +static struct ctx* thread_context, *target_context; +static int curr_uc; + +static void trampoline(int sig); +static void bootstrap_context(void) __attribute__((noinline)); + +/* The *_context functions are heavily based on Gnu pth + * http://www.gnu.org/software/pth/ + * + * adjusted to work in a multi-thread environment to + * offer a ucontext-like API + */ + +/* + * VARIANT 2: THE SIGNAL STACK TRICK + * + * This uses sigstack/sigaltstack() and friends and is really the + * most tricky part of Pth. When you understand the following + * stuff you're a good Unix hacker and then you've already + * understood the gory ingredients of Pth. So, either welcome to + * the club of hackers, or do yourself a favor and skip this ;) + * + * The ingenious fact is that this variant runs really on _all_ POSIX + * compliant systems without special platform kludges. But be _VERY_ + * carefully when you change something in the following code. The slightest + * change or reordering can lead to horribly broken code. Really every + * function call in the following case is intended to be how it is, doubt + * me... + * + * For more details we strongly recommend you to read the companion + * paper ``Portable Multithreading -- The Signal Stack Trick for + * User-Space Thread Creation'' from Ralf S. Engelschall. A copy of the + * draft of this paper you can find in the file rse-pmt.ps inside the + * GNU Pth distribution. + */ + +static int make_context(struct ctx *ctx, void (*f)(void), char *sp, size_t stack_size) +{ + struct sigaction sa; + struct sigaction osa; + stack_t ss; + stack_t oss; + sigset_t osigs; + sigset_t sigs; + + disable_irq(); + /* + * Preserve the trampoline_sig signal state, block trampoline_sig, + * and establish our signal handler. The signal will + * later transfer control onto the signal stack. + */ + sigemptyset(&sigs); + sigaddset(&sigs, trampoline_sig); + sigprocmask(SIG_BLOCK, &sigs, &osigs); + sa.sa_handler = trampoline; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + if (sigaction(trampoline_sig, &sa, &osa) != 0) + { + DEBUGF("%s(): %s\n", __func__, strerror(errno)); + return false; + } + /* + * Set the new stack. + * + * For sigaltstack we're lucky [from sigaltstack(2) on + * FreeBSD 3.1]: ``Signal stacks are automatically adjusted + * for the direction of stack growth and alignment + * requirements'' + * + * For sigstack we have to decide ourself [from sigstack(2) + * on Solaris 2.6]: ``The direction of stack growth is not + * indicated in the historical definition of struct sigstack. + * The only way to portably establish a stack pointer is for + * the application to determine stack growth direction.'' + */ + ss.ss_sp = sp; + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, &oss) < 0) + { + DEBUGF("%s(): %s\n", __func__, strerror(errno)); + return false; + } + + /* + * Now transfer control onto the signal stack and set it up. + * It will return immediately via "return" after the setjmp() + * was performed. Be careful here with race conditions. The + * signal can be delivered the first time sigsuspend() is + * called. + */ + sig_handler_called = false; + main_thread = pthread_self(); + sigfillset(&sigs); + sigdelset(&sigs, trampoline_sig); + pthread_kill(main_thread, trampoline_sig); + while(!sig_handler_called) + sigsuspend(&sigs); + + /* + * Inform the system that we are back off the signal stack by + * removing the alternative signal stack. Be careful here: It + * first has to be disabled, before it can be removed. + */ + sigaltstack(NULL, &ss); + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) < 0) + { + DEBUGF("%s(): %s\n", __func__, strerror(errno)); + return false; + } + sigaltstack(NULL, &ss); + if (!(ss.ss_flags & SS_DISABLE)) + { + DEBUGF("%s(): %s\n", __func__, strerror(errno)); + return false; + } + if (!(oss.ss_flags & SS_DISABLE)) + sigaltstack(&oss, NULL); + + /* + * Restore the old trampoline_sig signal handler and mask + */ + sigaction(trampoline_sig, &osa, NULL); + sigprocmask(SIG_SETMASK, &osigs, NULL); + + /* + * Tell the trampoline and bootstrap function where to dump + * the new machine context, and what to do afterwards... + */ + thread_func = f; + thread_context = ctx; + + /* + * Now enter the trampoline again, but this time not as a signal + * handler. Instead we jump into it directly. The functionally + * redundant ping-pong pointer arithmentic is neccessary to avoid + * type-conversion warnings related to the `volatile' qualifier and + * the fact that `jmp_buf' usually is an array type. + */ + if (setjmp(*((jmp_buf *)&bootstrap_buf)) == 0) + longjmp(*((jmp_buf *)&tramp_buf), 1); + + /* + * Ok, we returned again, so now we're finished + */ + enable_irq(); + return true; +} + +static void trampoline(int sig) +{ + (void)sig; + /* sanity check, no other thread should be here */ + if (pthread_self() != main_thread) + return; + + if (setjmp(*((jmp_buf *)&tramp_buf)) == 0) + { + sig_handler_called = true; + return; + } + /* longjump'd back in */ + bootstrap_context(); +} + +void bootstrap_context(void) +{ + /* copy to local storage so we can spawn further threads + * in the meantime */ + void (*thread_entry)(void) = thread_func; + struct ctx *t = thread_context; + + /* + * Save current machine state (on new stack) and + * go back to caller until we're scheduled for real... + */ + if (setjmp(t->thread_buf) == 0) + longjmp(*((jmp_buf *)&bootstrap_buf), 1); + + /* + * The new thread is now running: GREAT! + * Now we just invoke its init function.... + */ + thread_entry(); + DEBUGF("thread left\n"); + thread_exit(); +} + +static inline void set_context(struct ctx *c) +{ + longjmp(c->thread_buf, 1); +} + +static inline void swap_context(struct ctx *old, struct ctx *new) +{ + if (setjmp(old->thread_buf) == 0) + longjmp(new->thread_buf, 1); +} + +static inline void get_context(struct ctx *c) +{ + setjmp(c->thread_buf); +} + + +static void setup_thread(struct regs *context); + +#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 = &thread_bufs[curr_uc++]; + get_context(context->uc); +} + +#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; }) + + + +/* + * 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->uc = &thread_bufs[curr_uc++]; + while (!make_context(context->uc, fn, (char*)context->stack, context->stack_size)) + DEBUGF("Thread creation failed. Retrying"); +} + + +/* + * 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; + } + swap_context(target_context, r->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/target/hosted/thread-win32.c b/firmware/target/hosted/thread-win32.c new file mode 100644 index 0000000000..a60198494a --- /dev/null +++ b/firmware/target/hosted/thread-win32.c @@ -0,0 +1,85 @@ +/*************************************************************************** + * __________ __ ___. + * 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 <windows.h> +#include "system.h" + +#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); + /* can't assign stack pointer, only stack size */ + context->stack_size = 0; + context->start = NULL; + } + SwitchToFiber(context->uc); +} + +/* + * play nice with the host and sleep while waiting for the tick */ +static inline void core_sleep(void) +{ + enable_irq(); + wait_for_interrupt(); +} + diff --git a/firmware/thread.c b/firmware/thread.c index b71bc1749c..a81f56cdfe 100644 --- a/firmware/thread.c +++ b/firmware/thread.c @@ -174,10 +174,13 @@ 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" + +#if defined(HAVE_WIN32_FIBER_THREADS) +#include "thread-win32.c" +#elif defined(HAVE_SIGALTSTACK_THREADS) +#include "thread-unix.c" #elif defined(CPU_ARM) #include "thread-arm.c" #if defined (CPU_PP) @@ -2308,6 +2311,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 bcb7d0a864..889012ab78 100755 --- a/tools/configure +++ b/tools/configure @@ -25,6 +25,7 @@ bindir= libdir= sharedir= +thread_support="ASSEMBLER_THREADS" app_modelname= app_lcd_width= app_lcd_height= @@ -163,6 +164,39 @@ findsdl(){ done } +# check for availability of sigaltstack to support our thread engine +check_sigaltstack() { + cat >$tmpdir/check_threads.c <<EOF +#include <signal.h> +int main(int argc, char **argv) +{ + #define NULL (void*)0 + sigaltstack(NULL, NULL); + return 0; +} +EOF + $CC -o $tmpdir/check_threads $tmpdir/check_threads.c 1> /dev/null + result=$? + rm -rf $tmpdir/check_threads* + echo $result +} + +# check for availability of Fiber on Win32 to support our thread engine +check_fiber() { + cat >$tmpdir/check_threads.c <<EOF +#include <windows.h> +int main(int argc, char **argv) +{ + ConvertThreadToFiber(NULL); + return 0; +} +EOF + $CC -o $tmpdir/check_threads $tmpdir/check_threads.c 2>/dev/null + result=$? + rm -rf $tmpdir/check_threads* + echo $result +} + simcc () { # default tool setup for native building @@ -175,6 +209,8 @@ simcc () { GCCOPTS="$GCCOPTS -fno-builtin -g" GCCOPTIMIZE='' LDOPTS='-lm' # button-sdl.c uses sqrt() + sigaltstack="" + fibers="" # default output binary name, don't override app_get_platform() if [ "$app_type" != "sdl-app" ]; then @@ -193,6 +229,7 @@ simcc () { CYGWIN*) echo "Cygwin host detected" + fibers=`check_fiber` LDOPTS="$LDOPTS -mconsole" output="$output.exe" winbuild="yes" @@ -201,29 +238,33 @@ simcc () { MINGW*) echo "MinGW host detected" + fibers=`check_fiber` LDOPTS="$LDOPTS -mconsole" output="$output.exe" winbuild="yes" ;; Linux) + sigaltstack=`check_sigaltstack` echo "Linux host detected" LDOPTS="$LDOPTS -ldl" ;; FreeBSD) + sigaltstack=`check_sigaltstack` echo "FreeBSD host detected" LDOPTS="$LDOPTS -ldl" ;; Darwin) + sigaltstack=`check_sigaltstack` echo "Darwin host detected" LDOPTS="$LDOPTS -ldl" - SHARED_FLAG="-dynamiclib -Wl\,-single_module" ;; SunOS) + sigaltstack=`check_sigaltstack` echo "*Solaris host detected" GCCOPTS="$GCCOPTS -fPIC" @@ -319,11 +360,33 @@ EOF # add cross-compiler option(s) prefixtools i586-mingw32msvc- LDOPTS="$LDOPTS -mconsole" + fibers=`check_fiber` output="rockboxui.exe" endian="little" # windows is little endian echo "Enabling MMX support" GCCOPTS="$GCCOPTS -mmmx" fi + + thread_support= + if [ -z "$ARG_THREAD_SUPPORT" ] || [ "$ARG_THREAD_SUPPORT" = "0" ]; then + if [ "$sigaltstack" = "0" ]; then + thread_support="HAVE_SIGALTSTACK_THREADS" + echo "Selected sigaltstack threads" + elif [ "$fibers" = "0" ]; then + thread_support="HAVE_WIN32_FIBER_THREADS" + echo "Selected Win32 Fiber threads" + fi + fi + + if [ -n `echo $app_type | grep "sdl"` ] && [ -z "$thread_support" ] \ + && [ "$ARG_THREAD_SUPPORT" != "0" ]; then + thread_support="HAVE_SDL_THREADS" + if [ "$ARG_THREAD_SUPPORT" = "1" ]; then + echo "Selected SDL threads" + else + echo "WARNING: Falling back to SDL threads" + fi + fi } # @@ -994,6 +1057,11 @@ help() { --thumb Build with -mthumb (for ARM builds) --no-thumb The opposite of --thumb (don't use thumb even for targets where this is the default + --sdl-threads Force use of SDL threads. They have inferior performance, + but are better debuggable with GDB + --no-sdl-threads Disallow use of SDL threads. This prevents the default + behavior of falling back to them if no native thread + support was found. --prefix Target installation directory --help Shows this message (must not be used with other options) @@ -1015,6 +1083,7 @@ ARG_VOICE= ARG_ARM_EABI= ARG_ARM_THUMB= ARG_PREFIX="$PREFIX" +ARG_THREAD_SUPPORT= err= for arg in "$@"; do case "$arg" in @@ -1035,6 +1104,9 @@ for arg in "$@"; do --no-eabi) ARG_ARM_EABI=0;; --thumb) ARG_ARM_THUMB=1;; --no-thumb) ARG_ARM_THUMB=0;; + --sdl-threads)ARG_THREAD_SUPPORT=1;; + --no-sdl-threads) + ARG_THREAD_SUPPORT=0;; --prefix=*) ARG_PREFIX=`echo "$arg" | cut -d = -f 2`;; --help) help;; *) err=1; echo "[ERROR] Option '$arg' unsupported";; @@ -3326,6 +3398,7 @@ sed > autoconf.h \ -e "s<^#undef DO_BOOTCHART<$use_bootchart<g" \ -e "s<@config_rtc@<$config_rtc<g" \ -e "s<@have_rtc_alarm@<$have_rtc_alarm<g" \ + -e "s<@thread_support@<$thread_support<g" \ -e "s<@RBDIR@<${rbdir}<g" \ -e "s<@sharepath@<${sharedir}<g" \ -e "s<@binpath@<${bindir}<g" \ @@ -3362,6 +3435,9 @@ sed > autoconf.h \ @config_rtc@ @have_rtc_alarm@ +/* the threading backend we use */ +#define @thread_support@ + /* lcd dimensions for application builds from configure */ @lcd_width@ @lcd_height@ diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c index f77180dc21..0d680e0e8a 100644 --- a/uisimulator/common/io.c +++ b/uisimulator/common/io.c @@ -46,9 +46,12 @@ #endif #include <fcntl.h> -#include <SDL.h> -#include <SDL_thread.h> +#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" |