From: NIIMI S. <sa...@us...> - 2006-12-15 01:49:04
|
Update of /cvsroot/sbcl/sbcl/src/runtime In directory sc8-pr-cvs8.sourceforge.net:/tmp/cvs-serv23420/src/runtime Modified Files: GNUmakefile linux-os.c Added Files: pthread-futex.c Log Message: 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. --- NEW FILE: pthread-futex.c --- /* 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: GNUmakefile =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/GNUmakefile,v retrieving revision 1.28 retrieving revision 1.29 diff -u -d -r1.28 -r1.29 --- GNUmakefile 13 Nov 2006 16:03:31 -0000 1.28 +++ GNUmakefile 15 Dec 2006 01:48:59 -0000 1.29 @@ -40,7 +40,8 @@ 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 @@ $(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: linux-os.c =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/linux-os.c,v retrieving revision 1.66 retrieving revision 1.67 diff -u -d -r1.66 -r1.67 --- linux-os.c 5 Dec 2006 15:27:24 -0000 1.66 +++ linux-os.c 15 Dec 2006 01:48:59 -0000 1.67 @@ -62,7 +62,7 @@ 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 @@ { /* 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 @@ #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" |