summaryrefslogtreecommitdiff
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-6d85de341928aef8178465c60122f3cbe76f5dd6.tar.gz
rockbox-6d85de341928aef8178465c60122f3cbe76f5dd6.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
37#endif 37#endif
38target/hosted/sdl/lcd-sdl.c 38target/hosted/sdl/lcd-sdl.c
39target/hosted/sdl/system-sdl.c 39target/hosted/sdl/system-sdl.c
40#ifdef HAVE_SDL_THREADS
40target/hosted/sdl/thread-sdl.c 41target/hosted/sdl/thread-sdl.c
42#else
43thread.c
44#endif
41target/hosted/sdl/timer-sdl.c 45target/hosted/sdl/timer-sdl.c
42#ifdef HAVE_TOUCHSCREEN 46#ifdef HAVE_TOUCHSCREEN
43target/hosted/sdl/key_to_touch-sdl.c 47target/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 */
717#define HAVE_WAKEUP_EXT_CB 717#define HAVE_WAKEUP_EXT_CB
718 718
719 719
720#if (CONFIG_PLATFORM & PLATFORM_ANDROID) 720#if defined(ASSEMBLER_THREADS) \
721 || defined(HAVE_WIN32_FIBER_THREADS) \
722 || defined(HAVE_SIGALTSTACK_THREADS)
721#define HAVE_PRIORITY_SCHEDULING 723#define HAVE_PRIORITY_SCHEDULING
722#endif 724#endif
723 725
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 @@
84 * We need more stack when we run under a host 84 * We need more stack when we run under a host
85 * maybe more expensive C lib functions? 85 * maybe more expensive C lib functions?
86 * 86 *
87 * simulator doesn't simulate stack usage anyway but well ... */ 87 * simulator (possibly) doesn't simulate stack usage anyway but well ... */
88#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SIMULATOR)) 88#ifdef HAVE_SIGALTSTACK_THREADS
89#define DEFAULT_STACK_SIZE 0x400 /* Bytes */ 89#include <signal.h>
90#else 90/* MINSIGSTKSZ for the OS to deliver the signal + 0x3000 for us */
91#define DEFAULT_STACK_SIZE (MINSIGSTKSZ+0x3000) /* Bytes */
92#elif (CONFIG_PLATFORM & PLATFORM_ANDROID) || defined(HAVE_WIN32_FIBER_THREADS)
91#define DEFAULT_STACK_SIZE 0x1000 /* Bytes */ 93#define DEFAULT_STACK_SIZE 0x1000 /* Bytes */
94#else /* native threads, sdl threads */
95#define DEFAULT_STACK_SIZE 0x400 /* Bytes */
92#endif 96#endif
93 97
94 98
95#if (CONFIG_PLATFORM & (PLATFORM_NATIVE|PLATFORM_ANDROID)) 99#if defined(ASSEMBLER_THREADS)
96/* Need to keep structures inside the header file because debug_menu 100/* Need to keep structures inside the header file because debug_menu
97 * needs them. */ 101 * needs them. */
98#ifdef CPU_COLDFIRE 102#ifdef CPU_COLDFIRE
@@ -112,7 +116,7 @@ struct regs
112 uint32_t pr; /* 32 - Procedure register */ 116 uint32_t pr; /* 32 - Procedure register */
113 uint32_t start; /* 36 - Thread start address, or NULL when started */ 117 uint32_t start; /* 36 - Thread start address, or NULL when started */
114}; 118};
115#elif defined(CPU_ARM) || (CONFIG_PLATFORM & PLATFORM_ANDROID) 119#elif defined(CPU_ARM)
116struct regs 120struct regs
117{ 121{
118 uint32_t r[8]; /* 0-28 - Registers r4-r11 */ 122 uint32_t r[8]; /* 0-28 - Registers r4-r11 */
@@ -147,6 +151,16 @@ struct regs
147}; 151};
148#endif /* CONFIG_CPU */ 152#endif /* CONFIG_CPU */
149#elif (CONFIG_PLATFORM & PLATFORM_HOSTED) 153#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
154#ifndef HAVE_SDL_THREADS
155struct regs
156{
157 void (*start)(void); /* thread's entry point, or NULL when started */
158 void* uc; /* host thread handle */
159 uintptr_t sp; /* Stack pointer, unused */
160 size_t stack_size; /* stack size, not always used */
161 uintptr_t stack; /* pointer to start of the stack buffer */
162};
163#else /* SDL threads */
150struct regs 164struct regs
151{ 165{
152 void *t; /* OS thread */ 166 void *t; /* OS thread */
@@ -154,6 +168,7 @@ struct regs
154 void *s; /* Semaphore for blocking and wakeup */ 168 void *s; /* Semaphore for blocking and wakeup */
155 void (*start)(void); /* Start function */ 169 void (*start)(void); /* Start function */
156}; 170};
171#endif
157#endif /* PLATFORM_NATIVE */ 172#endif /* PLATFORM_NATIVE */
158 173
159/* NOTE: The use of the word "queue" may also refer to a linked list of 174/* 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 @@
34static SDL_TimerID tick_timer_id; 34static SDL_TimerID tick_timer_id;
35long start_tick; 35long start_tick;
36 36
37#ifndef HAVE_SDL_THREADS
38/* for the wait_for_interrupt function */
39static bool do_exit;
40static SDL_cond *wfi_cond;
41static SDL_mutex *wfi_mutex;
42#else
43#define do_exit false
44#endif
37/* Condition to signal that "interrupts" may proceed */ 45/* Condition to signal that "interrupts" may proceed */
38static SDL_cond *sim_thread_cond; 46static SDL_cond *sim_thread_cond;
39/* Mutex to serialize changing levels and exclude other threads while 47/* Mutex to serialize changing levels and exclude other threads while
@@ -98,6 +106,9 @@ void sim_exit_irq_handler(void)
98 106
99 status_reg = 0; 107 status_reg = 0;
100 SDL_UnlockMutex(sim_irq_mtx); 108 SDL_UnlockMutex(sim_irq_mtx);
109#ifndef HAVE_SDL_THREADS
110 SDL_CondSignal(wfi_cond);
111#endif
101} 112}
102 113
103static bool sim_kernel_init(void) 114static bool sim_kernel_init(void)
@@ -115,15 +126,33 @@ static bool sim_kernel_init(void)
115 panicf("Cannot create sim_thread_cond\n"); 126 panicf("Cannot create sim_thread_cond\n");
116 return false; 127 return false;
117 } 128 }
118 129#ifndef HAVE_SDL_THREADS
130 wfi_cond = SDL_CreateCond();
131 if (wfi_cond == NULL)
132 {
133 panicf("Cannot create wfi\n");
134 return false;
135 }
136 wfi_mutex = SDL_CreateMutex();
137 if (wfi_mutex == NULL)
138 {
139 panicf("Cannot create wfi mutex\n");
140 return false;
141 }
142#endif
119 return true; 143 return true;
120} 144}
121 145
122void sim_kernel_shutdown(void) 146void sim_kernel_shutdown(void)
123{ 147{
124 SDL_RemoveTimer(tick_timer_id); 148 SDL_RemoveTimer(tick_timer_id);
149#ifndef HAVE_SDL_THREADS
150 do_exit = true;
151 SDL_CondSignal(wfi_cond);
152#endif
153 disable_irq();
125 SDL_DestroyMutex(sim_irq_mtx); 154 SDL_DestroyMutex(sim_irq_mtx);
126 SDL_DestroyCond(sim_thread_cond); 155 SDL_DestroyCond(sim_thread_cond);
127} 156}
128 157
129Uint32 tick_timer(Uint32 interval, void *param) 158Uint32 tick_timer(Uint32 interval, void *param)
@@ -135,7 +164,7 @@ Uint32 tick_timer(Uint32 interval, void *param)
135 164
136 new_tick = (SDL_GetTicks() - start_tick) / (1000/HZ); 165 new_tick = (SDL_GetTicks() - start_tick) / (1000/HZ);
137 166
138 while(new_tick != current_tick) 167 while(new_tick != current_tick && !do_exit)
139 { 168 {
140 sim_enter_irq_handler(); 169 sim_enter_irq_handler();
141 170
@@ -146,7 +175,7 @@ Uint32 tick_timer(Uint32 interval, void *param)
146 sim_exit_irq_handler(); 175 sim_exit_irq_handler();
147 } 176 }
148 177
149 return interval; 178 return do_exit ? 0 : interval;
150} 179}
151 180
152void tick_start(unsigned int interval_in_ms) 181void tick_start(unsigned int interval_in_ms)
@@ -168,4 +197,29 @@ void tick_start(unsigned int interval_in_ms)
168 } 197 }
169 198
170 tick_timer_id = SDL_AddTimer(interval_in_ms, tick_timer, NULL); 199 tick_timer_id = SDL_AddTimer(interval_in_ms, tick_timer, NULL);
200#ifndef HAVE_SDL_THREADS
201 SDL_LockMutex(wfi_mutex);
202#endif
203}
204
205#ifndef HAVE_SDL_THREADS
206static void check_exit(void)
207{
208 if (UNLIKELY(do_exit))
209 {
210 SDL_DestroyCond(wfi_cond);
211 SDL_UnlockMutex(wfi_mutex);
212 SDL_DestroyMutex(wfi_mutex);
213 sim_do_exit();
214 }
215}
216
217void wait_for_interrupt(void)
218{
219 /* the exit may come at any time, during the CondWait or before,
220 * so check it twice */
221 check_exit();
222 SDL_CondWait(wfi_cond, wfi_mutex);
223 check_exit();
171} 224}
225#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)
184 184
185 /* Order here is relevent to prevent deadlocks and use of destroyed 185 /* Order here is relevent to prevent deadlocks and use of destroyed
186 sync primitives by kernel threads */ 186 sync primitives by kernel threads */
187 sim_thread_shutdown(); 187#ifdef HAVE_SDL_THREADS
188 sim_thread_shutdown(); /* not needed for native threads */
189#endif
188 sim_kernel_shutdown(); 190 sim_kernel_shutdown();
189 191
190 return 0; 192 return 0;
@@ -199,9 +201,13 @@ void sim_do_exit(void)
199 exit(EXIT_SUCCESS); 201 exit(EXIT_SUCCESS);
200} 202}
201 203
204uintptr_t *stackbegin;
205uintptr_t *stackend;
202void system_init(void) 206void system_init(void)
203{ 207{
204 SDL_sem *s; 208 SDL_sem *s;
209 /* fake stack, OS manages size (and growth) */
210 stackbegin = stackend = (uintptr_t*)&s;
205 211
206#if (CONFIG_PLATFORM & PLATFORM_MAEMO) 212#if (CONFIG_PLATFORM & PLATFORM_MAEMO)
207 /* Make glib thread safe */ 213 /* Make glib thread safe */
@@ -219,21 +225,24 @@ void system_init(void)
219 /* wait for sdl_event_thread to run so that it can initialize the surfaces 225 /* wait for sdl_event_thread to run so that it can initialize the surfaces
220 * and video subsystem needed for SDL events */ 226 * and video subsystem needed for SDL events */
221 SDL_SemWait(s); 227 SDL_SemWait(s);
222
223 /* cleanup */ 228 /* cleanup */
224 SDL_DestroySemaphore(s); 229 SDL_DestroySemaphore(s);
225} 230}
226 231
227void system_exception_wait(void)
228{
229 sim_thread_exception_wait();
230}
231 232
232void system_reboot(void) 233void system_reboot(void)
233{ 234{
235#ifdef HAVE_SDL_THREADS
234 sim_thread_exception_wait(); 236 sim_thread_exception_wait();
237#else
238 sim_do_exit();
239#endif
235} 240}
236 241
242void system_exception_wait(void)
243{
244 system_reboot();
245}
237 246
238void sys_handle_argv(int argc, char *argv[]) 247void sys_handle_argv(int argc, char *argv[])
239{ 248{
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);
46void sys_handle_argv(int argc, char *argv[]); 46void sys_handle_argv(int argc, char *argv[]);
47void gui_message_loop(void); 47void gui_message_loop(void);
48void sim_do_exit(void); 48void sim_do_exit(void);
49#ifndef HAVE_SDL_THREADS
50void wait_for_interrupt(void);
51#endif
49 52
50extern bool background; /* True if the background image is enabled */ 53extern bool background; /* True if the background image is enabled */
51extern bool showremote; 54extern 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 @@
22#ifndef __THREADSDL_H__ 22#ifndef __THREADSDL_H__
23#define __THREADSDL_H__ 23#define __THREADSDL_H__
24 24
25#ifdef HAVE_SDL_THREADS
25/* extra thread functions that only apply when running on hosting platforms */ 26/* extra thread functions that only apply when running on hosting platforms */
26void sim_thread_lock(void *me); 27void sim_thread_lock(void *me);
27void * sim_thread_unlock(void); 28void * sim_thread_unlock(void);
28void sim_thread_exception_wait(void); 29void sim_thread_exception_wait(void);
29void sim_thread_shutdown(void); /* Shut down all kernel threads gracefully */ 30void sim_thread_shutdown(void); /* Shut down all kernel threads gracefully */
31#endif
30 32
31#endif /* #ifndef __THREADSDL_H__ */ 33#endif /* #ifndef __THREADSDL_H__ */
32 34
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 @@
22 * 22 *
23 ****************************************************************************/ 23 ****************************************************************************/
24 24
25#include <jni.h>
26#include <system.h> 25#include <system.h>
27/*--------------------------------------------------------------------------- 26/*---------------------------------------------------------------------------
28 * Start the thread running and terminate it if it returns 27 * Start the thread running and terminate it if it returns
@@ -84,14 +83,15 @@ static inline void load_context(const void* addr)
84 * this core sleep suspends the OS thread rockbox runs under, which greatly 83 * this core sleep suspends the OS thread rockbox runs under, which greatly
85 * reduces cpu usage (~100% to <10%) 84 * reduces cpu usage (~100% to <10%)
86 * 85 *
87 * it returns when the RockboxTimer notified us, i.e. at each tick 86 * it returns when when the tick timer is called, other interrupt-like
88 * (after it called the tick tasks) 87 * events occur
89 * 88 *
90 * wait_for_interrupt is implemented in kernel-android.c 89 * wait_for_interrupt is implemented in kernel-<platform>.c
91 **/ 90 **/
92 91
93static inline void core_sleep(void) 92static inline void core_sleep(void)
94{ 93{
94 enable_irq();
95 wait_for_interrupt(); 95 wait_for_interrupt();
96} 96}
97 97
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 @@
1#include <stdlib.h>
2#include <stdbool.h>
3#include <signal.h>
4#include <stdio.h>
5#include <setjmp.h>
6#include <unistd.h>
7#include <pthread.h>
8#include <errno.h>
9#include "debug.h"
10
11static volatile bool sig_handler_called;
12static volatile jmp_buf tramp_buf;
13static volatile jmp_buf bootstrap_buf;
14static void (*thread_func)(void);
15static const int trampoline_sig = SIGUSR1;
16static pthread_t main_thread;
17
18static struct ctx {
19 jmp_buf thread_buf;
20} thread_bufs[MAXTHREADS];
21static struct ctx* thread_context, *target_context;
22static int curr_uc;
23
24static void trampoline(int sig);
25static void bootstrap_context(void) __attribute__((noinline));
26
27/* The *_context functions are heavily based on Gnu pth
28 * http://www.gnu.org/software/pth/
29 *
30 * adjusted to work in a multi-thread environment to
31 * offer a ucontext-like API
32 */
33
34/*
35 * VARIANT 2: THE SIGNAL STACK TRICK
36 *
37 * This uses sigstack/sigaltstack() and friends and is really the
38 * most tricky part of Pth. When you understand the following
39 * stuff you're a good Unix hacker and then you've already
40 * understood the gory ingredients of Pth. So, either welcome to
41 * the club of hackers, or do yourself a favor and skip this ;)
42 *
43 * The ingenious fact is that this variant runs really on _all_ POSIX
44 * compliant systems without special platform kludges. But be _VERY_
45 * carefully when you change something in the following code. The slightest
46 * change or reordering can lead to horribly broken code. Really every
47 * function call in the following case is intended to be how it is, doubt
48 * me...
49 *
50 * For more details we strongly recommend you to read the companion
51 * paper ``Portable Multithreading -- The Signal Stack Trick for
52 * User-Space Thread Creation'' from Ralf S. Engelschall. A copy of the
53 * draft of this paper you can find in the file rse-pmt.ps inside the
54 * GNU Pth distribution.
55 */
56
57static int make_context(struct ctx *ctx, void (*f)(void), char *sp, size_t stack_size)
58{
59 struct sigaction sa;
60 struct sigaction osa;
61 stack_t ss;
62 stack_t oss;
63 sigset_t osigs;
64 sigset_t sigs;
65
66 disable_irq();
67 /*
68 * Preserve the trampoline_sig signal state, block trampoline_sig,
69 * and establish our signal handler. The signal will
70 * later transfer control onto the signal stack.
71 */
72 sigemptyset(&sigs);
73 sigaddset(&sigs, trampoline_sig);
74 sigprocmask(SIG_BLOCK, &sigs, &osigs);
75 sa.sa_handler = trampoline;
76 sigemptyset(&sa.sa_mask);
77 sa.sa_flags = SA_ONSTACK;
78 if (sigaction(trampoline_sig, &sa, &osa) != 0)
79 {
80 DEBUGF("%s(): %s\n", __func__, strerror(errno));
81 return false;
82 }
83 /*
84 * Set the new stack.
85 *
86 * For sigaltstack we're lucky [from sigaltstack(2) on
87 * FreeBSD 3.1]: ``Signal stacks are automatically adjusted
88 * for the direction of stack growth and alignment
89 * requirements''
90 *
91 * For sigstack we have to decide ourself [from sigstack(2)
92 * on Solaris 2.6]: ``The direction of stack growth is not
93 * indicated in the historical definition of struct sigstack.
94 * The only way to portably establish a stack pointer is for
95 * the application to determine stack growth direction.''
96 */
97 ss.ss_sp = sp;
98 ss.ss_size = stack_size;
99 ss.ss_flags = 0;
100 if (sigaltstack(&ss, &oss) < 0)
101 {
102 DEBUGF("%s(): %s\n", __func__, strerror(errno));
103 return false;
104 }
105
106 /*
107 * Now transfer control onto the signal stack and set it up.
108 * It will return immediately via "return" after the setjmp()
109 * was performed. Be careful here with race conditions. The
110 * signal can be delivered the first time sigsuspend() is
111 * called.
112 */
113 sig_handler_called = false;
114 main_thread = pthread_self();
115 sigfillset(&sigs);
116 sigdelset(&sigs, trampoline_sig);
117 pthread_kill(main_thread, trampoline_sig);
118 while(!sig_handler_called)
119 sigsuspend(&sigs);
120
121 /*
122 * Inform the system that we are back off the signal stack by
123 * removing the alternative signal stack. Be careful here: It
124 * first has to be disabled, before it can be removed.
125 */
126 sigaltstack(NULL, &ss);
127 ss.ss_flags = SS_DISABLE;
128 if (sigaltstack(&ss, NULL) < 0)
129 {
130 DEBUGF("%s(): %s\n", __func__, strerror(errno));
131 return false;
132 }
133 sigaltstack(NULL, &ss);
134 if (!(ss.ss_flags & SS_DISABLE))
135 {
136 DEBUGF("%s(): %s\n", __func__, strerror(errno));
137 return false;
138 }
139 if (!(oss.ss_flags & SS_DISABLE))
140 sigaltstack(&oss, NULL);
141
142 /*
143 * Restore the old trampoline_sig signal handler and mask
144 */
145 sigaction(trampoline_sig, &osa, NULL);
146 sigprocmask(SIG_SETMASK, &osigs, NULL);
147
148 /*
149 * Tell the trampoline and bootstrap function where to dump
150 * the new machine context, and what to do afterwards...
151 */
152 thread_func = f;
153 thread_context = ctx;
154
155 /*
156 * Now enter the trampoline again, but this time not as a signal
157 * handler. Instead we jump into it directly. The functionally
158 * redundant ping-pong pointer arithmentic is neccessary to avoid
159 * type-conversion warnings related to the `volatile' qualifier and
160 * the fact that `jmp_buf' usually is an array type.
161 */
162 if (setjmp(*((jmp_buf *)&bootstrap_buf)) == 0)
163 longjmp(*((jmp_buf *)&tramp_buf), 1);
164
165 /*
166 * Ok, we returned again, so now we're finished
167 */
168 enable_irq();
169 return true;
170}
171
172static void trampoline(int sig)
173{
174 (void)sig;
175 /* sanity check, no other thread should be here */
176 if (pthread_self() != main_thread)
177 return;
178
179 if (setjmp(*((jmp_buf *)&tramp_buf)) == 0)
180 {
181 sig_handler_called = true;
182 return;
183 }
184 /* longjump'd back in */
185 bootstrap_context();
186}
187
188void bootstrap_context(void)
189{
190 /* copy to local storage so we can spawn further threads
191 * in the meantime */
192 void (*thread_entry)(void) = thread_func;
193 struct ctx *t = thread_context;
194
195 /*
196 * Save current machine state (on new stack) and
197 * go back to caller until we're scheduled for real...
198 */
199 if (setjmp(t->thread_buf) == 0)
200 longjmp(*((jmp_buf *)&bootstrap_buf), 1);
201
202 /*
203 * The new thread is now running: GREAT!
204 * Now we just invoke its init function....
205 */
206 thread_entry();
207 DEBUGF("thread left\n");
208 thread_exit();
209}
210
211static inline void set_context(struct ctx *c)
212{
213 longjmp(c->thread_buf, 1);
214}
215
216static inline void swap_context(struct ctx *old, struct ctx *new)
217{
218 if (setjmp(old->thread_buf) == 0)
219 longjmp(new->thread_buf, 1);
220}
221
222static inline void get_context(struct ctx *c)
223{
224 setjmp(c->thread_buf);
225}
226
227
228static void setup_thread(struct regs *context);
229
230#define INIT_MAIN_THREAD
231static void init_main_thread(void *addr)
232{
233 /* get a context for the main thread so that we can jump to it from
234 * other threads */
235 struct regs *context = (struct regs*)addr;
236 context->uc = &thread_bufs[curr_uc++];
237 get_context(context->uc);
238}
239
240#define THREAD_STARTUP_INIT(core, thread, function) \
241 ({ (thread)->context.stack_size = (thread)->stack_size, \
242 (thread)->context.stack = (uintptr_t)(thread)->stack; \
243 (thread)->context.start = function; })
244
245
246
247/*
248 * Prepare context to make the thread runnable by calling swapcontext on it
249 */
250static void setup_thread(struct regs *context)
251{
252 void (*fn)(void) = context->start;
253 context->uc = &thread_bufs[curr_uc++];
254 while (!make_context(context->uc, fn, (char*)context->stack, context->stack_size))
255 DEBUGF("Thread creation failed. Retrying");
256}
257
258
259/*
260 * Save the ucontext_t pointer for later use in swapcontext()
261 *
262 * Cannot do getcontext() here, because jumping back to the context
263 * resumes after the getcontext call (i.e. store_context), but we need
264 * to resume from load_context()
265 */
266static inline void store_context(void* addr)
267{
268 struct regs *r = (struct regs*)addr;
269 target_context = r->uc;
270}
271
272/*
273 * Perform context switch
274 */
275static inline void load_context(const void* addr)
276{
277 struct regs *r = (struct regs*)addr;
278 if (UNLIKELY(r->start))
279 {
280 setup_thread(r);
281 r->start = NULL;
282 }
283 swap_context(target_context, r->uc);
284}
285
286/*
287 * play nice with the host and sleep while waiting for the tick */
288extern void wait_for_interrupt(void);
289static inline void core_sleep(void)
290{
291 enable_irq();
292 wait_for_interrupt();
293}
294
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 by Thomas Martitz
11 *
12 * Generic ARM threading support
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24
25#include <windows.h>
26#include "system.h"
27
28#define INIT_MAIN_THREAD
29
30#define THREAD_STARTUP_INIT(core, thread, function) \
31 ({ (thread)->context.stack_size = (thread)->stack_size, \
32 (thread)->context.stack = (uintptr_t)(thread)->stack; \
33 (thread)->context.start = function; })
34
35static void init_main_thread(void *addr)
36{
37 struct regs *context = (struct regs*)addr;
38 /* we must convert the current main thread to a fiber to be able to
39 * schedule other fibers */
40 context->uc = ConvertThreadToFiber(NULL);
41 context->stack_size = 0;
42}
43
44static inline void store_context(void* addr)
45{
46 (void)addr;
47 /* nothing to do here, Fibers continue after the SwitchToFiber call */
48}
49
50static void start_thread(void)
51{
52 void (*func)(void) = GetFiberData();
53 func();
54 /* go out if thread function returns */
55 thread_exit();
56}
57
58/*
59 * Load context and run it
60 *
61 * Resume execution from the last load_context call for the thread
62 */
63
64static inline void load_context(const void* addr)
65{
66 struct regs *context = (struct regs*)addr;
67 if (UNLIKELY(context->start))
68 { /* need setup before switching to it */
69 context->uc = CreateFiber(context->stack_size,
70 (LPFIBER_START_ROUTINE)start_thread, context->start);
71 /* can't assign stack pointer, only stack size */
72 context->stack_size = 0;
73 context->start = NULL;
74 }
75 SwitchToFiber(context->uc);
76}
77
78/*
79 * play nice with the host and sleep while waiting for the tick */
80static inline void core_sleep(void)
81{
82 enable_irq();
83 wait_for_interrupt();
84}
85
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)
174 __attribute__((noinline)); 174 __attribute__((noinline));
175 175
176/**************************************************************************** 176/****************************************************************************
177 * Processor-specific section - include necessary core support 177 * Processor/OS-specific section - include necessary core support
178 */ 178 */
179#if defined(ANDROID) 179
180#include "thread-android-arm.c" 180#if defined(HAVE_WIN32_FIBER_THREADS)
181#include "thread-win32.c"
182#elif defined(HAVE_SIGALTSTACK_THREADS)
183#include "thread-unix.c"
181#elif defined(CPU_ARM) 184#elif defined(CPU_ARM)
182#include "thread-arm.c" 185#include "thread-arm.c"
183#if defined (CPU_PP) 186#if defined (CPU_PP)
@@ -2308,6 +2311,9 @@ void init_threads(void)
2308 thread_exit(); 2311 thread_exit();
2309#endif /* NUM_CORES */ 2312#endif /* NUM_CORES */
2310 } 2313 }
2314#ifdef INIT_MAIN_THREAD
2315 init_main_thread(&thread->context);
2316#endif
2311} 2317}
2312 2318
2313/* Shared stack scan helper for thread_stack_usage and idle_stack_usage */ 2319/* 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=
25libdir= 25libdir=
26sharedir= 26sharedir=
27 27
28thread_support="ASSEMBLER_THREADS"
28app_modelname= 29app_modelname=
29app_lcd_width= 30app_lcd_width=
30app_lcd_height= 31app_lcd_height=
@@ -163,6 +164,39 @@ findsdl(){
163 done 164 done
164} 165}
165 166
167# check for availability of sigaltstack to support our thread engine
168check_sigaltstack() {
169 cat >$tmpdir/check_threads.c <<EOF
170#include <signal.h>
171int main(int argc, char **argv)
172{
173 #define NULL (void*)0
174 sigaltstack(NULL, NULL);
175 return 0;
176}
177EOF
178 $CC -o $tmpdir/check_threads $tmpdir/check_threads.c 1> /dev/null
179 result=$?
180 rm -rf $tmpdir/check_threads*
181 echo $result
182}
183
184# check for availability of Fiber on Win32 to support our thread engine
185check_fiber() {
186 cat >$tmpdir/check_threads.c <<EOF
187#include <windows.h>
188int main(int argc, char **argv)
189{
190 ConvertThreadToFiber(NULL);
191 return 0;
192}
193EOF
194 $CC -o $tmpdir/check_threads $tmpdir/check_threads.c 2>/dev/null
195 result=$?
196 rm -rf $tmpdir/check_threads*
197 echo $result
198}
199
166simcc () { 200simcc () {
167 201
168 # default tool setup for native building 202 # default tool setup for native building
@@ -175,6 +209,8 @@ simcc () {
175 GCCOPTS="$GCCOPTS -fno-builtin -g" 209 GCCOPTS="$GCCOPTS -fno-builtin -g"
176 GCCOPTIMIZE='' 210 GCCOPTIMIZE=''
177 LDOPTS='-lm' # button-sdl.c uses sqrt() 211 LDOPTS='-lm' # button-sdl.c uses sqrt()
212 sigaltstack=""
213 fibers=""
178 214
179 # default output binary name, don't override app_get_platform() 215 # default output binary name, don't override app_get_platform()
180 if [ "$app_type" != "sdl-app" ]; then 216 if [ "$app_type" != "sdl-app" ]; then
@@ -193,6 +229,7 @@ simcc () {
193 CYGWIN*) 229 CYGWIN*)
194 echo "Cygwin host detected" 230 echo "Cygwin host detected"
195 231
232 fibers=`check_fiber`
196 LDOPTS="$LDOPTS -mconsole" 233 LDOPTS="$LDOPTS -mconsole"
197 output="$output.exe" 234 output="$output.exe"
198 winbuild="yes" 235 winbuild="yes"
@@ -201,29 +238,33 @@ simcc () {
201 MINGW*) 238 MINGW*)
202 echo "MinGW host detected" 239 echo "MinGW host detected"
203 240
241 fibers=`check_fiber`
204 LDOPTS="$LDOPTS -mconsole" 242 LDOPTS="$LDOPTS -mconsole"
205 output="$output.exe" 243 output="$output.exe"
206 winbuild="yes" 244 winbuild="yes"
207 ;; 245 ;;
208 246
209 Linux) 247 Linux)
248 sigaltstack=`check_sigaltstack`
210 echo "Linux host detected" 249 echo "Linux host detected"
211 LDOPTS="$LDOPTS -ldl" 250 LDOPTS="$LDOPTS -ldl"
212 ;; 251 ;;
213 252
214 FreeBSD) 253 FreeBSD)
254 sigaltstack=`check_sigaltstack`
215 echo "FreeBSD host detected" 255 echo "FreeBSD host detected"
216 LDOPTS="$LDOPTS -ldl" 256 LDOPTS="$LDOPTS -ldl"
217 ;; 257 ;;
218 258
219 Darwin) 259 Darwin)
260 sigaltstack=`check_sigaltstack`
220 echo "Darwin host detected" 261 echo "Darwin host detected"
221 LDOPTS="$LDOPTS -ldl" 262 LDOPTS="$LDOPTS -ldl"
222
223 SHARED_FLAG="-dynamiclib -Wl\,-single_module" 263 SHARED_FLAG="-dynamiclib -Wl\,-single_module"
224 ;; 264 ;;
225 265
226 SunOS) 266 SunOS)
267 sigaltstack=`check_sigaltstack`
227 echo "*Solaris host detected" 268 echo "*Solaris host detected"
228 269
229 GCCOPTS="$GCCOPTS -fPIC" 270 GCCOPTS="$GCCOPTS -fPIC"
@@ -319,11 +360,33 @@ EOF
319 # add cross-compiler option(s) 360 # add cross-compiler option(s)
320 prefixtools i586-mingw32msvc- 361 prefixtools i586-mingw32msvc-
321 LDOPTS="$LDOPTS -mconsole" 362 LDOPTS="$LDOPTS -mconsole"
363 fibers=`check_fiber`
322 output="rockboxui.exe" 364 output="rockboxui.exe"
323 endian="little" # windows is little endian 365 endian="little" # windows is little endian
324 echo "Enabling MMX support" 366 echo "Enabling MMX support"
325 GCCOPTS="$GCCOPTS -mmmx" 367 GCCOPTS="$GCCOPTS -mmmx"
326 fi 368 fi
369
370 thread_support=
371 if [ -z "$ARG_THREAD_SUPPORT" ] || [ "$ARG_THREAD_SUPPORT" = "0" ]; then
372 if [ "$sigaltstack" = "0" ]; then
373 thread_support="HAVE_SIGALTSTACK_THREADS"
374 echo "Selected sigaltstack threads"
375 elif [ "$fibers" = "0" ]; then
376 thread_support="HAVE_WIN32_FIBER_THREADS"
377 echo "Selected Win32 Fiber threads"
378 fi
379 fi
380
381 if [ -n `echo $app_type | grep "sdl"` ] && [ -z "$thread_support" ] \
382 && [ "$ARG_THREAD_SUPPORT" != "0" ]; then
383 thread_support="HAVE_SDL_THREADS"
384 if [ "$ARG_THREAD_SUPPORT" = "1" ]; then
385 echo "Selected SDL threads"
386 else
387 echo "WARNING: Falling back to SDL threads"
388 fi
389 fi
327} 390}
328 391
329# 392#
@@ -994,6 +1057,11 @@ help() {
994 --thumb Build with -mthumb (for ARM builds) 1057 --thumb Build with -mthumb (for ARM builds)
995 --no-thumb The opposite of --thumb (don't use thumb even for targets 1058 --no-thumb The opposite of --thumb (don't use thumb even for targets
996 where this is the default 1059 where this is the default
1060 --sdl-threads Force use of SDL threads. They have inferior performance,
1061 but are better debuggable with GDB
1062 --no-sdl-threads Disallow use of SDL threads. This prevents the default
1063 behavior of falling back to them if no native thread
1064 support was found.
997 --prefix Target installation directory 1065 --prefix Target installation directory
998 --help Shows this message (must not be used with other options) 1066 --help Shows this message (must not be used with other options)
999 1067
@@ -1015,6 +1083,7 @@ ARG_VOICE=
1015ARG_ARM_EABI= 1083ARG_ARM_EABI=
1016ARG_ARM_THUMB= 1084ARG_ARM_THUMB=
1017ARG_PREFIX="$PREFIX" 1085ARG_PREFIX="$PREFIX"
1086ARG_THREAD_SUPPORT=
1018err= 1087err=
1019for arg in "$@"; do 1088for arg in "$@"; do
1020 case "$arg" in 1089 case "$arg" in
@@ -1035,6 +1104,9 @@ for arg in "$@"; do
1035 --no-eabi) ARG_ARM_EABI=0;; 1104 --no-eabi) ARG_ARM_EABI=0;;
1036 --thumb) ARG_ARM_THUMB=1;; 1105 --thumb) ARG_ARM_THUMB=1;;
1037 --no-thumb) ARG_ARM_THUMB=0;; 1106 --no-thumb) ARG_ARM_THUMB=0;;
1107 --sdl-threads)ARG_THREAD_SUPPORT=1;;
1108 --no-sdl-threads)
1109 ARG_THREAD_SUPPORT=0;;
1038 --prefix=*) ARG_PREFIX=`echo "$arg" | cut -d = -f 2`;; 1110 --prefix=*) ARG_PREFIX=`echo "$arg" | cut -d = -f 2`;;
1039 --help) help;; 1111 --help) help;;
1040 *) err=1; echo "[ERROR] Option '$arg' unsupported";; 1112 *) err=1; echo "[ERROR] Option '$arg' unsupported";;
@@ -3326,6 +3398,7 @@ sed > autoconf.h \
3326 -e "s<^#undef DO_BOOTCHART<$use_bootchart<g" \ 3398 -e "s<^#undef DO_BOOTCHART<$use_bootchart<g" \
3327 -e "s<@config_rtc@<$config_rtc<g" \ 3399 -e "s<@config_rtc@<$config_rtc<g" \
3328 -e "s<@have_rtc_alarm@<$have_rtc_alarm<g" \ 3400 -e "s<@have_rtc_alarm@<$have_rtc_alarm<g" \
3401 -e "s<@thread_support@<$thread_support<g" \
3329 -e "s<@RBDIR@<${rbdir}<g" \ 3402 -e "s<@RBDIR@<${rbdir}<g" \
3330 -e "s<@sharepath@<${sharedir}<g" \ 3403 -e "s<@sharepath@<${sharedir}<g" \
3331 -e "s<@binpath@<${bindir}<g" \ 3404 -e "s<@binpath@<${bindir}<g" \
@@ -3362,6 +3435,9 @@ sed > autoconf.h \
3362@config_rtc@ 3435@config_rtc@
3363@have_rtc_alarm@ 3436@have_rtc_alarm@
3364 3437
3438/* the threading backend we use */
3439#define @thread_support@
3440
3365/* lcd dimensions for application builds from configure */ 3441/* lcd dimensions for application builds from configure */
3366@lcd_width@ 3442@lcd_width@
3367@lcd_height@ 3443@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 @@
46#endif 46#endif
47 47
48#include <fcntl.h> 48#include <fcntl.h>
49#include <SDL.h> 49#ifdef HAVE_SDL_THREADS
50#include <SDL_thread.h>
51#include "thread-sdl.h" 50#include "thread-sdl.h"
51#else
52#define sim_thread_unlock() NULL
53#define sim_thread_lock(a)
54#endif
52#include "thread.h" 55#include "thread.h"
53#include "kernel.h" 56#include "kernel.h"
54#include "debug.h" 57#include "debug.h"