summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Guenthe <guenther@cvs.openbsd.org>2012-03-20 00:47:24 +0000
committerPhilip Guenthe <guenther@cvs.openbsd.org>2012-03-20 00:47:24 +0000
commitf20d2e0042fa909488b83bbb2a0f664c2d1a7299 (patch)
tree027d26c619f500e7fb6ba32b875fc769cb0e11ea
parent3878465307b8010631b215296da1e11a958e1b5a (diff)
Permit recursive locking in _rthread_dl_lock(), as an so's destructor
may need to call dlclose(). problem observed by Antti Harri (iku at openbsd.fi), ok kurt@
-rw-r--r--lib/librthread/rthread.c44
1 files changed, 42 insertions, 2 deletions
diff --git a/lib/librthread/rthread.c b/lib/librthread/rthread.c
index 110c8bab268..72224990978 100644
--- a/lib/librthread/rthread.c
+++ b/lib/librthread/rthread.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rthread.c,v 1.58 2012/03/14 21:23:37 guenther Exp $ */
+/* $OpenBSD: rthread.c,v 1.59 2012/03/20 00:47:23 guenther Exp $ */
/*
* Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
* All Rights Reserved.
@@ -580,15 +580,55 @@ _thread_dump_info(void)
}
#if defined(__ELF__) && defined(PIC)
+/*
+ * _rthread_dl_lock() provides the locking for dlopen(), dlclose(), and
+ * the function called via atexit() to invoke all destructors. The latter
+ * two call shared-object destructors, which may need to call dlclose(),
+ * so this lock needs to permit recursive locking.
+ * The specific code here was extracted from _rthread_mutex_lock() and
+ * pthread_mutex_unlock() and simplified to use the static variables.
+ */
void
_rthread_dl_lock(int what)
{
static _spinlock_lock_t lock = _SPINLOCK_UNLOCKED;
+ static pthread_t owner = NULL;
+ static struct pthread_queue lockers = TAILQ_HEAD_INITIALIZER(lockers);
+ static int count = 0;
if (what == 0)
+ {
+ pthread_t self = pthread_self();
+
+ /* lock, possibly recursive */
_spinlock(&lock);
- else
+ if (owner == NULL) {
+ owner = self;
+ } else if (owner != self) {
+ TAILQ_INSERT_TAIL(&lockers, self, waiting);
+ while (owner != self) {
+ __thrsleep(self, 0, NULL, &lock, NULL);
+ _spinlock(&lock);
+ }
+ }
+ count++;
_spinunlock(&lock);
+ }
+ else
+ {
+ /* unlock, possibly recursive */
+ if (--count == 0) {
+ pthread_t next;
+
+ _spinlock(&lock);
+ owner = next = TAILQ_FIRST(&lockers);
+ if (next != NULL)
+ TAILQ_REMOVE(&lockers, next, waiting);
+ _spinunlock(&lock);
+ if (next != NULL)
+ __thrwakeup(next, 1);
+ }
+ }
}
void