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