From: sa2c Date: Fri, 15 Dec 2006 01:48:59 +0000 (+0000) Subject: 1.0.0.31: pthread-futex: another pthread back-end of mutex X-Git-Url: http://repo.macrolet.net/gitweb/?a=commitdiff_plain;h=4efb45cc0b0b9c3109d7cfd72f3f35de2ca4727e;p=sbcl.git 1.0.0.31: pthread-futex: another pthread back-end of mutex An implementation of futex by pthread. This is default back-end on FreeBSD since pthread-lutex has some problems on these systems. --- diff --git a/NEWS b/NEWS index 395535b..f5a1e3e 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ changes in sbcl-1.0.1 relative to sbcl-1.0: users and the general community) * improvement: sb-sprof traces call stacks to an arbitrary depth on x86/x86-64, rather than the previous fixed depth of 8 + * improvement: another pthread back-end of mutex "pthread-futex". * bug fix: non-ascii command-line arguments are processed correctly (thanks to Yaroslav Kavenchuk) * bug fix: non-required arguments were not passed correctly when a method diff --git a/make-config.sh b/make-config.sh index 911fa24..1e82a92 100644 --- a/make-config.sh +++ b/make-config.sh @@ -191,8 +191,9 @@ case "$sbcl_os" in freebsd) printf ' :elf' >> $ltf printf ' :freebsd' >> $ltf + printf ' :sb-pthread-futex' >> $ltf if [ $sbcl_arch = "x86" ]; then - printf ' :sb-lutex :restore-tls-segment-register-from-tls' >> $ltf + printf ' :restore-tls-segment-register-from-tls' >> $ltf fi link_or_copy Config.$sbcl_arch-freebsd Config ;; diff --git a/src/runtime/GNUmakefile b/src/runtime/GNUmakefile index 44a2c3a..af75160 100644 --- a/src/runtime/GNUmakefile +++ b/src/runtime/GNUmakefile @@ -40,7 +40,8 @@ include Config COMMON_SRC = alloc.c backtrace.c breakpoint.c coreparse.c \ dynbind.c gc-common.c globals.c interr.c interrupt.c largefile.c \ - monitor.c os-common.c parse.c print.c purify.c pthread-lutex.c \ + monitor.c os-common.c parse.c print.c purify.c \ + pthread-futex.c pthread-lutex.c \ regnames.c run-program.c runtime.c save.c search.c \ thread.c time.c util.c validate.c vars.c wrap.c @@ -58,7 +59,7 @@ $(TARGET): $(OBJS) $(CC) ${LINKFLAGS} -o $@ $^ $(LIBS) sbcl.nm: $(TARGET) - $(NM) $(TARGET) | $(GREP) -v " F \| U " > ,$@ + $(NM) $(TARGET) | $(GREP) -v " [FUw] " > ,$@ mv -f ,$@ $@ sbcl.h: $(wildcard genesis/*.h) diff --git a/src/runtime/linux-os.c b/src/runtime/linux-os.c index d5ae16c..4c6ef83 100644 --- a/src/runtime/linux-os.c +++ b/src/runtime/linux-os.c @@ -62,7 +62,7 @@ int personality (unsigned long); size_t os_vm_page_size; -#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_SB_LUTEX) +#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_SB_LUTEX) && !defined(LISP_FEATURE_SB_PTHREAD_FUTEX) #include #include #include @@ -145,7 +145,7 @@ os_init(char *argv[], char *envp[]) { /* Conduct various version checks: do we have enough mmap(), is * this a sparc running 2.2, can we do threads? */ -#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_SB_LUTEX) +#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_SB_LUTEX) && !defined(LISP_FEATURE_SB_PTHREAD_FUTEX) int *futex=0; #endif struct utsname name; @@ -171,7 +171,7 @@ os_init(char *argv[], char *envp[]) #endif } #ifdef LISP_FEATURE_SB_THREAD -#if !defined(LISP_FEATURE_SB_LUTEX) +#if !defined(LISP_FEATURE_SB_LUTEX) && !defined(LISP_FEATURE_SB_PTHREAD_FUTEX) futex_wait(futex,-1); if(errno==ENOSYS) { lose("This version of SBCL is compiled with threading support, but your kernel\n" diff --git a/src/runtime/pthread-futex.c b/src/runtime/pthread-futex.c new file mode 100644 index 0000000..902b4d8 --- /dev/null +++ b/src/runtime/pthread-futex.c @@ -0,0 +1,313 @@ +/* An approximation of Linux futexes implemented using pthread mutexes + * and pthread condition variables. + */ + +/* + * This software is part of the SBCL system. See the README file for + * more information. + * + * The software is in the public domain and is provided with + * absolutely no warranty. See the COPYING and CREDITS files for more + * information. + */ + +#include "sbcl.h" + +#if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_PTHREAD_FUTEX) + +#include +#include +#include + +#include "runtime.h" +#include "arch.h" +#include "target-arch-os.h" +#include "os.h" + +#define FUTEX_WAIT_NSEC (10000000) /* 10 msec */ + +#if 1 +# define futex_assert(ex) \ +do { \ + if (!(ex)) futex_abort(); \ +} while (0) +# define futex_assert_verbose(ex, fmt, ...) \ +do { \ + if (!(ex)) { \ + fprintf(stderr, fmt, ## __VA_ARGS__); \ + futex_abort(); \ + } \ +} while (0) +#else +# define futex_assert(ex) +# define futex_assert_verbose(ex, fmt, ...) +#endif + +#define futex_abort() \ + lose("Futex assertion failure, file \"%s\", line %d\n", __FILE__, __LINE__) + +struct futex { + struct futex *prev; + struct futex *next; + int *lock_word; + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; +}; + +static pthread_mutex_t futex_lock = PTHREAD_MUTEX_INITIALIZER; + +static struct futex *futex_head = NULL; +static struct futex *futex_free_head = NULL; + +static struct futex * +futex_add(struct futex *head, struct futex *futex) +{ + futex->prev = NULL; + futex->next = head; + if (head != NULL) + head->prev = futex; + head = futex; + + return head; +} + +static struct futex * +futex_delete(struct futex *head, struct futex *futex) +{ + if (head == futex) + head = futex->next; + if (futex->prev != NULL) + futex->prev->next = futex->next; + if (futex->next != NULL) + futex->next->prev = futex->prev; + + return head; +} + +static struct futex * +futex_find(struct futex *head, int *lock_word) +{ + struct futex *futex; + + for (futex = head; futex != NULL; futex = futex->next) { + if (futex->lock_word == lock_word) + break; + } + + return futex; +} + +static struct futex * +futex_get(int *lock_word) +{ + int ret; + struct futex *futex; + + ret = pthread_mutex_lock(&futex_lock); + futex_assert(ret == 0); + + futex = futex_find(futex_head, lock_word); + + if (futex != NULL) + futex->count++; + + ret = pthread_mutex_unlock(&futex_lock); + futex_assert(ret == 0); + + if (futex != NULL) { + ret = pthread_mutex_lock(&futex->mutex); + futex_assert(ret == 0); + } + + return futex; +} + +static struct futex * +futex_allocate(int *lock_word) +{ + int ret; + struct futex *futex; + + ret = pthread_mutex_lock(&futex_lock); + futex_assert(ret == 0); + + futex = futex_free_head; + + if (futex != NULL) + futex_free_head = futex_delete(futex_free_head, futex); + + ret = pthread_mutex_unlock(&futex_lock); + futex_assert(ret == 0); + + if (futex == NULL) { + futex = malloc(sizeof(struct futex)); + futex_assert(futex != NULL); + + ret = pthread_mutex_init(&futex->mutex, NULL); + futex_assert(ret == 0); + + ret = pthread_cond_init(&futex->cond, NULL); + futex_assert(ret == 0); + } + + futex->lock_word = lock_word; + futex->count = 1; + + /* Lock mutex before register to avoid race conditions. */ + ret = pthread_mutex_lock(&futex->mutex); + futex_assert(ret == 0); + + ret = pthread_mutex_lock(&futex_lock); + futex_assert(ret == 0); + + futex_head = futex_add(futex_head, futex); + + ret = pthread_mutex_unlock(&futex_lock); + futex_assert(ret == 0); + + return futex; +} + +static void +futex_cleanup(void *p) +{ + struct futex *futex = (struct futex *)p; + int ret, count; + + ret = pthread_mutex_lock(&futex_lock); + futex_assert(ret == 0); + + count = --futex->count; + if (count <= 0) { + futex_head = futex_delete(futex_head, futex); + futex_free_head = futex_add(futex_free_head, futex); + } + + ret = pthread_mutex_unlock(&futex_lock); + futex_assert(ret == 0); + + ret = pthread_mutex_unlock(&futex->mutex); + futex_assert(ret == 0); +} + +static int +futex_relative_to_abs(struct timespec *tp, int relative) +{ + int ret; + struct timeval tv; + + ret = gettimeofday(&tv, NULL); + if (ret != 0) + return ret; + tp->tv_sec = tv.tv_sec + (tv.tv_usec * 1000 + relative) / 1000000000; + tp->tv_nsec = (tv.tv_usec * 1000 + relative) % 1000000000; + return 0; +} + +int +futex_wait(int *lock_word, int oldval) +{ + int ret, result; + struct futex *futex; + sigset_t oldset, newset; + + sigemptyset(&newset); + sigaddset_deferrable(&newset); + +again: + pthread_sigmask(SIG_BLOCK, &newset, &oldset); + + futex = futex_get(lock_word); + + if (futex == NULL) + futex = futex_allocate(lock_word); + + pthread_cleanup_push(futex_cleanup, futex); + + /* Compare lock_word after the lock is aquired to avoid race + * conditions. */ + if (*(volatile int *)lock_word != oldval) { + result = EWOULDBLOCK; + goto done; + } + + /* It's not possible to unwind frames across pthread_cond_wait(3). */ + for (;;) { + int i; + sigset_t pendset; + struct timespec abstime; + + ret = futex_relative_to_abs(&abstime, FUTEX_WAIT_NSEC); + futex_assert(ret == 0); + + result = pthread_cond_timedwait(&futex->cond, &futex->mutex, + &abstime); + futex_assert(result == 0 || result == ETIMEDOUT); + + if (result != ETIMEDOUT) + break; + + /* futex system call of Linux returns with EINTR errno when + * it's interrupted by signals. Check pending signals here to + * emulate this behaviour. */ + sigpending(&pendset); + for (i = 1; i < NSIG; i++) { + if (sigismember(&pendset, i) && sigismember(&newset, i)) { + result = EINTR; + goto done; + } + } + } +done: + ; /* Null statement is required between label and pthread_cleanup_pop. */ + pthread_cleanup_pop(1); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + + /* futex_wake() in linux-os.c loops when futex system call returns + * EINTR. */ + if (result == EINTR) { + sched_yield(); + goto again; + } + + return result; +} + +int +futex_wake(int *lock_word, int n) +{ + int ret; + struct futex *futex; + sigset_t newset, oldset; + + sigemptyset(&newset); + sigaddset_deferrable(&newset); + + pthread_sigmask(SIG_BLOCK, &newset, &oldset); + + futex = futex_get(lock_word); + + if (futex != NULL) { + pthread_cleanup_push(futex_cleanup, futex); + + /* The lisp-side code passes N=2**29-1 for a broadcast. */ + if (n >= ((1 << 29) - 1)) { + /* CONDITION-BROADCAST */ + ret = pthread_cond_broadcast(&futex->cond); + futex_assert(ret == 0); + } else { + while (n-- > 0) { + ret = pthread_cond_signal(&futex->cond); + futex_assert(ret == 0); + } + } + + pthread_cleanup_pop(1); + } + + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + + return 0; +} +#endif diff --git a/version.lisp-expr b/version.lisp-expr index 37de098..d0a3b0a 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -17,4 +17,4 @@ ;;; checkins which aren't released. (And occasionally for internal ;;; versions, especially for internal versions off the main CVS ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".) -"1.0.0.30" +"1.0.0.31"