xref: /netbsd-src/lib/libpthread/pthread_mutex.c (revision 568eb77ef2a2b1fbe2457d69c7aa6e1dd3d8de8b)
1*568eb77eSriastradh /*	$NetBSD: pthread_mutex.c,v 1.83 2022/04/10 10:38:33 riastradh Exp $	*/
2c62a74e6Sthorpej 
3c62a74e6Sthorpej /*-
4bc77394cSad  * Copyright (c) 2001, 2003, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc.
5c62a74e6Sthorpej  * All rights reserved.
6c62a74e6Sthorpej  *
7c62a74e6Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8b0427b61Sad  * by Nathan J. Williams, by Jason R. Thorpe, and by Andrew Doran.
9c62a74e6Sthorpej  *
10c62a74e6Sthorpej  * Redistribution and use in source and binary forms, with or without
11c62a74e6Sthorpej  * modification, are permitted provided that the following conditions
12c62a74e6Sthorpej  * are met:
13c62a74e6Sthorpej  * 1. Redistributions of source code must retain the above copyright
14c62a74e6Sthorpej  *    notice, this list of conditions and the following disclaimer.
15c62a74e6Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
16c62a74e6Sthorpej  *    notice, this list of conditions and the following disclaimer in the
17c62a74e6Sthorpej  *    documentation and/or other materials provided with the distribution.
18c62a74e6Sthorpej  *
19c62a74e6Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20c62a74e6Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21c62a74e6Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22c62a74e6Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23c62a74e6Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24c62a74e6Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25c62a74e6Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26c62a74e6Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27c62a74e6Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28c62a74e6Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29c62a74e6Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
30c62a74e6Sthorpej  */
31c62a74e6Sthorpej 
32d5afa113Sad /*
33d5afa113Sad  * To track threads waiting for mutexes to be released, we use lockless
34d5afa113Sad  * lists built on atomic operations and memory barriers.
35d5afa113Sad  *
36d5afa113Sad  * A simple spinlock would be faster and make the code easier to
37d5afa113Sad  * follow, but spinlocks are problematic in userspace.  If a thread is
38d5afa113Sad  * preempted by the kernel while holding a spinlock, any other thread
39d5afa113Sad  * attempting to acquire that spinlock will needlessly busy wait.
40d5afa113Sad  *
41d5afa113Sad  * There is no good way to know that the holding thread is no longer
42d5afa113Sad  * running, nor to request a wake-up once it has begun running again.
43d5afa113Sad  * Of more concern, threads in the SCHED_FIFO class do not have a
44d5afa113Sad  * limited time quantum and so could spin forever, preventing the
45d5afa113Sad  * thread holding the spinlock from getting CPU time: it would never
46d5afa113Sad  * be released.
47d5afa113Sad  */
48d5afa113Sad 
49c62a74e6Sthorpej #include <sys/cdefs.h>
50*568eb77eSriastradh __RCSID("$NetBSD: pthread_mutex.c,v 1.83 2022/04/10 10:38:33 riastradh Exp $");
517adb4107Sriastradh 
527adb4107Sriastradh /* Need to use libc-private names for atomic operations. */
537adb4107Sriastradh #include "../../common/lib/libc/atomic/atomic_op_namespace.h"
54b43749fdSad 
55b43749fdSad #include <sys/types.h>
56a67e1e34Sad #include <sys/lwpctl.h>
577cf7644fSchristos #include <sys/sched.h>
582b0c4dcfSmatt #include <sys/lock.h>
59f043c0fbSlukem 
60c62a74e6Sthorpej #include <errno.h>
61c62a74e6Sthorpej #include <limits.h>
62c62a74e6Sthorpej #include <stdlib.h>
6371d484f9Schristos #include <time.h>
645f7bdeaeSscw #include <string.h>
65a67e1e34Sad #include <stdio.h>
66c62a74e6Sthorpej 
67c62a74e6Sthorpej #include "pthread.h"
68c62a74e6Sthorpej #include "pthread_int.h"
6971d484f9Schristos #include "reentrant.h"
70c62a74e6Sthorpej 
71a67e1e34Sad #define	MUTEX_RECURSIVE_BIT		((uintptr_t)0x02)
727cf7644fSchristos #define	MUTEX_PROTECT_BIT		((uintptr_t)0x08)
737cf7644fSchristos #define	MUTEX_THREAD			((uintptr_t)~0x0f)
74a67e1e34Sad 
75a67e1e34Sad #define	MUTEX_RECURSIVE(x)		((uintptr_t)(x) & MUTEX_RECURSIVE_BIT)
767cf7644fSchristos #define	MUTEX_PROTECT(x)		((uintptr_t)(x) & MUTEX_PROTECT_BIT)
77a67e1e34Sad #define	MUTEX_OWNER(x)			((uintptr_t)(x) & MUTEX_THREAD)
78a67e1e34Sad 
797cf7644fSchristos #define	MUTEX_GET_TYPE(x)		\
807cf7644fSchristos     ((int)(((uintptr_t)(x) & 0x000000ff) >> 0))
817cf7644fSchristos #define	MUTEX_SET_TYPE(x, t) 		\
827cf7644fSchristos     (x) = (void *)(((uintptr_t)(x) & ~0x000000ff) | ((t) << 0))
837cf7644fSchristos #define	MUTEX_GET_PROTOCOL(x)		\
847cf7644fSchristos     ((int)(((uintptr_t)(x) & 0x0000ff00) >> 8))
857cf7644fSchristos #define	MUTEX_SET_PROTOCOL(x, p)	\
867cf7644fSchristos     (x) = (void *)(((uintptr_t)(x) & ~0x0000ff00) | ((p) << 8))
877cf7644fSchristos #define	MUTEX_GET_CEILING(x)		\
887cf7644fSchristos     ((int)(((uintptr_t)(x) & 0x00ff0000) >> 16))
897cf7644fSchristos #define	MUTEX_SET_CEILING(x, c)	\
907cf7644fSchristos     (x) = (void *)(((uintptr_t)(x) & ~0x00ff0000) | ((c) << 16))
917cf7644fSchristos 
92a67e1e34Sad #if __GNUC_PREREQ__(3, 0)
93a67e1e34Sad #define	NOINLINE		__attribute ((noinline))
94a67e1e34Sad #else
95a67e1e34Sad #define	NOINLINE		/* nothing */
96a67e1e34Sad #endif
97a67e1e34Sad 
9862e0939eSad struct waiter {
9962e0939eSad 	struct waiter	*volatile next;
10062e0939eSad 	lwpid_t		volatile lid;
10162e0939eSad };
10262e0939eSad 
10362e0939eSad static void	pthread__mutex_wakeup(pthread_t, struct pthread__waiter *);
1047cf7644fSchristos static int	pthread__mutex_lock_slow(pthread_mutex_t *,
1057cf7644fSchristos     const struct timespec *);
106a67e1e34Sad static void	pthread__mutex_pause(void);
107c62a74e6Sthorpej 
108989565f8Sad int		_pthread_mutex_held_np(pthread_mutex_t *);
109989565f8Sad pthread_t	_pthread_mutex_owner_np(pthread_mutex_t *);
110989565f8Sad 
__weak_alias(pthread_mutex_held_np,_pthread_mutex_held_np)111989565f8Sad __weak_alias(pthread_mutex_held_np,_pthread_mutex_held_np)
112989565f8Sad __weak_alias(pthread_mutex_owner_np,_pthread_mutex_owner_np)
113989565f8Sad 
114c62a74e6Sthorpej __strong_alias(__libc_mutex_init,pthread_mutex_init)
115c62a74e6Sthorpej __strong_alias(__libc_mutex_lock,pthread_mutex_lock)
116c62a74e6Sthorpej __strong_alias(__libc_mutex_trylock,pthread_mutex_trylock)
117c62a74e6Sthorpej __strong_alias(__libc_mutex_unlock,pthread_mutex_unlock)
118c62a74e6Sthorpej __strong_alias(__libc_mutex_destroy,pthread_mutex_destroy)
119c62a74e6Sthorpej 
120592409d3Sthorpej __strong_alias(__libc_mutexattr_init,pthread_mutexattr_init)
121592409d3Sthorpej __strong_alias(__libc_mutexattr_destroy,pthread_mutexattr_destroy)
122096005bfSthorpej __strong_alias(__libc_mutexattr_settype,pthread_mutexattr_settype)
123592409d3Sthorpej 
124c62a74e6Sthorpej int
125a67e1e34Sad pthread_mutex_init(pthread_mutex_t *ptm, const pthread_mutexattr_t *attr)
126c62a74e6Sthorpej {
1277cf7644fSchristos 	uintptr_t type, proto, val, ceil;
128c62a74e6Sthorpej 
129331480e6Skamil #if 0
1302cb70116Schristos 	/*
1312cb70116Schristos 	 * Always initialize the mutex structure, maybe be used later
1322cb70116Schristos 	 * and the cost should be minimal.
1332cb70116Schristos 	 */
13471d484f9Schristos 	if (__predict_false(__uselibcstub))
13571d484f9Schristos 		return __libc_mutex_init_stub(ptm, attr);
136331480e6Skamil #endif
13771d484f9Schristos 
138260b3a17Skamil 	pthread__error(EINVAL, "Invalid mutes attribute",
139260b3a17Skamil 	    attr == NULL || attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
140260b3a17Skamil 
1417cf7644fSchristos 	if (attr == NULL) {
142a67e1e34Sad 		type = PTHREAD_MUTEX_NORMAL;
1437cf7644fSchristos 		proto = PTHREAD_PRIO_NONE;
1447cf7644fSchristos 		ceil = 0;
1457cf7644fSchristos 	} else {
1467cf7644fSchristos 		val = (uintptr_t)attr->ptma_private;
147c62a74e6Sthorpej 
1487cf7644fSchristos 		type = MUTEX_GET_TYPE(val);
1497cf7644fSchristos 		proto = MUTEX_GET_PROTOCOL(val);
1507cf7644fSchristos 		ceil = MUTEX_GET_CEILING(val);
1517cf7644fSchristos 	}
152a67e1e34Sad 	switch (type) {
153a67e1e34Sad 	case PTHREAD_MUTEX_ERRORCHECK:
1542b0c4dcfSmatt 		__cpu_simple_lock_set(&ptm->ptm_errorcheck);
155a67e1e34Sad 		ptm->ptm_owner = NULL;
156a67e1e34Sad 		break;
157a67e1e34Sad 	case PTHREAD_MUTEX_RECURSIVE:
1582b0c4dcfSmatt 		__cpu_simple_lock_clear(&ptm->ptm_errorcheck);
159a67e1e34Sad 		ptm->ptm_owner = (void *)MUTEX_RECURSIVE_BIT;
160a67e1e34Sad 		break;
161a67e1e34Sad 	default:
1622b0c4dcfSmatt 		__cpu_simple_lock_clear(&ptm->ptm_errorcheck);
163a67e1e34Sad 		ptm->ptm_owner = NULL;
164a67e1e34Sad 		break;
165c62a74e6Sthorpej 	}
1667cf7644fSchristos 	switch (proto) {
1677cf7644fSchristos 	case PTHREAD_PRIO_PROTECT:
1687cf7644fSchristos 		val = (uintptr_t)ptm->ptm_owner;
1697cf7644fSchristos 		val |= MUTEX_PROTECT_BIT;
1707cf7644fSchristos 		ptm->ptm_owner = (void *)val;
1717cf7644fSchristos 		break;
172c62a74e6Sthorpej 
1737cf7644fSchristos 	}
174a67e1e34Sad 	ptm->ptm_magic = _PT_MUTEX_MAGIC;
175a67e1e34Sad 	ptm->ptm_waiters = NULL;
176377f098aSad 	ptm->ptm_recursed = 0;
1777cf7644fSchristos 	ptm->ptm_ceiling = (unsigned char)ceil;
178c62a74e6Sthorpej 
179c62a74e6Sthorpej 	return 0;
180c62a74e6Sthorpej }
181c62a74e6Sthorpej 
182c62a74e6Sthorpej int
pthread_mutex_destroy(pthread_mutex_t * ptm)183a67e1e34Sad pthread_mutex_destroy(pthread_mutex_t *ptm)
184c62a74e6Sthorpej {
185c62a74e6Sthorpej 
18671d484f9Schristos 	if (__predict_false(__uselibcstub))
18771d484f9Schristos 		return __libc_mutex_destroy_stub(ptm);
18871d484f9Schristos 
1893f6de8d8Snathanw 	pthread__error(EINVAL, "Invalid mutex",
190a67e1e34Sad 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
1913f6de8d8Snathanw 	pthread__error(EBUSY, "Destroying locked mutex",
192a67e1e34Sad 	    MUTEX_OWNER(ptm->ptm_owner) == 0);
193c62a74e6Sthorpej 
194a67e1e34Sad 	ptm->ptm_magic = _PT_MUTEX_DEAD;
195c62a74e6Sthorpej 	return 0;
196c62a74e6Sthorpej }
197c62a74e6Sthorpej 
198c62a74e6Sthorpej int
pthread_mutex_lock(pthread_mutex_t * ptm)199a67e1e34Sad pthread_mutex_lock(pthread_mutex_t *ptm)
200c62a74e6Sthorpej {
201b0427b61Sad 	pthread_t self;
202a67e1e34Sad 	void *val;
203c62a74e6Sthorpej 
20471d484f9Schristos 	if (__predict_false(__uselibcstub))
20571d484f9Schristos 		return __libc_mutex_lock_stub(ptm);
20671d484f9Schristos 
20712ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex",
20812ee584aSkamil 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
20912ee584aSkamil 
210b0427b61Sad 	self = pthread__self();
211a67e1e34Sad 	val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self);
212a67e1e34Sad 	if (__predict_true(val == NULL)) {
213a67e1e34Sad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
214a67e1e34Sad 		membar_enter();
215a67e1e34Sad #endif
216c62a74e6Sthorpej 		return 0;
217c62a74e6Sthorpej 	}
2187cf7644fSchristos 	return pthread__mutex_lock_slow(ptm, NULL);
2197cf7644fSchristos }
2207cf7644fSchristos 
2217cf7644fSchristos int
pthread_mutex_timedlock(pthread_mutex_t * ptm,const struct timespec * ts)2227cf7644fSchristos pthread_mutex_timedlock(pthread_mutex_t* ptm, const struct timespec *ts)
2237cf7644fSchristos {
2247cf7644fSchristos 	pthread_t self;
2257cf7644fSchristos 	void *val;
2267cf7644fSchristos 
22712ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex",
22812ee584aSkamil 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
22912ee584aSkamil 
2307cf7644fSchristos 	self = pthread__self();
2317cf7644fSchristos 	val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self);
2327cf7644fSchristos 	if (__predict_true(val == NULL)) {
2337cf7644fSchristos #ifndef PTHREAD__ATOMIC_IS_MEMBAR
2347cf7644fSchristos 		membar_enter();
2357cf7644fSchristos #endif
2367cf7644fSchristos 		return 0;
2377cf7644fSchristos 	}
2387cf7644fSchristos 	return pthread__mutex_lock_slow(ptm, ts);
239a67e1e34Sad }
240c62a74e6Sthorpej 
241a67e1e34Sad /* We want function call overhead. */
242a67e1e34Sad NOINLINE static void
pthread__mutex_pause(void)243a67e1e34Sad pthread__mutex_pause(void)
244c62a74e6Sthorpej {
245a67e1e34Sad 
246a67e1e34Sad 	pthread__smt_pause();
247a67e1e34Sad }
248a67e1e34Sad 
249a67e1e34Sad /*
250a67e1e34Sad  * Spin while the holder is running.  'lwpctl' gives us the true
25151002188Sad  * status of the thread.
252a67e1e34Sad  */
253a67e1e34Sad NOINLINE static void *
pthread__mutex_spin(pthread_mutex_t * ptm,pthread_t owner)254a67e1e34Sad pthread__mutex_spin(pthread_mutex_t *ptm, pthread_t owner)
255a67e1e34Sad {
256a67e1e34Sad 	pthread_t thread;
257a67e1e34Sad 	unsigned int count, i;
258a67e1e34Sad 
259a67e1e34Sad 	for (count = 2;; owner = ptm->ptm_owner) {
260a67e1e34Sad 		thread = (pthread_t)MUTEX_OWNER(owner);
261a67e1e34Sad 		if (thread == NULL)
262a67e1e34Sad 			break;
26351002188Sad 		if (thread->pt_lwpctl->lc_curcpu == LWPCTL_CPU_NONE)
264a67e1e34Sad 			break;
265a67e1e34Sad 		if (count < 128)
266a67e1e34Sad 			count += count;
267a67e1e34Sad 		for (i = count; i != 0; i--)
268a67e1e34Sad 			pthread__mutex_pause();
269a67e1e34Sad 	}
270a67e1e34Sad 
271a67e1e34Sad 	return owner;
272a67e1e34Sad }
273a67e1e34Sad 
274a67e1e34Sad NOINLINE static int
pthread__mutex_lock_slow(pthread_mutex_t * ptm,const struct timespec * ts)2757cf7644fSchristos pthread__mutex_lock_slow(pthread_mutex_t *ptm, const struct timespec *ts)
276a67e1e34Sad {
27762e0939eSad 	void *newval, *owner, *next;
27862e0939eSad 	struct waiter waiter;
279a67e1e34Sad 	pthread_t self;
280de79b493Schristos 	int serrno;
2817cf7644fSchristos 	int error;
282c62a74e6Sthorpej 
283a67e1e34Sad 	owner = ptm->ptm_owner;
284a67e1e34Sad 	self = pthread__self();
285bc77394cSad 	serrno = errno;
286bc77394cSad 
28762e0939eSad 	pthread__assert(self->pt_lid != 0);
288c62a74e6Sthorpej 
289a67e1e34Sad 	/* Recursive or errorcheck? */
290a67e1e34Sad 	if (MUTEX_OWNER(owner) == (uintptr_t)self) {
291a67e1e34Sad 		if (MUTEX_RECURSIVE(owner)) {
292377f098aSad 			if (ptm->ptm_recursed == INT_MAX)
293c62a74e6Sthorpej 				return EAGAIN;
294377f098aSad 			ptm->ptm_recursed++;
295a67e1e34Sad 			return 0;
296a67e1e34Sad 		}
2972b0c4dcfSmatt 		if (__SIMPLELOCK_LOCKED_P(&ptm->ptm_errorcheck))
298a67e1e34Sad 			return EDEADLK;
299a67e1e34Sad 	}
300a67e1e34Sad 
3017cf7644fSchristos 	/* priority protect */
3027cf7644fSchristos 	if (MUTEX_PROTECT(owner) && _sched_protect(ptm->ptm_ceiling) == -1) {
303bc77394cSad 		error = errno;
304bc77394cSad 		errno = serrno;
305bc77394cSad 		return error;
3067cf7644fSchristos 	}
307a67e1e34Sad 
308bc77394cSad 	for (;;) {
309a67e1e34Sad 		/* If it has become free, try to acquire it again. */
310a67e1e34Sad 		if (MUTEX_OWNER(owner) == 0) {
311bc77394cSad 			newval = (void *)((uintptr_t)self | (uintptr_t)owner);
312bc77394cSad 			next = atomic_cas_ptr(&ptm->ptm_owner, owner, newval);
313bc77394cSad 			if (__predict_false(next != owner)) {
314bc77394cSad 				owner = next;
315bc77394cSad 				continue;
316bc77394cSad 			}
317de79b493Schristos 			errno = serrno;
318a67e1e34Sad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
319a67e1e34Sad 			membar_enter();
320a67e1e34Sad #endif
321c62a74e6Sthorpej 			return 0;
322bc77394cSad 		} else if (MUTEX_OWNER(owner) != (uintptr_t)self) {
323bc77394cSad 			/* Spin while the owner is running. */
324bc77394cSad 			owner = pthread__mutex_spin(ptm, owner);
325bc77394cSad 			if (MUTEX_OWNER(owner) == 0) {
326a67e1e34Sad 				continue;
327a67e1e34Sad 			}
328bc77394cSad 		}
329c62a74e6Sthorpej 
330c62a74e6Sthorpej 		/*
331a67e1e34Sad 		 * Nope, still held.  Add thread to the list of waiters.
33262e0939eSad 		 * Issue a memory barrier to ensure stores to 'waiter'
33362e0939eSad 		 * are visible before we enter the list.
334c62a74e6Sthorpej 		 */
33562e0939eSad 		waiter.next = ptm->ptm_waiters;
33662e0939eSad 		waiter.lid = self->pt_lid;
337bc77394cSad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
338a67e1e34Sad 		membar_producer();
339bc77394cSad #endif
34062e0939eSad 		next = atomic_cas_ptr(&ptm->ptm_waiters, waiter.next, &waiter);
34162e0939eSad 		if (next != waiter.next) {
34262e0939eSad 			owner = ptm->ptm_owner;
34362e0939eSad 			continue;
344c62a74e6Sthorpej 		}
345c62a74e6Sthorpej 
346bc77394cSad 		/*
34762e0939eSad 		 * If the mutex has become free since entering self onto the
34862e0939eSad 		 * waiters list, need to wake everybody up (including self)
34962e0939eSad 		 * and retry.  It's possible to race with an unlocking
35062e0939eSad 		 * thread, so self may have already been awoken.
351bc77394cSad 		 */
352bc77394cSad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
35362e0939eSad 		membar_enter();
354bc77394cSad #endif
35562e0939eSad 		if (MUTEX_OWNER(ptm->ptm_owner) == 0) {
35662e0939eSad 			pthread__mutex_wakeup(self,
35762e0939eSad 			    atomic_swap_ptr(&ptm->ptm_waiters, NULL));
35851002188Sad 		}
359a67e1e34Sad 
360a67e1e34Sad 		/*
361bc77394cSad 		 * We must not proceed until told that we are no longer
36262e0939eSad 		 * waiting (via waiter.lid being set to zero).  Otherwise
36362e0939eSad 		 * it's unsafe to re-enter "waiter" onto the waiters list.
364a67e1e34Sad 		 */
36562e0939eSad 		while (waiter.lid != 0) {
36685d957d4Skre 			error = _lwp_park(CLOCK_REALTIME, TIMER_ABSTIME,
36762e0939eSad 			    __UNCONST(ts), 0, NULL, NULL);
36806d492d1Sad 			if (error < 0 && errno == ETIMEDOUT) {
3697cf7644fSchristos 				/* Remove self from waiters list */
37062e0939eSad 				pthread__mutex_wakeup(self,
37162e0939eSad 				    atomic_swap_ptr(&ptm->ptm_waiters, NULL));
372051faad4Sad 
373051faad4Sad 				/*
374051faad4Sad 				 * Might have raced with another thread to
375051faad4Sad 				 * do the wakeup.  In any case there will be
376051faad4Sad 				 * a wakeup for sure.  Eat it and wait for
37762e0939eSad 				 * waiter.lid to clear.
378051faad4Sad 				 */
37962e0939eSad 				while (waiter.lid != 0) {
38062e0939eSad 					(void)_lwp_park(CLOCK_MONOTONIC, 0,
38162e0939eSad 					    NULL, 0, NULL, NULL);
38262e0939eSad 				}
383051faad4Sad 
38406d492d1Sad 				/* Priority protect */
3857cf7644fSchristos 				if (MUTEX_PROTECT(owner))
3867cf7644fSchristos 					(void)_sched_protect(-1);
387bc77394cSad 				errno = serrno;
3887cf7644fSchristos 				return ETIMEDOUT;
3897cf7644fSchristos 			}
39062e0939eSad 		}
391bc77394cSad 		owner = ptm->ptm_owner;
392a67e1e34Sad 	}
393a67e1e34Sad }
394a67e1e34Sad 
395a67e1e34Sad int
pthread_mutex_trylock(pthread_mutex_t * ptm)396a67e1e34Sad pthread_mutex_trylock(pthread_mutex_t *ptm)
397a67e1e34Sad {
398a67e1e34Sad 	pthread_t self;
39968692162Sad 	void *val, *new, *next;
400a67e1e34Sad 
40171d484f9Schristos 	if (__predict_false(__uselibcstub))
40271d484f9Schristos 		return __libc_mutex_trylock_stub(ptm);
40371d484f9Schristos 
40412ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex",
40512ee584aSkamil 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
40612ee584aSkamil 
407a67e1e34Sad 	self = pthread__self();
408a67e1e34Sad 	val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self);
409a67e1e34Sad 	if (__predict_true(val == NULL)) {
410a67e1e34Sad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
411a67e1e34Sad 		membar_enter();
412a67e1e34Sad #endif
413c62a74e6Sthorpej 		return 0;
414c62a74e6Sthorpej 	}
415c62a74e6Sthorpej 
41668692162Sad 	if (MUTEX_RECURSIVE(val)) {
41768692162Sad 		if (MUTEX_OWNER(val) == 0) {
41868692162Sad 			new = (void *)((uintptr_t)self | (uintptr_t)val);
41968692162Sad 			next = atomic_cas_ptr(&ptm->ptm_owner, val, new);
42068692162Sad 			if (__predict_true(next == val)) {
42168692162Sad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
42268692162Sad 				membar_enter();
42368692162Sad #endif
42468692162Sad 				return 0;
42568692162Sad 			}
42668692162Sad 		}
42768692162Sad 		if (MUTEX_OWNER(val) == (uintptr_t)self) {
428377f098aSad 			if (ptm->ptm_recursed == INT_MAX)
429c62a74e6Sthorpej 				return EAGAIN;
430377f098aSad 			ptm->ptm_recursed++;
431c62a74e6Sthorpej 			return 0;
432c62a74e6Sthorpej 		}
43368692162Sad 	}
434c62a74e6Sthorpej 
435c62a74e6Sthorpej 	return EBUSY;
436c62a74e6Sthorpej }
437c62a74e6Sthorpej 
438a67e1e34Sad int
pthread_mutex_unlock(pthread_mutex_t * ptm)439a67e1e34Sad pthread_mutex_unlock(pthread_mutex_t *ptm)
440a67e1e34Sad {
441a67e1e34Sad 	pthread_t self;
44262e0939eSad 	void *val, *newval;
443bc77394cSad 	int error;
444c62a74e6Sthorpej 
44571d484f9Schristos 	if (__predict_false(__uselibcstub))
44671d484f9Schristos 		return __libc_mutex_unlock_stub(ptm);
44771d484f9Schristos 
44812ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex",
44912ee584aSkamil 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
45012ee584aSkamil 
451a67e1e34Sad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
452a67e1e34Sad 	membar_exit();
453a67e1e34Sad #endif
454a67e1e34Sad 	error = 0;
455bc77394cSad 	self = pthread__self();
45662e0939eSad 	newval = NULL;
4572639b90eSad 
45862e0939eSad 	val = atomic_cas_ptr(&ptm->ptm_owner, self, newval);
459bc77394cSad 	if (__predict_false(val != self)) {
460bc77394cSad 		bool weown = (MUTEX_OWNER(val) == (uintptr_t)self);
4612b0c4dcfSmatt 		if (__SIMPLELOCK_LOCKED_P(&ptm->ptm_errorcheck)) {
462a67e1e34Sad 			if (!weown) {
463a67e1e34Sad 				error = EPERM;
464bc77394cSad 				newval = val;
465a67e1e34Sad 			} else {
466bc77394cSad 				newval = NULL;
467a67e1e34Sad 			}
468bc77394cSad 		} else if (MUTEX_RECURSIVE(val)) {
469a67e1e34Sad 			if (!weown) {
470a67e1e34Sad 				error = EPERM;
471bc77394cSad 				newval = val;
472377f098aSad 			} else if (ptm->ptm_recursed) {
473377f098aSad 				ptm->ptm_recursed--;
474bc77394cSad 				newval = val;
475a67e1e34Sad 			} else {
476bc77394cSad 				newval = (pthread_t)MUTEX_RECURSIVE_BIT;
477a67e1e34Sad 			}
478a67e1e34Sad 		} else {
479a67e1e34Sad 			pthread__error(EPERM,
480bc77394cSad 			    "Unlocking unlocked mutex", (val != NULL));
4813f6de8d8Snathanw 			pthread__error(EPERM,
4823f6de8d8Snathanw 			    "Unlocking mutex owned by another thread", weown);
483bc77394cSad 			newval = NULL;
484baa07328Snathanw 		}
485b0427b61Sad 
4866cca91fcSnathanw 		/*
487a67e1e34Sad 		 * Release the mutex.  If there appear to be waiters, then
488a67e1e34Sad 		 * wake them up.
4896cca91fcSnathanw 		 */
490bc77394cSad 		if (newval != val) {
491bc77394cSad 			val = atomic_swap_ptr(&ptm->ptm_owner, newval);
492bc77394cSad 			if (__predict_false(MUTEX_PROTECT(val))) {
4937cf7644fSchristos 				/* restore elevated priority */
4947cf7644fSchristos 				(void)_sched_protect(-1);
4957cf7644fSchristos 			}
496c62a74e6Sthorpej 		}
497a67e1e34Sad 	}
498c62a74e6Sthorpej 
499a67e1e34Sad 	/*
500bc77394cSad 	 * Finally, wake any waiters and return.
501a67e1e34Sad 	 */
502bc77394cSad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
503bc77394cSad 	membar_enter();
504bc77394cSad #endif
50562e0939eSad 	if (MUTEX_OWNER(newval) == 0 && ptm->ptm_waiters != NULL) {
50662e0939eSad 		pthread__mutex_wakeup(self,
50762e0939eSad 		    atomic_swap_ptr(&ptm->ptm_waiters, NULL));
508a67e1e34Sad 	}
509a67e1e34Sad 	return error;
510a67e1e34Sad }
511a67e1e34Sad 
512a74bc621Syamt /*
513a74bc621Syamt  * pthread__mutex_wakeup: unpark threads waiting for us
514a74bc621Syamt  */
515a74bc621Syamt 
516a67e1e34Sad static void
pthread__mutex_wakeup(pthread_t self,struct pthread__waiter * cur)51762e0939eSad pthread__mutex_wakeup(pthread_t self, struct pthread__waiter *cur)
518a67e1e34Sad {
51962e0939eSad 	lwpid_t lids[PTHREAD__UNPARK_MAX];
52062e0939eSad 	const size_t mlid = pthread__unpark_max;
52162e0939eSad 	struct pthread__waiter *next;
52262e0939eSad 	size_t nlid;
523a67e1e34Sad 
524a67e1e34Sad 	/*
525bc77394cSad 	 * Pull waiters from the queue and add to our list.  Use a memory
52662e0939eSad 	 * barrier to ensure that we safely read the value of waiter->next
52762e0939eSad 	 * before the awoken thread sees waiter->lid being cleared.
528a67e1e34Sad 	 */
52962e0939eSad 	membar_datadep_consumer(); /* for alpha */
53062e0939eSad 	for (nlid = 0; cur != NULL; cur = next) {
53162e0939eSad 		if (nlid == mlid) {
53262e0939eSad 			(void)_lwp_unpark_all(lids, nlid, NULL);
53362e0939eSad 			nlid = 0;
534a67e1e34Sad 		}
53562e0939eSad 		next = cur->next;
53662e0939eSad 		pthread__assert(cur->lid != 0);
53762e0939eSad 		lids[nlid++] = cur->lid;
5382b4d5392Sad 		membar_exit();
53962e0939eSad 		cur->lid = 0;
54062e0939eSad 		/* No longer safe to touch 'cur' */
541a67e1e34Sad 	}
54262e0939eSad 	if (nlid == 1) {
54362e0939eSad 		(void)_lwp_unpark(lids[0], NULL);
54462e0939eSad 	} else if (nlid > 1) {
54562e0939eSad 		(void)_lwp_unpark_all(lids, nlid, NULL);
54662e0939eSad 	}
547a67e1e34Sad }
548a74bc621Syamt 
549c62a74e6Sthorpej int
pthread_mutexattr_init(pthread_mutexattr_t * attr)550c62a74e6Sthorpej pthread_mutexattr_init(pthread_mutexattr_t *attr)
551c62a74e6Sthorpej {
552331480e6Skamil #if 0
55371d484f9Schristos 	if (__predict_false(__uselibcstub))
55471d484f9Schristos 		return __libc_mutexattr_init_stub(attr);
555331480e6Skamil #endif
556c62a74e6Sthorpej 
557c62a74e6Sthorpej 	attr->ptma_magic = _PT_MUTEXATTR_MAGIC;
558a67e1e34Sad 	attr->ptma_private = (void *)PTHREAD_MUTEX_DEFAULT;
559c62a74e6Sthorpej 	return 0;
560c62a74e6Sthorpej }
561c62a74e6Sthorpej 
562c62a74e6Sthorpej int
pthread_mutexattr_destroy(pthread_mutexattr_t * attr)563c62a74e6Sthorpej pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
564c62a74e6Sthorpej {
56571d484f9Schristos 	if (__predict_false(__uselibcstub))
56671d484f9Schristos 		return __libc_mutexattr_destroy_stub(attr);
567c62a74e6Sthorpej 
5683f6de8d8Snathanw 	pthread__error(EINVAL, "Invalid mutex attribute",
5693f6de8d8Snathanw 	    attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
570c62a74e6Sthorpej 
571d48cac51Skamil 	attr->ptma_magic = _PT_MUTEXATTR_DEAD;
572d48cac51Skamil 
573c62a74e6Sthorpej 	return 0;
574c62a74e6Sthorpej }
575c62a74e6Sthorpej 
576c62a74e6Sthorpej int
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * typep)577c62a74e6Sthorpej pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *typep)
578c62a74e6Sthorpej {
5797cf7644fSchristos 
5803f6de8d8Snathanw 	pthread__error(EINVAL, "Invalid mutex attribute",
5813f6de8d8Snathanw 	    attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
582c62a74e6Sthorpej 
5837cf7644fSchristos 	*typep = MUTEX_GET_TYPE(attr->ptma_private);
584c62a74e6Sthorpej 	return 0;
585c62a74e6Sthorpej }
586c62a74e6Sthorpej 
587c62a74e6Sthorpej int
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)588c62a74e6Sthorpej pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
589c62a74e6Sthorpej {
5907cf7644fSchristos 
59171d484f9Schristos 	if (__predict_false(__uselibcstub))
59271d484f9Schristos 		return __libc_mutexattr_settype_stub(attr, type);
593c62a74e6Sthorpej 
5943f6de8d8Snathanw 	pthread__error(EINVAL, "Invalid mutex attribute",
5953f6de8d8Snathanw 	    attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
596b7ac97b8Snathanw 
597c62a74e6Sthorpej 	switch (type) {
598c62a74e6Sthorpej 	case PTHREAD_MUTEX_NORMAL:
599c62a74e6Sthorpej 	case PTHREAD_MUTEX_ERRORCHECK:
600c62a74e6Sthorpej 	case PTHREAD_MUTEX_RECURSIVE:
6017cf7644fSchristos 		MUTEX_SET_TYPE(attr->ptma_private, type);
602a67e1e34Sad 		return 0;
603c62a74e6Sthorpej 	default:
604c62a74e6Sthorpej 		return EINVAL;
605c62a74e6Sthorpej 	}
606c62a74e6Sthorpej }
607c62a74e6Sthorpej 
6087cf7644fSchristos int
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * proto)6097cf7644fSchristos pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int*proto)
6107cf7644fSchristos {
6117cf7644fSchristos 
6127cf7644fSchristos 	pthread__error(EINVAL, "Invalid mutex attribute",
6137cf7644fSchristos 	    attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
6147cf7644fSchristos 
6157cf7644fSchristos 	*proto = MUTEX_GET_PROTOCOL(attr->ptma_private);
6167cf7644fSchristos 	return 0;
6177cf7644fSchristos }
6187cf7644fSchristos 
6197cf7644fSchristos int
pthread_mutexattr_setprotocol(pthread_mutexattr_t * attr,int proto)6207cf7644fSchristos pthread_mutexattr_setprotocol(pthread_mutexattr_t* attr, int proto)
6217cf7644fSchristos {
6227cf7644fSchristos 
6237cf7644fSchristos 	pthread__error(EINVAL, "Invalid mutex attribute",
6247cf7644fSchristos 	    attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
6257cf7644fSchristos 
6267cf7644fSchristos 	switch (proto) {
6277cf7644fSchristos 	case PTHREAD_PRIO_NONE:
6287cf7644fSchristos 	case PTHREAD_PRIO_PROTECT:
6297cf7644fSchristos 		MUTEX_SET_PROTOCOL(attr->ptma_private, proto);
6307cf7644fSchristos 		return 0;
6317cf7644fSchristos 	case PTHREAD_PRIO_INHERIT:
6327cf7644fSchristos 		return ENOTSUP;
6337cf7644fSchristos 	default:
6347cf7644fSchristos 		return EINVAL;
6357cf7644fSchristos 	}
6367cf7644fSchristos }
6377cf7644fSchristos 
6387cf7644fSchristos int
pthread_mutexattr_getprioceiling(const pthread_mutexattr_t * attr,int * ceil)6397cf7644fSchristos pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *ceil)
6407cf7644fSchristos {
6417cf7644fSchristos 
6427cf7644fSchristos 	pthread__error(EINVAL, "Invalid mutex attribute",
6437cf7644fSchristos 		attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
6447cf7644fSchristos 
6457cf7644fSchristos 	*ceil = MUTEX_GET_CEILING(attr->ptma_private);
6467cf7644fSchristos 	return 0;
6477cf7644fSchristos }
6487cf7644fSchristos 
6497cf7644fSchristos int
pthread_mutexattr_setprioceiling(pthread_mutexattr_t * attr,int ceil)6507cf7644fSchristos pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int ceil)
6517cf7644fSchristos {
6527cf7644fSchristos 
6537cf7644fSchristos 	pthread__error(EINVAL, "Invalid mutex attribute",
6547cf7644fSchristos 		attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
6557cf7644fSchristos 
6567cf7644fSchristos 	if (ceil & ~0xff)
6577cf7644fSchristos 		return EINVAL;
6587cf7644fSchristos 
6597cf7644fSchristos 	MUTEX_SET_CEILING(attr->ptma_private, ceil);
6607cf7644fSchristos 	return 0;
6617cf7644fSchristos }
6627cf7644fSchristos 
6637cf7644fSchristos #ifdef _PTHREAD_PSHARED
6647cf7644fSchristos int
pthread_mutexattr_getpshared(const pthread_mutexattr_t * __restrict attr,int * __restrict pshared)6657cf7644fSchristos pthread_mutexattr_getpshared(const pthread_mutexattr_t * __restrict attr,
6667cf7644fSchristos     int * __restrict pshared)
6677cf7644fSchristos {
6687cf7644fSchristos 
66912ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex attribute",
67012ee584aSkamil 		attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
67112ee584aSkamil 
6727cf7644fSchristos 	*pshared = PTHREAD_PROCESS_PRIVATE;
6737cf7644fSchristos 	return 0;
6747cf7644fSchristos }
6757cf7644fSchristos 
6767cf7644fSchristos int
pthread_mutexattr_setpshared(pthread_mutexattr_t * attr,int pshared)6777cf7644fSchristos pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)
6787cf7644fSchristos {
6797cf7644fSchristos 
68012ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex attribute",
68112ee584aSkamil 		attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
68212ee584aSkamil 
6837cf7644fSchristos 	switch(pshared) {
6847cf7644fSchristos 	case PTHREAD_PROCESS_PRIVATE:
6857cf7644fSchristos 		return 0;
6867cf7644fSchristos 	case PTHREAD_PROCESS_SHARED:
6877cf7644fSchristos 		return ENOSYS;
6887cf7644fSchristos 	}
6897cf7644fSchristos 	return EINVAL;
6907cf7644fSchristos }
6917cf7644fSchristos #endif
6927cf7644fSchristos 
693a74bc621Syamt /*
694bc77394cSad  * In order to avoid unnecessary contention on interlocking mutexes, we try
695bc77394cSad  * to defer waking up threads until we unlock the mutex.  The threads will
69662e0939eSad  * be woken up when the calling thread (self) releases the mutex.
697a74bc621Syamt  */
6982bcb8bf1Sad void
pthread__mutex_deferwake(pthread_t self,pthread_mutex_t * ptm,struct pthread__waiter * head)69962e0939eSad pthread__mutex_deferwake(pthread_t self, pthread_mutex_t *ptm,
70062e0939eSad     struct pthread__waiter *head)
701f4fd6b79Sad {
70262e0939eSad 	struct pthread__waiter *tail, *n, *o;
70362e0939eSad 
70462e0939eSad 	pthread__assert(head != NULL);
705f4fd6b79Sad 
7062bcb8bf1Sad 	if (__predict_false(ptm == NULL ||
7072bcb8bf1Sad 	    MUTEX_OWNER(ptm->ptm_owner) != (uintptr_t)self)) {
70862e0939eSad 	    	pthread__mutex_wakeup(self, head);
70962e0939eSad 	    	return;
71062e0939eSad 	}
71162e0939eSad 
71262e0939eSad 	/* This is easy if no existing waiters on mutex. */
71362e0939eSad 	if (atomic_cas_ptr(&ptm->ptm_waiters, NULL, head) == NULL) {
71462e0939eSad 		return;
71562e0939eSad 	}
71662e0939eSad 
71762e0939eSad 	/* Oops need to append.  Find the tail of the new queue. */
71862e0939eSad 	for (tail = head; tail->next != NULL; tail = tail->next) {
71962e0939eSad 		/* nothing */
72062e0939eSad 	}
72162e0939eSad 
72262e0939eSad 	/* Append atomically. */
72362e0939eSad 	for (o = ptm->ptm_waiters;; o = n) {
72462e0939eSad 		tail->next = o;
7252b4d5392Sad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
7262b4d5392Sad 		membar_producer();
7272b4d5392Sad #endif
72862e0939eSad 		n = atomic_cas_ptr(&ptm->ptm_waiters, o, head);
72962e0939eSad 		if (__predict_true(n == o)) {
73062e0939eSad 			break;
73162e0939eSad 		}
7322bcb8bf1Sad 	}
733f4fd6b79Sad }
734f4fd6b79Sad 
735989565f8Sad int
pthread_mutex_getprioceiling(const pthread_mutex_t * ptm,int * ceil)7367cf7644fSchristos pthread_mutex_getprioceiling(const pthread_mutex_t *ptm, int *ceil)
7377cf7644fSchristos {
73812ee584aSkamil 
73912ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex",
74012ee584aSkamil 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
74112ee584aSkamil 
7423b2c691cSskrll 	*ceil = ptm->ptm_ceiling;
7437cf7644fSchristos 	return 0;
7447cf7644fSchristos }
7457cf7644fSchristos 
7467cf7644fSchristos int
pthread_mutex_setprioceiling(pthread_mutex_t * ptm,int ceil,int * old_ceil)7477cf7644fSchristos pthread_mutex_setprioceiling(pthread_mutex_t *ptm, int ceil, int *old_ceil)
7487cf7644fSchristos {
7497cf7644fSchristos 	int error;
7507cf7644fSchristos 
75112ee584aSkamil 	pthread__error(EINVAL, "Invalid mutex",
75212ee584aSkamil 	    ptm->ptm_magic == _PT_MUTEX_MAGIC);
75312ee584aSkamil 
7547cf7644fSchristos 	error = pthread_mutex_lock(ptm);
7557cf7644fSchristos 	if (error == 0) {
7563b2c691cSskrll 		*old_ceil = ptm->ptm_ceiling;
7577cf7644fSchristos 		/*check range*/
7583b2c691cSskrll 		ptm->ptm_ceiling = ceil;
7597cf7644fSchristos 		pthread_mutex_unlock(ptm);
7607cf7644fSchristos 	}
7617cf7644fSchristos 	return error;
7627cf7644fSchristos }
7637cf7644fSchristos 
7647cf7644fSchristos int
_pthread_mutex_held_np(pthread_mutex_t * ptm)765a67e1e34Sad _pthread_mutex_held_np(pthread_mutex_t *ptm)
766989565f8Sad {
767989565f8Sad 
768a67e1e34Sad 	return MUTEX_OWNER(ptm->ptm_owner) == (uintptr_t)pthread__self();
769989565f8Sad }
770989565f8Sad 
771989565f8Sad pthread_t
_pthread_mutex_owner_np(pthread_mutex_t * ptm)772a67e1e34Sad _pthread_mutex_owner_np(pthread_mutex_t *ptm)
773989565f8Sad {
774989565f8Sad 
775a67e1e34Sad 	return (pthread_t)MUTEX_OWNER(ptm->ptm_owner);
776989565f8Sad }
777