1.0.0.31: pthread-futex: another pthread back-end of mutex
authorsa2c <sa2c>
Fri, 15 Dec 2006 01:48:59 +0000 (01:48 +0000)
committersa2c <sa2c>
Fri, 15 Dec 2006 01:48:59 +0000 (01:48 +0000)
An implementation of futex by pthread.  This is default back-end on
FreeBSD since pthread-lutex has some problems on these systems.

NEWS
make-config.sh
src/runtime/GNUmakefile
src/runtime/linux-os.c
src/runtime/pthread-futex.c [new file with mode: 0644]
version.lisp-expr

diff --git a/NEWS b/NEWS
index 395535b..f5a1e3e 100644 (file)
--- 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
index 911fa24..1e82a92 100644 (file)
@@ -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
                 ;;
index 44a2c3a..af75160 100644 (file)
@@ -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)
index d5ae16c..4c6ef83 100644 (file)
@@ -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 <sys/syscall.h>
 #include <unistd.h>
 #include <errno.h>
@@ -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 (file)
index 0000000..902b4d8
--- /dev/null
@@ -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 <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#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
index 37de098..d0a3b0a 100644 (file)
@@ -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"