Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/nathanw_sa]: src/lib/libpthread Implement recursive and errorcheck mutexes.



details:   https://anonhg.NetBSD.org/src/rev/ab6e3c8874eb
branches:  nathanw_sa
changeset: 506735:ab6e3c8874eb
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Mon Jan 13 22:50:09 2003 +0000

description:
Implement recursive and errorcheck mutexes.

diffstat:

 lib/libpthread/TODO            |    2 -
 lib/libpthread/pthread.h       |   12 +-
 lib/libpthread/pthread_mutex.c |  228 +++++++++++++++++++++++++++++++++++++---
 3 files changed, 220 insertions(+), 22 deletions(-)

diffs (truncated from 417 to 300 lines):

diff -r f3824c64ff5f -r ab6e3c8874eb lib/libpthread/TODO
--- a/lib/libpthread/TODO       Mon Jan 13 20:26:25 2003 +0000
+++ b/lib/libpthread/TODO       Mon Jan 13 22:50:09 2003 +0000
@@ -51,8 +51,6 @@
 - Get rid of thread structures when too many accumulate (is this
   actually a good idea?)
 - Adaptive spin/sleep locks for mutexes.
-- Supporting different mutex types would be nice (normal, debugging,
-  recursive, etc).
 - Currently, each thread uses two real pages of memory: one at the top
   of the stack for actual stack data, and one at the bottom for the
   pthread_st. If we can get suitable space above the initial stack for
diff -r f3824c64ff5f -r ab6e3c8874eb lib/libpthread/pthread.h
--- a/lib/libpthread/pthread.h  Mon Jan 13 20:26:25 2003 +0000
+++ b/lib/libpthread/pthread.h  Mon Jan 13 22:50:09 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: pthread.h,v 1.1.2.19 2003/01/08 19:34:22 thorpej Exp $ */
+/*     $NetBSD: pthread.h,v 1.1.2.20 2003/01/13 22:50:09 thorpej Exp $ */
 
 /*-
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -80,6 +80,8 @@
 int    pthread_mutex_unlock(pthread_mutex_t *mutex);
 int    pthread_mutexattr_init(pthread_mutexattr_t *attr);
 int    pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
+int    pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
+int    pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
 
 int    pthread_cond_init(pthread_cond_t *cond,
            const pthread_condattr_t *attr);
@@ -180,4 +182,12 @@
 #define PTHREAD_STACK_MIN      4096 /* XXX Pulled out of my butt */
 #define PTHREAD_THREADS_MAX    64              /* Min. required */
 
+/*
+ * Mutex attributes.
+ */
+#define        PTHREAD_MUTEX_NORMAL            0
+#define        PTHREAD_MUTEX_ERRORCHECK        1
+#define        PTHREAD_MUTEX_RECURSIVE         2
+#define        PTHREAD_MUTEX_DEFAULT           PTHREAD_MUTEX_NORMAL
+
 #endif /* _LIB_PTHREAD_H */
diff -r f3824c64ff5f -r ab6e3c8874eb lib/libpthread/pthread_mutex.c
--- a/lib/libpthread/pthread_mutex.c    Mon Jan 13 20:26:25 2003 +0000
+++ b/lib/libpthread/pthread_mutex.c    Mon Jan 13 22:50:09 2003 +0000
@@ -1,11 +1,11 @@
-/*     $NetBSD: pthread_mutex.c,v 1.1.2.18 2003/01/09 19:27:52 thorpej Exp $   */
+/*     $NetBSD: pthread_mutex.c,v 1.1.2.19 2003/01/13 22:50:10 thorpej Exp $   */
 
 /*-
- * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * Copyright (c) 2001, 2003 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
- * by Nathan J. Williams.
+ * by Nathan J. Williams, and by Jason R. Thorpe.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -36,14 +36,16 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
 #include <assert.h>
 #include <errno.h>
-#include <sys/cdefs.h>
+#include <limits.h>
+#include <stdlib.h>
 
 #include "pthread.h"
 #include "pthread_int.h"
 
-static void pthread_mutex_lock_slow(pthread_mutex_t *);
+static int pthread_mutex_lock_slow(pthread_mutex_t *);
 
 __strong_alias(__libc_mutex_init,pthread_mutex_init)
 __strong_alias(__libc_mutex_lock,pthread_mutex_lock)
@@ -53,9 +55,42 @@
 
 __strong_alias(__libc_thr_once,pthread_once)
 
+struct mutex_private {
+       int     type;
+       int     recursecount;
+};
+
+static const struct mutex_private mutex_private_default = {
+       PTHREAD_MUTEX_DEFAULT,
+       0,
+};
+
+struct mutexattr_private {
+       int     type;
+};
+
+static const struct mutexattr_private mutexattr_private_default = {
+       PTHREAD_MUTEX_DEFAULT,
+};
+
+/*
+ * If the mutex does not already have private data (i.e. was statically
+ * initialized), then give it the default.
+ */
+#define        GET_MUTEX_PRIVATE(mutex, mp)                                    \
+do {                                                                   \
+       if (__predict_false((mp = (mutex)->ptm_private) == NULL)) {     \
+               /* LINTED cast away const */                            \
+               mp = ((mutex)->ptm_private =                            \
+                   (void *)&mutex_private_default);                    \
+       }                                                               \
+} while (/*CONSTCOND*/0)
+
 int
 pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
 {
+       struct mutexattr_private *map;
+       struct mutex_private *mp;
 
 #ifdef ERRORCHECK
        if ((mutex == NULL) || 
@@ -63,11 +98,25 @@
                return EINVAL;
 #endif
 
+       if (attr != NULL && (map = attr->ptma_private) != NULL &&
+           memcmp(map, &mutexattr_private_default, sizeof(*map)) != 0) {
+               mp = malloc(sizeof(*mp));
+               if (mp == NULL)
+                       return ENOMEM;
+
+               mp->type = map->type;
+               mp->recursecount = 0;
+       } else {
+               /* LINTED cast away const */
+               mp = (struct mutex_private *) &mutex_private_default;
+       }
+
        mutex->ptm_magic = _PT_MUTEX_MAGIC;
        mutex->ptm_owner = NULL;
        pthread_lockinit(&mutex->ptm_lock);
        pthread_lockinit(&mutex->ptm_interlock);
        PTQ_INIT(&mutex->ptm_blocked);
+       mutex->ptm_private = mp;
 
        return 0;
 }
@@ -85,6 +134,9 @@
 #endif
 
        mutex->ptm_magic = _PT_MUTEX_DEAD;
+       if (mutex->ptm_private != NULL &&
+           mutex->ptm_private != (void *)&mutex_private_default)
+               free(mutex->ptm_private);
 
        return 0;
 }
@@ -105,23 +157,31 @@
 int
 pthread_mutex_lock(pthread_mutex_t *mutex)
 {
+       int error;
 
 #ifdef ERRORCHECK
        if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
                return EINVAL;
 #endif
 
-       if (__predict_false(pthread__simple_lock_try(&mutex->ptm_lock) == 0))
-               pthread_mutex_lock_slow(mutex);
+       /*
+        * Note that if we get the lock, we don't have to deal with any
+        * non-default lock type handling.
+        */
+       if (__predict_false(pthread__simple_lock_try(&mutex->ptm_lock) == 0)) {
+               error = pthread_mutex_lock_slow(mutex);
+               if (error)
+                       return error;
+       }
 
        /* We have the lock! */
-#ifdef ERRORCHECK
-       mutex->ptm_owner = (pthread_t)pthread__sp();
-#endif 
+       mutex->ptm_owner = pthread__self();
+
        return 0;
 }
 
-static void
+
+static int
 pthread_mutex_lock_slow(pthread_mutex_t *mutex)
 {
        pthread_t self;
@@ -130,7 +190,7 @@
 
        while (/*CONSTCOND*/1) {
                if (pthread__simple_lock_try(&mutex->ptm_lock))
-                   break; /* got it! */
+                       break; /* got it! */
                
                /* Okay, didn't look free. Get the interlock... */
                pthread_spinlock(self, &mutex->ptm_interlock);
@@ -142,6 +202,32 @@
                 * again.
                 */
                if (mutex->ptm_lock == __SIMPLELOCK_LOCKED) {
+                       struct mutex_private *mp;
+
+                       GET_MUTEX_PRIVATE(mutex, mp);
+
+                       if (mutex->ptm_owner == self) {
+                               /*
+                                * It's safe to do this without holding the
+                                * interlock, because we only modify it if
+                                * we know we own the mutex.
+                                */
+                               switch (mp->type) {
+                               case PTHREAD_MUTEX_ERRORCHECK:
+                                       pthread_spinunlock(self,
+                                           &mutex->ptm_interlock);
+                                       return EDEADLK;
+
+                               case PTHREAD_MUTEX_RECURSIVE:
+                                       pthread_spinunlock(self,
+                                           &mutex->ptm_interlock);
+                                       if (mp->recursecount == INT_MAX)
+                                               return EAGAIN;
+                                       mp->recursecount++;
+                                       return 0;
+                               }
+                       }
+
                        PTQ_INSERT_TAIL(&mutex->ptm_blocked, self, pt_sleep);
                        /*
                         * Locking a mutex is not a cancellation
@@ -165,24 +251,49 @@
                }
                /* Go around for another try. */
        }
+
+       return 0;
 }
 
 
 int
 pthread_mutex_trylock(pthread_mutex_t *mutex)
 {
+       pthread_t self = pthread__self();
 
 #ifdef ERRORCHECK
        if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
                return EINVAL;
 #endif
 
-       if (pthread__simple_lock_try(&mutex->ptm_lock) == 0)
-               return EBUSY;
+       if (pthread__simple_lock_try(&mutex->ptm_lock) == 0) {
+               struct mutex_private *mp;
+
+               GET_MUTEX_PRIVATE(mutex, mp);
 
-#ifdef ERRORCHECK
-       mutex->ptm_owner = (pthread_t)pthread__sp();
-#endif
+               /*
+                * These tests can be performed without holding the
+                * interlock because these fields are only modified
+                * if we know we own the mutex.
+                */
+               if (mutex->ptm_owner == self) {
+                       switch (mp->type) {
+                       case PTHREAD_MUTEX_ERRORCHECK:
+                               return EDEADLK;
+
+                       case PTHREAD_MUTEX_RECURSIVE:
+                               if (mp->recursecount == INT_MAX)
+                                       return EAGAIN;
+                               mp->recursecount++;
+                               return 0;
+                       }
+               }
+
+               return EBUSY;
+       }
+
+       mutex->ptm_owner = self;
+
        return 0;
 }
 
@@ -190,6 +301,7 @@
 int
 pthread_mutex_unlock(pthread_mutex_t *mutex)
 {
+       struct mutex_private *mp;
        pthread_t self, blocked; 



Home | Main Index | Thread Index | Old Index