implement robust mutexes
authorRich Felker <dalias@aerifal.cx>
Fri, 18 Mar 2011 00:41:37 +0000 (20:41 -0400)
committerRich Felker <dalias@aerifal.cx>
Fri, 18 Mar 2011 00:41:37 +0000 (20:41 -0400)
some of this code should be cleaned up, e.g. using macros for some of
the bit flags, masks, etc. nonetheless, the code is believed to be
working and correct at this point.

include/pthread.h
src/internal/pthread_impl.h
src/thread/pthread_mutex_consistent.c [new file with mode: 0644]
src/thread/pthread_mutex_init.c
src/thread/pthread_mutex_lock.c
src/thread/pthread_mutex_timedlock.c
src/thread/pthread_mutex_trylock.c
src/thread/pthread_mutex_unlock.c
src/thread/pthread_mutexattr_getrobust.c [new file with mode: 0644]
src/thread/pthread_mutexattr_setrobust.c [new file with mode: 0644]

index d40002e6ad430cdec9b7efd3200db35b4e7008d1..e15f25bb9f7235accfef66bab05e617905780b3f 100644 (file)
@@ -92,6 +92,7 @@ int pthread_mutex_unlock(pthread_mutex_t *);
 int pthread_mutex_trylock(pthread_mutex_t *);
 int pthread_mutex_timedlock(pthread_mutex_t *, const struct timespec *);
 int pthread_mutex_destroy(pthread_mutex_t *);
+int pthread_mutex_consistent(pthread_mutex_t *);
 
 int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
 int pthread_cond_destroy(pthread_cond_t *);
index 9d56e8fad4029398266ee80cd4ecdc4afa600022..e3a9a0e0a2b703af60b9db228dab679aa90a0448 100644 (file)
@@ -37,6 +37,11 @@ struct pthread {
        void **tsd;
        pthread_attr_t attr;
        volatile int dead;
+       struct {
+               void **head;
+               long off;
+               void *pending;
+       } robust_list;
 };
 
 #define __SU (sizeof(size_t)/sizeof(int))
diff --git a/src/thread/pthread_mutex_consistent.c b/src/thread/pthread_mutex_consistent.c
new file mode 100644 (file)
index 0000000..7dfb904
--- /dev/null
@@ -0,0 +1,10 @@
+#include "pthread_impl.h"
+
+int pthread_mutex_consistent(pthread_mutex_t *m)
+{
+       if (m->_m_type < 8) return EINVAL;
+       if ((m->_m_lock & 0x3fffffff) != pthread_self()->tid)
+               return EPERM;
+       m->_m_type -= 8;
+       return 0;
+}
index 6e23df139a213f64b036b901fa9f933c7cf59419..75ddf02bc129e4398d7ccf1b4bbfc0d0234d2437 100644 (file)
@@ -3,6 +3,6 @@
 int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a)
 {
        memset(m, 0, sizeof *m);
-       if (a) m->_m_type = *a & 3;
+       if (a) m->_m_type = *a & 7;
        return 0;
 }
index 56111ec84f8951c09bd06c54731a3a3085c1bf07..477b3d808175164ed1c1c2b3460b7673d19dce71 100644 (file)
@@ -4,9 +4,9 @@ int pthread_mutex_lock(pthread_mutex_t *m)
 {
        int r;
        while ((r=pthread_mutex_trylock(m)) == EBUSY) {
-               if (!(r=m->_m_lock)) continue;
-               if (m->_m_type == PTHREAD_MUTEX_ERRORCHECK
-                && r == pthread_self()->tid)
+               if (!(r=m->_m_lock) || (r&0x40000000)) continue;
+               if ((m->_m_type&3) == PTHREAD_MUTEX_ERRORCHECK
+                && (r&0x1fffffff) == pthread_self()->tid)
                        return EDEADLK;
                __wait(&m->_m_lock, &m->_m_waiters, r, 0);
        }
index 20ce01283816fcd108662c424ae3f88d91ea3e53..f1c3eed74f0868561a5c3bc288ce798922b18700 100644 (file)
@@ -4,8 +4,9 @@ int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *at)
 {
        int r, w=0;
        while ((r=pthread_mutex_trylock(m)) == EBUSY) {
+               if (!(r=m->_m_lock) || (r&0x40000000)) continue;
                if (!w) a_inc(&m->_m_waiters), w++;
-               if (__timedwait(&m->_m_lock, 1, CLOCK_REALTIME, at, 0) == ETIMEDOUT) {
+               if (__timedwait(&m->_m_lock, r, CLOCK_REALTIME, at, 0) == ETIMEDOUT) {
                        if (w) a_dec(&m->_m_waiters);
                        return ETIMEDOUT;
                }
index de57ff9ec3b5d4e994748646d9b9d7ab925ec2ae..f48aaadec3c761e83b42ad4052417972d8b5c30d 100644 (file)
@@ -3,19 +3,51 @@
 int pthread_mutex_trylock(pthread_mutex_t *m)
 {
        int tid;
+       int own;
+       pthread_t self;
 
        if (m->_m_type == PTHREAD_MUTEX_NORMAL)
                return (m->_m_lock || a_swap(&m->_m_lock, 1)) ? EBUSY : 0;
 
-       tid = pthread_self()->tid;
+       self = pthread_self();
+       tid = self->tid | 0x80000000;
 
-       if (m->_m_lock == tid && m->_m_type == PTHREAD_MUTEX_RECURSIVE) {
+       if (m->_m_type >= 4) {
+               if (!self->robust_list.off)
+                       syscall2(__NR_set_robust_list,
+                               (long)&self->robust_list, 3*sizeof(long));
+               self->robust_list.off = (char*)&m->_m_lock-(char *)&m->_m_next;
+               self->robust_list.pending = &m->_m_next;
+       }
+
+       if (m->_m_lock == tid && (m->_m_type&3) == PTHREAD_MUTEX_RECURSIVE) {
                if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
                m->_m_count++;
                return 0;
        }
 
-       if (m->_m_lock || a_cas(&m->_m_lock, 0, tid)) return EBUSY;
+       own = m->_m_lock;
+       if ((own && !(own & 0x40000000)) || a_cas(&m->_m_lock, own, tid)!=own)
+               return EBUSY;
+
        m->_m_count = 1;
+
+       if (m->_m_type < 4) return 0;
+
+       if (m->_m_type >= 8) {
+               m->_m_lock = 0;
+               return ENOTRECOVERABLE;
+       }
+       m->_m_next = self->robust_list.head;
+       m->_m_prev = &self->robust_list.head;
+       if (self->robust_list.head)
+               self->robust_list.head[-1] = &m->_m_next;
+       self->robust_list.head = &m->_m_next;
+       self->robust_list.pending = 0;
+       if (own) {
+               m->_m_type += 8;
+               return EOWNERDEAD;
+       }
+
        return 0;
 }
index 3733788d8ebee7a375d54db6959aef1f8702a623..67aa7ba5d14e1e983e25745f8a1104493b413ff8 100644 (file)
@@ -2,14 +2,23 @@
 
 int pthread_mutex_unlock(pthread_mutex_t *m)
 {
+       pthread_t self;
+
        if (m->_m_type != PTHREAD_MUTEX_NORMAL) {
-               if (!m->_m_lock || m->_m_lock != __pthread_self()->tid)
+               self = __pthread_self();
+               if ((m->_m_lock&0x1fffffff) != self->tid)
                        return EPERM;
-               if (m->_m_type == PTHREAD_MUTEX_RECURSIVE && --m->_m_count)
+               if ((m->_m_type&3) == PTHREAD_MUTEX_RECURSIVE && --m->_m_count)
                        return 0;
+               if (m->_m_type >= 4) {
+                       self->robust_list.pending = &m->_m_next;
+                       *(void **)m->_m_prev = m->_m_next;
+                       if (m->_m_next) ((void **)m->_m_next)[-1] = m->_m_prev;
+               }
        }
 
        m->_m_lock = 0;
        if (m->_m_waiters) __wake(&m->_m_lock, 1, 0);
+       if (m->_m_type >= 4) self->robust_list.pending = 0;
        return 0;
 }
diff --git a/src/thread/pthread_mutexattr_getrobust.c b/src/thread/pthread_mutexattr_getrobust.c
new file mode 100644 (file)
index 0000000..b83cb7c
--- /dev/null
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_mutexattr_getrobust(const pthread_mutexattr_t *a, int *robust)
+{
+       *robust = *a / 4U % 2;
+       return 0;
+}
diff --git a/src/thread/pthread_mutexattr_setrobust.c b/src/thread/pthread_mutexattr_setrobust.c
new file mode 100644 (file)
index 0000000..4a296ba
--- /dev/null
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+
+int pthread_mutexattr_setrobust(pthread_mutexattr_t *a, int robust)
+{
+       if (robust > 1U) return EINVAL;
+       *a &= ~4;
+       *a |= robust*4;
+       return 0;
+}