summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-02-18 22:46:01 +0000
committerThomas Martitz <kugel@rockbox.org>2011-02-18 22:46:01 +0000
commit6d85de341928aef8178465c60122f3cbe76f5dd6 (patch)
treeff86c384a574ac20d3418c1b904ed4d0de1f6980
parent3926c30705cc7235122e2f2e35ab506b53238cdf (diff)
downloadrockbox-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/SOURCES4
-rw-r--r--firmware/export/config.h4
-rw-r--r--firmware/export/thread.h27
-rw-r--r--firmware/target/hosted/sdl/kernel-sdl.c62
-rw-r--r--firmware/target/hosted/sdl/system-sdl.c21
-rw-r--r--firmware/target/hosted/sdl/system-sdl.h3
-rw-r--r--firmware/target/hosted/sdl/thread-sdl.h2
-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.c294
-rw-r--r--firmware/target/hosted/thread-win32.c85
-rw-r--r--firmware/thread.c12
-rwxr-xr-xtools/configure78
-rw-r--r--uisimulator/common/io.c7
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"