Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 22 Mar 2016 10:51:43 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r297185 - head/lib/libthr/thread
Message-ID:  <201603221051.u2MAphFa074523@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Tue Mar 22 10:51:42 2016
New Revision: 297185
URL: https://svnweb.freebsd.org/changeset/base/297185

Log:
  Apparently there are some popular programs around which assume that it
  is safe to call pthread_mutex_init() on the same shared mutex several
  times.  POSIX claims that the behaviour in this case is undefined.
  
  Make this working by only allowing one caller to initialize the mutex.
  Other callers either see already completed initialization and do
  nothing, or busy-loop yielding while designated initializer finishes.
  Also make the API requirements loose by initializing mutexes on other
  pthread_mutex*() calls if they see uninitialized shared mutex.
  
  Only mutexes provide the hack for now, but it could be also
  implemented for other process shared primitives from libthr.
  
  Reported and tested by:	"Oleg V. Nauman" <oleg@opentransfer.com>
  Sponsored by:	The FreeBSD Foundation

Modified:
  head/lib/libthr/thread/thr_mutex.c
  head/lib/libthr/thread/thr_private.h

Modified: head/lib/libthr/thread/thr_mutex.c
==============================================================================
--- head/lib/libthr/thread/thr_mutex.c	Tue Mar 22 08:36:25 2016	(r297184)
+++ head/lib/libthr/thread/thr_mutex.c	Tue Mar 22 10:51:42 2016	(r297185)
@@ -38,6 +38,7 @@
  * $FreeBSD$
  */
 
+#include <stdbool.h>
 #include "namespace.h"
 #include <stdlib.h>
 #include <errno.h>
@@ -264,6 +265,51 @@ set_inherited_priority(struct pthread *c
 		m->m_lock.m_ceilings[1] = -1;
 }
 
+static void
+shared_mutex_init(struct pthread_mutex *pmtx, const struct
+    pthread_mutex_attr *mutex_attr)
+{
+	static const struct pthread_mutex_attr foobar_mutex_attr = {
+		.m_type = PTHREAD_MUTEX_DEFAULT,
+		.m_protocol = PTHREAD_PRIO_NONE,
+		.m_ceiling = 0,
+		.m_pshared = PTHREAD_PROCESS_SHARED
+	};
+	bool done;
+
+	/*
+	 * Hack to allow multiple pthread_mutex_init() calls on the
+	 * same process-shared mutex.  We rely on kernel allocating
+	 * zeroed offpage for the mutex, i.e. the
+	 * PMUTEX_INITSTAGE_ALLOC value must be zero.
+	 */
+	for (done = false; !done;) {
+		switch (pmtx->m_ps) {
+		case PMUTEX_INITSTAGE_DONE:
+			atomic_thread_fence_acq();
+			done = true;
+			break;
+		case PMUTEX_INITSTAGE_ALLOC:
+			if (atomic_cmpset_int(&pmtx->m_ps,
+			    PMUTEX_INITSTAGE_ALLOC, PMUTEX_INITSTAGE_BUSY)) {
+				if (mutex_attr == NULL)
+					mutex_attr = &foobar_mutex_attr;
+				mutex_init_body(pmtx, mutex_attr);
+				atomic_store_rel_int(&pmtx->m_ps,
+				    PMUTEX_INITSTAGE_DONE);
+				done = true;
+			}
+			break;
+		case PMUTEX_INITSTAGE_BUSY:
+			_pthread_yield();
+			break;
+		default:
+			PANIC("corrupted offpage");
+			break;
+		}
+	}
+}
+
 int
 __pthread_mutex_init(pthread_mutex_t *mutex,
     const pthread_mutexattr_t *mutex_attr)
@@ -285,7 +331,7 @@ __pthread_mutex_init(pthread_mutex_t *mu
 	if (pmtx == NULL)
 		return (EFAULT);
 	*mutex = THR_PSHARED_PTR;
-	mutex_init_body(pmtx, *mutex_attr);
+	shared_mutex_init(pmtx, *mutex_attr);
 	return (0);
 }
 
@@ -426,6 +472,7 @@ check_and_init_mutex(pthread_mutex_t *mu
 		*m = __thr_pshared_offpage(mutex, 0);
 		if (*m == NULL)
 			ret = EINVAL;
+		shared_mutex_init(*m, NULL);
 	} else if (__predict_false(*m <= THR_MUTEX_DESTROYED)) {
 		if (*m == THR_MUTEX_DESTROYED) {
 			ret = EINVAL;
@@ -588,6 +635,7 @@ _pthread_mutex_unlock(pthread_mutex_t *m
 		mp = __thr_pshared_offpage(mutex, 0);
 		if (mp == NULL)
 			return (EINVAL);
+		shared_mutex_init(mp, NULL);
 	} else {
 		mp = *mutex;
 	}
@@ -815,6 +863,7 @@ _pthread_mutex_getprioceiling(pthread_mu
 		m = __thr_pshared_offpage(mutex, 0);
 		if (m == NULL)
 			return (EINVAL);
+		shared_mutex_init(m, NULL);
 	} else {
 		m = *mutex;
 		if (m <= THR_MUTEX_DESTROYED)
@@ -839,6 +888,7 @@ _pthread_mutex_setprioceiling(pthread_mu
 		m = __thr_pshared_offpage(mutex, 0);
 		if (m == NULL)
 			return (EINVAL);
+		shared_mutex_init(m, NULL);
 	} else {
 		m = *mutex;
 		if (m <= THR_MUTEX_DESTROYED)
@@ -942,12 +992,13 @@ __pthread_mutex_setyieldloops_np(pthread
 int
 _pthread_mutex_isowned_np(pthread_mutex_t *mutex)
 {
-	struct pthread_mutex	*m;
+	struct pthread_mutex *m;
 
 	if (*mutex == THR_PSHARED_PTR) {
 		m = __thr_pshared_offpage(mutex, 0);
 		if (m == NULL)
 			return (0);
+		shared_mutex_init(m, NULL);
 	} else {
 		m = *mutex;
 		if (m <= THR_MUTEX_DESTROYED)

Modified: head/lib/libthr/thread/thr_private.h
==============================================================================
--- head/lib/libthr/thread/thr_private.h	Tue Mar 22 08:36:25 2016	(r297184)
+++ head/lib/libthr/thread/thr_private.h	Tue Mar 22 10:51:42 2016	(r297185)
@@ -146,6 +146,13 @@ TAILQ_HEAD(mutex_queue, pthread_mutex);
 
 #define MAX_DEFER_WAITERS       50
 
+/*
+ * Values for pthread_mutex m_ps indicator.
+ */
+#define	PMUTEX_INITSTAGE_ALLOC	0
+#define	PMUTEX_INITSTAGE_BUSY	1
+#define	PMUTEX_INITSTAGE_DONE	2
+
 struct pthread_mutex {
 	/*
 	 * Lock for accesses to this structure.
@@ -156,6 +163,7 @@ struct pthread_mutex {
 	int				m_count;
 	int				m_spinloops;
 	int				m_yieldloops;
+	int				m_ps;	/* pshared init stage */
 	/*
 	 * Link for all mutexes a thread currently owns, of the same
 	 * prio type.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201603221051.u2MAphFa074523>