From b8c1d655b2d830e4f41441f5ab8335b4d48582ad Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Sun, 16 May 2010 12:56:15 +0200 Subject: [PATCH] Add target/hosted/unix/thread-pth.c, which is a working thread implementation using the Gnu Pth thread library. It has some advantages over SDL threads because it's cooperative by design and supports priorities. It's not the default yet, enable via tools/configure --with-pth --- firmware/SOURCES | 6 + firmware/export/config.h | 11 +- firmware/target/hosted/thread-pth.h | 34 ++ firmware/target/hosted/unix/thread-pth.c | 747 +++++++++++++++++++++++++++++++ tools/configure | 49 +- uisimulator/common/io.c | 11 +- uisimulator/common/stubs.c | 1 - 7 files changed, 838 insertions(+), 21 deletions(-) create mode 100644 firmware/target/hosted/thread-pth.h create mode 100644 firmware/target/hosted/unix/thread-pth.c diff --git a/firmware/SOURCES b/firmware/SOURCES index 47249a2d6..14cda8aaf 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -31,9 +31,15 @@ target/hosted/sdl/lcd-remote-bitmap.c #endif target/hosted/sdl/lcd-sdl.c target/hosted/sdl/system-sdl.c +#ifndef HAVE_GNU_PTH_THREADS target/hosted/sdl/thread-sdl.c +#endif target/hosted/sdl/timer-sdl.c #endif +#ifdef HAVE_GNU_PTH_THREADS +#undef unix +target/hosted/unix/thread-pth.c +#endif panic.c debug.c diff --git a/firmware/export/config.h b/firmware/export/config.h index 070bd959b..6b06e536d 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -669,10 +669,14 @@ Lyre prototype 1 */ #define HAVE_EXTENDED_MESSAGING_AND_NAME #define HAVE_WAKEUP_EXT_CB -#ifndef SIMULATOR +#if (!defined(SIMULATOR) || defined(HAVE_GNU_PTH_THREADS)) #define HAVE_PRIORITY_SCHEDULING + +#ifndef HAVE_GNU_PTH_THREADS #define HAVE_SCHEDULER_BOOSTCTRL -#endif /* SIMULATOR */ +#endif + +#endif /* SIMULATOR || HAVE_GNU_PTH_THREADS */ #define HAVE_SEMAPHORE_OBJECTS @@ -932,7 +936,8 @@ Lyre prototype 1 */ #define HAVE_PLUGIN_CHECK_OPEN_CLOSE #endif -#if defined(HAVE_DIRCACHE) && !defined(SIMULATOR) +#if defined(HAVE_DIRCACHE) && \ + (!defined(SIMULATOR) || defined(HAVE_GNU_PTH_THREADS)) #define HAVE_IO_PRIORITY #endif diff --git a/firmware/target/hosted/thread-pth.h b/firmware/target/hosted/thread-pth.h new file mode 100644 index 000000000..65ff27e3e --- /dev/null +++ b/firmware/target/hosted/thread-pth.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Thomas Martitz + * + * 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. + * + ****************************************************************************/ + +#ifndef __THREAD_PTH_H__ +#define __THREAD_PTH_H__ +/* + * export sim specific functions for the sim thread interface + * + **/ +void * sim_thread_unlock(void); +void sim_thread_lock(void *me); +void sim_thread_exception_wait(void); +void sim_thread_shutdown(void); /* Shut down all kernel threads gracefully */ + +#endif /* #ifndef __THREAD_PTH_H__ */ + diff --git a/firmware/target/hosted/unix/thread-pth.c b/firmware/target/hosted/unix/thread-pth.c new file mode 100644 index 000000000..58601a1e0 --- /dev/null +++ b/firmware/target/hosted/unix/thread-pth.c @@ -0,0 +1,747 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Thomas Martitz + * + * 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 +#include +#include +#include +#include +#include +#include "system.h" +#include "kernel.h" +#include "thread.h" +#include "thread-pth.h" +#include "debug.h" +#include /* SDL_GetTicks() */ + +/* Define this as 1 to show informational messages that are not errors. */ +#define HAVE_SIM_THREAD_DEBUG 0 + +#if HAVE_SIM_THREAD_DEBUG + +#undef THREAD_EXTRA_CHECKS +#define THREAD_EXTRA_CHECKS /* always when debugging */ + +#define SIM_THREAD_DEBUGF(...) DEBUGF(__VA_ARGS__) +static char __name[32]; +#define SIM_THREAD_GET_NAME(thread) \ + ({ thread_get_name(__name, ARRAYLEN(__name), thread); __name; }) +#else +#define SIM_THREAD_DEBUGF(...) +#define SIM_THREAD_GET_NAME(thread) +#endif + +#ifdef WIN32 +#define NEEDLE '\\' +#else +#define NEEDLE '/' +#endif + +#define THREAD_PANICF(str...) \ + do { \ + fprintf(stderr, "%s:%d:%s(): ", strrchr(__FILE__,NEEDLE)+1, __LINE__, __func__); \ + fprintf(stderr, str); \ + exit(-1); \ + } while (0) + + +/* Thread entries as in rockbox core */ +struct thread_entry *current_thread; /* cache current cache */ +struct thread_entry threads[MAXTHREADS]; + +extern long start_tick; + + +#ifdef HAVE_PRIORITY_SCHEDULING +/* map Rockbox prio to pth prios, LUT gives more freedom than + * calculative conversion; index is the corresponding rockbox thread level */ +#if (PTH_PRIO_MAX != 5 || PTH_PRIO_MIN != -5 || PTH_PRIO_STD != 0) +#error Priority map out of sync +#endif +static int prio_map[] = +{ + 5, 5, 5, 5, 5, /* HIGHEST_PRIORITY, PRIORITY_REALTIME_* */ + 4, 4, 4, /* PRIORITY_PLAYBACK_MAX */ + 3, 3, 3, + 2, 2, 2, + 1, 1, /* PRIORITY_BUFFERING */ + 0, 0, /* PRIORITY_USER_INTERFACE, _RECORDING, _PLAYBACK */ + -1, -1, /* PRIORITY_SYSTEM */ + -2, -2, -2, /* PRIORITY_BACKGROUND */ + -3, -3, -3, + -4, -4, -4, + -5, -5, -5, /* LOWEST_PRIORITY */ +}; +#endif + + +static int check_wakeup_cb(void* data) +{ + struct thread_entry *t = (struct thread_entry*)data; + return (t->state == STATE_RUNNING); +} + +void sim_thread_shutdown(void) +{ + int i; + /* wait for all threads to exit gracefully - pth_exit() is called + * during pth_cancel() + * pth_exit() goes back in the call stack to return in thread's entry + * routine and then runthread() */ + + /* main thread is removed last */ + for (i = 1; i < MAXTHREADS; i++) + { + struct thread_entry *thread = &threads[i]; + if (thread != NULL) + { + pth_t t = thread->context.t; + if (t) + { + /* force thread runnable */ + thread->state = STATE_RUNNING; + /* Wait for it to finish */ + SIM_THREAD_DEBUGF("Waiting for (%s) to finish\n", + SIM_THREAD_GET_NAME(thread)); + pth_cancel(t); + } + } + } + pth_cancel(threads[0].context.t); +} + +static void new_thread_id(unsigned int slot_num, + struct thread_entry *thread) +{ + unsigned int version = + (thread->id + (1u << THREAD_ID_VERSION_SHIFT)) + & THREAD_ID_VERSION_MASK; + + if (version == 0) + version = 1u << THREAD_ID_VERSION_SHIFT; + + thread->id = version | (slot_num & THREAD_ID_SLOT_MASK); +} + +static struct thread_entry * find_empty_thread_slot(void) +{ + struct thread_entry *thread = NULL; + int n; + + for (n = 0; n < MAXTHREADS; n++) + { + int state = threads[n].state; + + if (state == STATE_KILLED) + { + thread = &threads[n]; + break; + } + } + + return thread; +} + + +#if HAVE_SIM_THREAD_DEBUG +static void dump_threads(int sig) +{ + if (sig != SIGSEGV) + pth_ctrl(PTH_CTRL_DUMPSTATE, stderr); + switch (sig) + { + case SIGABRT: + case SIGSEGV: + pth_kill(); + default: + break; + } +} +#endif + + +void thread_wait(unsigned int id) +{ + pth_event_t ev; + struct thread_entry *thread = thread_id_entry(id); + struct thread_entry *current = current_thread; + current->bqp = &thread->queue; + ev = pth_event(PTH_EVENT_TID | PTH_UNTIL_TID_DEAD, thread->context.t); + pth_wait(ev); +} + +/* Initialize threading */ +void init_threads(void) +{ + int i; + struct thread_entry* thread; + + memset(threads, 0, sizeof(threads)); + /* Initialize all IDs */ + for (i = 0; i < MAXTHREADS; i++) + threads[i].id = THREAD_ID_INIT(i); + + if (pth_init() != true) + { + THREAD_PANICF("Failed to initialize pth threads\n"); + return; + } +#if defined(HAVE_PRIORITY_SCHEDULING) && defined(THREAD_EXTRA_CHECKS) + /* sanity check */ + if (ARRAYLEN(prio_map) != NUM_PRIORITIES) + THREAD_PANICF("Priority map out of sync\n"); +#endif + +#if HAVE_SIM_THREAD_DEBUG + signal(SIGABRT, dump_threads); + signal(SIGINT, dump_threads); +#endif + + thread = &threads[0]; + thread->stack = (uintptr_t *)" "; + thread->stack_size = 8; + thread->name = "main"; + thread->state = STATE_RUNNING; + thread->context.t = NULL; /* NULL for the implicit main thread */ + current_thread = thread; + + SIM_THREAD_DEBUGF("Main thread: %p\n", thread_id_entry(i)); + return; +} + +void sim_thread_exception_wait(void) +{ + while (1) + sleep(HZ/10); +} + +/* not applicable under cooperative threading */ +void sim_thread_lock(void *me) +{ + (void)me; +} + +void * sim_thread_unlock(void) +{ + return current_thread; +} + +struct thread_entry * thread_id_entry(unsigned int thread_id) +{ + struct thread_entry *thread = (thread_id == THREAD_ID_CURRENT) ? + current_thread : &threads[thread_id & THREAD_ID_SLOT_MASK]; +#ifdef THREAD_EXTRA_CHECKS + if (thread_id == THREAD_ID_CURRENT && thread->context.t != pth_self()) + THREAD_PANICF("Current thread corrupted\n"); +#endif + return thread; +} + +static void add_to_list_l(struct thread_entry **list, + struct thread_entry *thread) +{ + if (*list == NULL) + { + /* Insert into unoccupied list */ + thread->l.next = thread; + thread->l.prev = thread; + *list = thread; + } + else + { + /* Insert last */ + thread->l.next = *list; + thread->l.prev = (*list)->l.prev; + thread->l.prev->l.next = thread; + (*list)->l.prev = thread; + } +} + +static void remove_from_list_l(struct thread_entry **list, + struct thread_entry *thread) +{ + if (thread == thread->l.next) + { + /* The only item */ + *list = NULL; + return; + } + + if (thread == *list) + { + /* List becomes next item */ + *list = thread->l.next; + } + + /* Fix links to jump over the removed entry. */ + thread->l.prev->l.next = thread->l.next; + thread->l.next->l.prev = thread->l.prev; +} + +unsigned int thread_get_current(void) +{ + return current_thread->id; +} + +void switch_thread(void) +{ + struct thread_entry *current = current_thread; + + enable_irq(); + + switch (current->state) + { + case STATE_RUNNING: + { + /* let other threads run, + * pass NULL to let pth decide which thread to run next based */ + pth_yield(NULL); + break; + } /* STATE_RUNNING: */ + case STATE_BLOCKED: + { + int oldlevel; + pth_event_t ev = pth_event(PTH_EVENT_FUNC, check_wakeup_cb, + current, pth_time(0, 1000)); + /* wait for something being posted on the event */ + pth_wait(ev); + pth_event_free(ev, PTH_FREE_THIS); + oldlevel = disable_irq_save(); + current->state = STATE_RUNNING; + restore_irq(oldlevel); + break; + } /* STATE_BLOCKED: */ + case STATE_BLOCKED_W_TMO: + { + int oldlevel; + long usecs = current->tmo_tick; + long secs = usecs / 1000000; + /* create the message event as for BLOCKED */ + pth_event_t msg_ev = pth_event(PTH_EVENT_FUNC, check_wakeup_cb, + current, pth_time(0, 1000)); + /* add an additional timeout event */ + pth_event_t time_ev = pth_event(PTH_EVENT_TIME, pth_timeout(secs, + usecs % 1000000)); + + /* wait for either event to fire first */ + pth_event_t ring = pth_event_concat(msg_ev, time_ev, NULL); + + pth_wait(ring); + pth_event_free(ring, PTH_FREE_ALL); + + oldlevel = disable_irq_save(); + + if (current->state == STATE_BLOCKED_W_TMO) + { + /* Timed out */ + remove_from_list_l(current->bqp, current); + +#ifdef HAVE_WAKEUP_EXT_CB + if (current->wakeup_ext_cb != NULL) + current->wakeup_ext_cb(current); +#endif + current->state = STATE_RUNNING; + } + + restore_irq(oldlevel); + break; + } /* STATE_BLOCKED_W_TMO: */ + + case STATE_SLEEPING: + { + pth_nap(pth_time(0, (unsigned)current->tmo_tick)); + current->state = STATE_RUNNING; + break; + } /* STATE_SLEEPING: */ + } + + current_thread = current; +} + +void sleep_thread(int ticks) +{ + struct thread_entry *current = current_thread; + int rem; + + current->state = STATE_SLEEPING; + + /* add a reminder for more accurate sleeps */ + rem = (SDL_GetTicks() - start_tick) % (1000/HZ); + if (rem < 0) + rem = 0; + /* tick to ms to us, then add remainder ms */ + current->tmo_tick = (ticks*(1000/HZ) + ((1000/HZ)-1) - rem)*1000; +} + +void block_thread(struct thread_entry *current) +{ + current->state = STATE_BLOCKED; + add_to_list_l(current->bqp, current); +} + +void block_thread_w_tmo(struct thread_entry *current, int ticks) +{ + current->state = STATE_BLOCKED_W_TMO; + current->tmo_tick = ticks*(1000/HZ)*1000; + add_to_list_l(current->bqp, current); +} + +unsigned int wakeup_thread(struct thread_entry **list) +{ + struct thread_entry *thread = *list; + + if (thread != NULL) + { + switch (thread->state) + { + case STATE_BLOCKED: + case STATE_BLOCKED_W_TMO: + { + unsigned retval = THREAD_OK; +#ifdef HAVE_PRIORITY_SCHEDULING + struct thread_entry *bl = thread->blocker ? + thread->blocker->thread:NULL; + if (thread->priority <= prio_map[PRIORITY_REALTIME] || + (bl != NULL && bl->priority > thread->priority)) + retval |= THREAD_SWITCH; +#endif + + remove_from_list_l(list, thread); + thread->state = STATE_RUNNING; + return retval; + } + } + } + + return THREAD_NONE; +} + +unsigned int thread_queue_wake(struct thread_entry **list) +{ + unsigned int result = THREAD_NONE; + + for (;;) + { + unsigned int rc = wakeup_thread(list); + + if (rc == THREAD_NONE) + break; + + result |= rc; + } + + return result; +} + +void thread_thaw(unsigned int thread_id) +{ + struct thread_entry *thread = thread_id_entry(thread_id); + + if (thread->state == STATE_FROZEN) + { + thread->state = STATE_RUNNING; + /* wakeup */ + pth_resume(thread->context.t); + } +} + +/* + * removes the thread entry struct for the scheduler */ +static void thread_cleanup(void *data) +{ + struct thread_entry *thread = (struct thread_entry*)data; + + thread->context.t = NULL; + /* the slot can now be reassigned */ + thread->state = STATE_KILLED; +} + +#ifdef HAVE_PRIORITY_SCHEDULING +/* + * stubs are sufficient for the next two, as pth handles priorities itself + * + * kernel.c pulls these in. fortunately only thread.c (which this file replaces) + * actually calls them so returning NULL should be ok */ +struct thread_entry *wakeup_priority_protocol_transfer(struct thread_entry*t) +{ + (void)t; + return NULL; +} + +struct thread_entry *wakeup_priority_protocol_release(struct thread_entry*t) +{ + (void)t; + return NULL; +} + +int thread_set_priority(unsigned id, int prio) +{ + struct thread_entry *thread; + pth_attr_t attr; + int old_level; + int old_base_prio = -1; + if (prio < HIGHEST_PRIORITY || prio > LOWEST_PRIORITY) + return -1; + + + thread = thread_id_entry(id); + if (thread == NULL) + return -2; + old_level = disable_irq_save(); + + attr = pth_attr_of(thread->context.t); + pth_attr_set(attr, PTH_ATTR_PRIO, prio_map[prio]); + old_base_prio = thread->base_priority; + /* keep the rockbox prio scheme for the outside */ + thread->priority = thread->base_priority = prio; + + restore_irq(old_level); + return old_base_prio; +} + + +/* + * copied as is from firmware/thread.c */ +int thread_get_priority(unsigned thread_id) +{ + struct thread_entry *thread = thread_id_entry(thread_id); + int base_priority = thread->base_priority; + + /* Simply check without locking slot. It may or may not be valid by the + * time the function returns anyway. If all tests pass, it is the + * correct value for when it was valid. */ + if (thread_id != THREAD_ID_CURRENT && + (thread->id != thread_id || thread->state == STATE_KILLED)) + base_priority = -1; + + return base_priority; +} + +#endif + + +#ifdef HAVE_IO_PRIORITY +int thread_get_io_priority(unsigned int thread_id) +{ + struct thread_entry *thread = thread_id_entry(thread_id); + return thread->io_priority; +} + +void thread_set_io_priority(unsigned int thread_id,int io_priority) +{ + struct thread_entry *thread = thread_id_entry(thread_id); + thread->io_priority = io_priority; +} +#endif +/* + * Wrap entering the thread's entry point for easier exiting */ +static void* runthread(void *thread) +{ + struct thread_entry *current; + + current = current_thread = thread; + current->context.start(); + SIM_THREAD_DEBUGF("Thread Done: %ld (%s)\n", + current - threads, SIM_THREAD_GET_NAME(current)); + /* Thread routine returned - terminate */ + return NULL; +} + + +unsigned int create_thread(void (*function)(void), + void* stack, size_t stack_size, + unsigned flags, const char *name IF_PRIO(,int prio)) +{ + /* TODO: implement priority */ + struct thread_entry *thread; + pth_t t; + pth_attr_t attr; + IF_PRIO( + int pth_prio = prio_map[prio]; + ) + /* + * pth wants the stack on the heap, so ignore the most probably static stack + * buffer passed to us + * also, we seem to need much more stack on a pc */ + stack_size = MAX(stack_size, 16u<<10); + stack = malloc(stack_size); + + if (stack == NULL) + { + THREAD_PANICF("Out of memory\n", __func__); + return 0; + } + + SIM_THREAD_DEBUGF("Creating thread: (%s)\n", name ? name : ""); + + thread = find_empty_thread_slot(); + if (thread == NULL) + { + DEBUGF("Failed to find thread slot\n"); + free(stack); + return 0; + } + + attr = pth_attr_new(); + pth_attr_set(attr, PTH_ATTR_NAME, name); +#ifdef HAVE_PRIORITY_SCHEDULING + pth_attr_set(attr, PTH_ATTR_PRIO, pth_prio); +#endif + /* explicitely set joinable we do need it for sane exiting */ + pth_attr_set(attr, PTH_ATTR_JOINABLE, true); + pth_attr_set(attr, PTH_ATTR_STACK_ADDR, stack); + pth_attr_set(attr, PTH_ATTR_STACK_SIZE, stack_size); + /* we could make cancellation asynchronous so it happens immediately, + * but making the cancel request pending so that the thread is cancelled + * when the thread runs next is considered cleaner */ + pth_attr_set(attr, PTH_ATTR_CANCEL_STATE, + PTH_CANCEL_ENABLE|PTH_CANCEL_ASYNCHRONOUS); + + /* remember the thread for killing */ + t = pth_spawn(attr, runthread, thread); + if (t == NULL) + { + DEBUGF("Failed to create thread\n"); + free(stack); + return 0; + } + + /* enable sane exiting */ + pth_cleanup_push(thread_cleanup, thread); + + thread->context.t = t; + thread->stack = stack; // pth wants stack on heap + thread->stack_size = stack_size; + thread->name = name; + thread->state = (flags & CREATE_THREAD_FROZEN) ? + STATE_FROZEN : STATE_RUNNING; +#ifdef HAVE_PRIORITY_SCHEDULING + thread->priority = thread->skip_count = thread->base_priority = prio; + thread->blocker = NULL; +#endif + + /* sleep until thread_thaw() is called for it */ + if (thread->state == STATE_FROZEN) + pth_suspend(thread->context.t); + + thread->context.start = function; + + SIM_THREAD_DEBUGF("New Thread: %ld (%s)\n", + thread->id, SIM_THREAD_GET_NAME(thread)); + + return thread->id; +} + + +#ifndef ALLOW_REMOVE_THREAD +static +#endif +void remove_thread(unsigned int thread_id) +{ + struct thread_entry *current = current_thread; + struct thread_entry *thread = thread_id_entry(thread_id); + pth_t t; + + if (thread_id != THREAD_ID_CURRENT && thread->id != thread_id) + return; + + int oldlevel = disable_irq_save(); + + + t = thread->context.t; + + if (thread != current) + { + switch (thread->state) + { + case STATE_BLOCKED: + case STATE_BLOCKED_W_TMO: + /* Remove thread from object it's waiting on */ + remove_from_list_l(thread->bqp, thread); + + /* wake the thread up */ + thread->state = STATE_RUNNING; +#ifdef HAVE_WAKEUP_EXT_CB + if (thread->wakeup_ext_cb != NULL) + thread->wakeup_ext_cb(thread); +#endif + break; + case STATE_SLEEPING: + /* let the thread sleep until it wakes up, for now */ + switch_thread(); + break; + } + } + + SIM_THREAD_DEBUGF("Removing thread: %ld (%s)\n", + thread->id, SIM_THREAD_GET_NAME(thread)); + + new_thread_id(thread->id, thread); + thread->state = STATE_KILLED; + thread_queue_wake(&thread->queue); + + if (thread == current) + { + /* Do a graceful exit */ + restore_irq(oldlevel); + /* terminate the current thread - it works by returning in the + * threads entry function */ + pth_exit(NULL); + } + else + { + pth_cancel(t); + restore_irq(oldlevel); + } +} + + +void thread_exit(void) +{ + remove_thread(THREAD_ID_CURRENT); +} + + +int thread_stack_usage(const struct thread_entry *thread) +{ + return 50; + (void)thread; +} + + +/* Return name if one or its address if none */ +void thread_get_name(char *buffer, int size, + struct thread_entry *thread) +{ + if (size <= 0) + return; + + *buffer = '\0'; + + if (thread) + { + bool named = thread->name && *thread->name; + const char *fmt = named ? "%s" : "%08lX"; + intptr_t name = named ? + (intptr_t)thread->name : (intptr_t)thread; + snprintf(buffer, size, fmt, name); + } +} diff --git a/tools/configure b/tools/configure index bf49be8a9..4d14a019d 100755 --- a/tools/configure +++ b/tools/configure @@ -170,6 +170,20 @@ simcc () { fi fi + if [ "$ARG_GNU_PTH" = "1" ]; then + if [ "$crosscompile" = "yes" ]; then + echo "[ERROR] GNU Pth cannot be used on windows" + fi + if [ -n "`findtool pth-config`" ]; then + GCCOPTS="$GCCOPTS `pth-config --cflags`" + extradefines="$extradefines -DHAVE_GNU_PTH_THREADS" + LDOPTS="$LDOPTS `pth-config --libs`" + echo "Found and use GNU Pth Portable Threads library" + else + echo "Configure did not find pth-config.\nFalling back to SDL library for threads" + fi + fi + GCCOPTS="$GCCOPTS -I\$(SIMDIR)" if test "X$crosscompile" != "Xyes"; then @@ -790,6 +804,8 @@ help() { --eabi Make configure prefer toolchains that are able to compile for the new ARM standard abi EABI --no-eabi The opposite of --eabi (prefer old non-eabi toolchains) + --with-pth Enable/Disable use of GNU Pth threads for the simulator + --without-pth --help Shows this message (must not be used with other options) EOF @@ -808,24 +824,27 @@ ARG_TTSOPTS= ARG_TYPE= ARG_VOICE= ARG_ARM_EABI= +ARG_GNU_PTH= err= for arg in "$@"; do case "$arg" in - --ccache) ARG_CCACHE=1;; - --no-ccache) ARG_CCACHE=0;; - --encopts=*) ARG_ENCOPTS=`echo "$arg" | cut -d = -f 2`;; - --language=*) ARG_LANG=`echo "$arg" | cut -d = -f 2`;; - --ram=*) ARG_RAM=`echo "$arg" | cut -d = -f 2`;; - --rbdir=*) ARG_RBDIR=`echo "$arg" | cut -d = -f 2`;; - --target=*) ARG_TARGET=`echo "$arg" | cut -d = -f 2`;; - --tts=*) ARG_TTS=`echo "$arg" | cut -d = -f 2`;; - --ttsopts=*) ARG_TTSOPTS=`echo "$arg" | cut -d = -f 2`;; - --type=*) ARG_TYPE=`echo "$arg" | cut -d = -f 2`;; - --voice=*) ARG_VOICE=`echo "$arg" | cut -d = -f 2`;; - --eabi) ARG_ARM_EABI=1;; - --no-eabi) ARG_ARM_EABI=0;; - --help) help;; - *) err=1; echo "[ERROR] Option '$arg' unsupported";; + --ccache) ARG_CCACHE=1;; + --no-ccache) ARG_CCACHE=0;; + --encopts=*) ARG_ENCOPTS=`echo "$arg" | cut -d = -f 2`;; + --language=*) ARG_LANG=`echo "$arg" | cut -d = -f 2`;; + --ram=*) ARG_RAM=`echo "$arg" | cut -d = -f 2`;; + --rbdir=*) ARG_RBDIR=`echo "$arg" | cut -d = -f 2`;; + --target=*) ARG_TARGET=`echo "$arg" | cut -d = -f 2`;; + --tts=*) ARG_TTS=`echo "$arg" | cut -d = -f 2`;; + --ttsopts=*) ARG_TTSOPTS=`echo "$arg" | cut -d = -f 2`;; + --type=*) ARG_TYPE=`echo "$arg" | cut -d = -f 2`;; + --voice=*) ARG_VOICE=`echo "$arg" | cut -d = -f 2`;; + --eabi) ARG_ARM_EABI=1;; + --no-eabi) ARG_ARM_EABI=0;; + --help) help;; + --with-pth) ARG_GNU_PTH=1;; + --without-pth) ARG_GNU_PTH=0;; + *) err=1; echo "[ERROR] Option '$arg' unsupported";; esac done [ "$err" ] && exit 1 diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c index bdcc7e6ca..97c64cf0a 100644 --- a/uisimulator/common/io.c +++ b/uisimulator/common/io.c @@ -45,13 +45,16 @@ #include #include -#include #include "thread.h" #include "kernel.h" #include "debug.h" #include "config.h" #include "ata.h" /* for IF_MV2 et al. */ +#ifdef HAVE_GNU_PTH_THREADS +#include "thread-pth.h" +#else #include "thread-sdl.h" +#endif /* Windows (and potentially other OSes) distinguish binary and text files. @@ -231,7 +234,9 @@ static ssize_t io_trigger_and_wait(int cmd) if (io.count > IO_YIELD_THRESHOLD || (io.accum += io.count) >= IO_YIELD_THRESHOLD) { - /* Allow other rockbox threads to run */ + /* + * leave the cooperative threading management and let other threads + * interrupt us (preemptively) during IO access */ io.accum = 0; mythread = sim_thread_unlock(); } @@ -249,6 +254,8 @@ static ssize_t io_trigger_and_wait(int cmd) /* Regain our status as current */ if (mythread != NULL) { + /* + * Re-enter the cooperative threading management again */ sim_thread_lock(mythread); } diff --git a/uisimulator/common/stubs.c b/uisimulator/common/stubs.c index d4a9af126..a274e5eda 100644 --- a/uisimulator/common/stubs.c +++ b/uisimulator/common/stubs.c @@ -21,7 +21,6 @@ #include #include #include -#include "thread-sdl.h" #include "debug.h" -- 2.11.4.GIT