Subject: [nathanw_sa] RAS locking in libpthread
To: None <tech-userlevel@netbsd.org>
From: Jason R Thorpe <thorpej@wasabisystems.com>
List: tech-userlevel
Date: 12/29/2002 17:17:39
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
The following adds support for using RAS locking to libpthread. It is
preferred on uniprocessors where RAS is available. Multiprocessors always
use atomic locking.
This is only minimally tested at this time -- If there are no objections,
I'll check it in after a more thorough round of testing.
--
-- Jason R. Thorpe <thorpej@wasabisystems.com>
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=pthread-ras-patch
Index: TODO
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/TODO,v
retrieving revision 1.1.2.16
diff -c -r1.1.2.16 TODO
*** TODO 2002/10/30 07:35:31 1.1.2.16
--- TODO 2002/12/30 01:12:36
***************
*** 21,29 ****
- Consider moving pthread__signal_tramp() to its own file, and building
it with -fasync-unwind-tables, so that DWARF2 EH unwinding works through
it. (This is required for e.g. GCC's libjava.)
- - locks: Add support for RAS on uniprocessors.
- - pthread_lock.c: pthread_spin_lock() should use a loop around
- __cpu_simple_lock_try() so that it is easy to add support for RAS.
- Add locking to ld.elf_so so that multiple threads doing lazy binding
doesn't trash things.
- Verify the cancel stub symbol trickery.
--- 21,26 ----
Index: pthread.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread.c,v
retrieving revision 1.1.2.36
diff -c -r1.1.2.36 pthread.c
*** pthread.c 2002/12/20 01:06:16 1.1.2.36
--- pthread.c 2002/12/30 01:12:36
***************
*** 141,146 ****
--- 141,147 ----
PTQ_INSERT_HEAD(&allqueue, first, pt_allq);
/* Start subsystems */
+ pthread__lock_init();
pthread__alarm_init();
pthread__signal_init();
PTHREAD_MD_INIT
Index: pthread_int.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_int.h,v
retrieving revision 1.1.2.36
diff -c -r1.1.2.36 pthread_int.h
*** pthread_int.h 2002/12/16 18:17:46 1.1.2.36
--- pthread_int.h 2002/12/30 01:12:37
***************
*** 151,158 ****
#endif
};
-
/* Thread types */
#define PT_THREAD_NORMAL 1
#define PT_THREAD_UPCALL 2
--- 151,162 ----
#endif
};
+ struct pthread_lock_ops {
+ void (*plo_init)(__cpu_simple_lock_t *);
+ int (*plo_try)(__cpu_simple_lock_t *);
+ void (*plo_unlock)(__cpu_simple_lock_t *);
+ };
/* Thread types */
#define PT_THREAD_NORMAL 1
#define PT_THREAD_UPCALL 2
***************
*** 235,244 ****
--- 239,255 ----
void pthread__alarm_process(pthread_t self, void *arg);
/* Internal locking primitives */
+ void pthread__lock_init(void);
void pthread_lockinit(pthread_spin_t *lock);
void pthread_spinlock(pthread_t thread, pthread_spin_t *lock);
int pthread_spintrylock(pthread_t thread, pthread_spin_t *lock);
void pthread_spinunlock(pthread_t thread, pthread_spin_t *lock);
+
+ extern const struct pthread_lock_ops *pthread__lock_ops;
+
+ #define pthread__simple_lock_init(alp) (*pthread__lock_ops->plo_init)(alp)
+ #define pthread__simple_lock_try(alp) (*pthread__lock_ops->plo_try)(alp)
+ #define pthread__simple_unlock(alp) (*pthread__lock_ops->plo_unlock)(alp)
int _getcontext_u(ucontext_t *);
int _setcontext_u(const ucontext_t *);
Index: pthread_lock.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_lock.c,v
retrieving revision 1.1.2.13
diff -c -r1.1.2.13 pthread_lock.c
*** pthread_lock.c 2002/12/18 22:53:14 1.1.2.13
--- pthread_lock.c 2002/12/30 01:12:37
***************
*** 36,41 ****
--- 36,45 ----
* POSSIBILITY OF SUCH DAMAGE.
*/
+ #include <sys/param.h>
+ #include <sys/ras.h>
+ #include <sys/sysctl.h>
+
#include <assert.h>
#include <errno.h>
#include <unistd.h>
***************
*** 56,66 ****
static int nspins = NSPINS;
void
pthread_lockinit(pthread_spin_t *lock)
{
! __cpu_simple_lock_init(lock);
}
void
--- 60,164 ----
static int nspins = NSPINS;
+ extern char pthread__lock_ras_start[], pthread__lock_ras_end[];
+
+ static void
+ pthread__ras_simple_lock_init(__cpu_simple_lock_t *alp)
+ {
+
+ *alp = __SIMPLELOCK_UNLOCKED;
+ }
+
+ static int
+ pthread__ras_simple_lock_try(__cpu_simple_lock_t *alp)
+ {
+ __cpu_simple_lock_t old;
+
+ /* This is the atomic sequence. */
+ __asm __volatile("pthread__lock_ras_start:");
+ old = *alp;
+ *alp = __SIMPLELOCK_LOCKED;
+ __asm __volatile("pthread__lock_ras_stop:");
+
+ return (old == __SIMPLELOCK_UNLOCKED);
+ }
+
+ static void
+ pthread__ras_simple_unlock(__cpu_simple_lock_t *alp)
+ {
+
+ *alp = __SIMPLELOCK_UNLOCKED;
+ }
+
+ static const struct pthread_lock_ops pthread__lock_ops_ras = {
+ pthread__ras_simple_lock_init,
+ pthread__ras_simple_lock_try,
+ pthread__ras_simple_unlock,
+ };
+
+ static void
+ pthread__atomic_simple_lock_init(__cpu_simple_lock_t *alp)
+ {
+
+ __cpu_simple_lock_init(alp);
+ }
+
+ static int
+ pthread__atomic_simple_lock_try(__cpu_simple_lock_t *alp)
+ {
+
+ return (__cpu_simple_lock_try(alp));
+ }
+
+ static void
+ pthread__atomic_simple_unlock(__cpu_simple_lock_t *alp)
+ {
+
+ __cpu_simple_unlock(alp);
+ }
+
+ static const struct pthread_lock_ops pthread__lock_ops_atomic = {
+ pthread__atomic_simple_lock_init,
+ pthread__atomic_simple_lock_try,
+ pthread__atomic_simple_unlock,
+ };
+
+ const struct pthread_lock_ops *pthread__lock_ops;
+
+ /*
+ * Initialize the locking primitives. On uniprocessors, we always
+ * use Restartable Atomic Sequences if they are available. Otherwise,
+ * we fall back onto machine-dependent atomic lock primitives.
+ */
+ void
+ pthread__lock_init(void)
+ {
+ int mib[2];
+ size_t len;
+ int ncpu;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+
+ len = sizeof(ncpu);
+ sysctl(mib, 2, &ncpu, &len, NULL, 0);
+
+ if (ncpu == 1 &&
+ rasctl(pthread__lock_ras_start,
+ (caddr_t)pthread__lock_ras_end -
+ (caddr_t)pthread__lock_ras_start, RAS_INSTALL) == 0) {
+ pthread__lock_ops = &pthread__lock_ops_ras;
+ return;
+ }
+
+ pthread__lock_ops = &pthread__lock_ops_atomic;
+ }
+
void
pthread_lockinit(pthread_spin_t *lock)
{
! pthread__simple_lock_init(lock);
}
void
***************
*** 80,86 ****
++thread->pt_spinlocks;
do {
! while (((ret = __cpu_simple_lock_try(lock)) == 0) && --count)
;
if (ret == 1)
--- 178,184 ----
++thread->pt_spinlocks;
do {
! while (((ret = pthread__simple_lock_try(lock)) == 0) && --count)
;
if (ret == 1)
***************
*** 126,132 ****
thread, thread->pt_spinlocks));
++thread->pt_spinlocks;
! ret = __cpu_simple_lock_try(lock);
if (ret == 0) {
SDPRINTF(("(pthread_spintrylock %p) decrementing spinlock from %d\n",
--- 224,230 ----
thread, thread->pt_spinlocks));
++thread->pt_spinlocks;
! ret = pthread__simple_lock_try(lock);
if (ret == 0) {
SDPRINTF(("(pthread_spintrylock %p) decrementing spinlock from %d\n",
***************
*** 147,153 ****
pthread_spinunlock(pthread_t thread, pthread_spin_t *lock)
{
! __cpu_simple_unlock(lock);
SDPRINTF(("(pthread_spinunlock %p) decrementing spinlock %p (count %d)\n",
thread, lock, thread->pt_spinlocks));
--thread->pt_spinlocks;
--- 245,251 ----
pthread_spinunlock(pthread_t thread, pthread_spin_t *lock)
{
! pthread__simple_unlock(lock);
SDPRINTF(("(pthread_spinunlock %p) decrementing spinlock %p (count %d)\n",
thread, lock, thread->pt_spinlocks));
--thread->pt_spinlocks;
***************
*** 228,234 ****
return EINVAL;
#endif
! __cpu_simple_lock(&lock->pts_spin);
return 0;
}
--- 326,333 ----
return EINVAL;
#endif
! while (pthread__simple_lock_try(&lock->pts_spin) == 0)
! /* spin */ ;
return 0;
}
***************
*** 242,248 ****
return EINVAL;
#endif
! if (__cpu_simple_lock_try(&lock->pts_spin) == 0)
return EBUSY;
return 0;
--- 341,347 ----
return EINVAL;
#endif
! if (pthread__simple_lock_try(&lock->pts_spin) == 0)
return EBUSY;
return 0;
***************
*** 257,263 ****
return EINVAL;
#endif
! __cpu_simple_unlock(&lock->pts_spin);
return 0;
}
--- 356,362 ----
return EINVAL;
#endif
! pthread__simple_unlock(&lock->pts_spin);
return 0;
}
Index: pthread_mutex.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_mutex.c,v
retrieving revision 1.1.2.15
diff -c -r1.1.2.15 pthread_mutex.c
*** pthread_mutex.c 2002/10/26 02:17:44 1.1.2.15
--- pthread_mutex.c 2002/12/30 01:12:37
***************
*** 89,96 ****
* same mutex.
*
* A memory barrier after a lock and before an unlock will provide
! * this behavior. This code relies on __cpu_simple_lock_try() to issue
! * a barrier after obtaining a lock, and on __cpu_simple_unlock() to
* issue a barrier before releasing a lock.
*/
--- 89,96 ----
* same mutex.
*
* A memory barrier after a lock and before an unlock will provide
! * this behavior. This code relies on pthread__simple_lock_try() to issue
! * a barrier after obtaining a lock, and on pthread__simple_unlock() to
* issue a barrier before releasing a lock.
*/
***************
*** 103,109 ****
return EINVAL;
#endif
! if (__predict_false(__cpu_simple_lock_try(&mutex->ptm_lock) == 0))
pthread_mutex_lock_slow(mutex);
/* We have the lock! */
--- 103,109 ----
return EINVAL;
#endif
! if (__predict_false(pthread__simple_lock_try(&mutex->ptm_lock) == 0))
pthread_mutex_lock_slow(mutex);
/* We have the lock! */
***************
*** 121,127 ****
self = pthread__self();
while (/*CONSTCOND*/1) {
! if (__cpu_simple_lock_try(&mutex->ptm_lock))
break; /* got it! */
/* Okay, didn't look free. Get the interlock... */
--- 121,127 ----
self = pthread__self();
while (/*CONSTCOND*/1) {
! if (pthread__simple_lock_try(&mutex->ptm_lock))
break; /* got it! */
/* Okay, didn't look free. Get the interlock... */
***************
*** 169,175 ****
return EINVAL;
#endif
! if (__cpu_simple_lock_try(&mutex->ptm_lock) == 0)
return EBUSY;
#ifdef ERRORCHECK
--- 169,175 ----
return EINVAL;
#endif
! if (pthread__simple_lock_try(&mutex->ptm_lock) == 0)
return EBUSY;
#ifdef ERRORCHECK
***************
*** 201,207 ****
#ifdef ERRORCHECK
mutex->ptm_owner = NULL;
#endif
! __cpu_simple_unlock(&mutex->ptm_lock);
pthread_spinunlock(self, &mutex->ptm_interlock);
/* Give the head of the blocked queue another try. */
--- 201,207 ----
#ifdef ERRORCHECK
mutex->ptm_owner = NULL;
#endif
! pthread__simple_unlock(&mutex->ptm_lock);
pthread_spinunlock(self, &mutex->ptm_interlock);
/* Give the head of the blocked queue another try. */
Index: pthread_sa.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_sa.c,v
retrieving revision 1.1.2.34
diff -c -r1.1.2.34 pthread_sa.c
*** pthread_sa.c 2002/12/16 18:32:21 1.1.2.34
--- pthread_sa.c 2002/12/30 01:12:38
***************
*** 381,387 ****
*/
lock = victim->pt_heldlock;
victim->pt_heldlock = NULL;
! __cpu_simple_unlock(lock);
victim->pt_uc =
victim->pt_sleepuc;
victim->pt_sleepuc = NULL;
--- 381,387 ----
*/
lock = victim->pt_heldlock;
victim->pt_heldlock = NULL;
! pthread__simple_unlock(lock);
victim->pt_uc =
victim->pt_sleepuc;
victim->pt_sleepuc = NULL;
Index: pthread_types.h
===================================================================
RCS file: /cvsroot/src/lib/libpthread/Attic/pthread_types.h,v
retrieving revision 1.1.2.9
diff -c -r1.1.2.9 pthread_types.h
*** pthread_types.h 2002/10/28 00:06:09 1.1.2.9
--- pthread_types.h 2002/12/30 01:12:38
***************
*** 89,95 ****
unsigned int ptm_magic;
/* Not a real spinlock; will never be spun on. Locked with
! * __cpu_simple_lock_try() or not at all. Therefore, not
* subject to preempted-spinlock-continuation.
*
* Open research question: Would it help threaded applications if
--- 89,95 ----
unsigned int ptm_magic;
/* Not a real spinlock; will never be spun on. Locked with
! * pthread__simple_lock_try() or not at all. Therefore, not
* subject to preempted-spinlock-continuation.
*
* Open research question: Would it help threaded applications if
--0OAP2g/MAC+5xKAE--