]> git.saurik.com Git - apple/libpthread.git/blame - src/pthread_mutex.c
libpthread-330.250.2.tar.gz
[apple/libpthread.git] / src / pthread_mutex.c
CommitLineData
f1a1da6c
A
1/*
2 * Copyright (c) 2000-2003, 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
a0619f9c 5 *
f1a1da6c
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
a0619f9c 12 *
f1a1da6c
A
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
a0619f9c 20 *
f1a1da6c
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991
25 * All Rights Reserved
26 *
27 * Permission to use, copy, modify, and distribute this software and
28 * its documentation for any purpose and without fee is hereby granted,
29 * provided that the above copyright notice appears in all copies and
30 * that both the copyright notice and this permission notice appear in
31 * supporting documentation.
32 *
33 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
34 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
35 * FOR A PARTICULAR PURPOSE.
36 *
37 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
39 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
40 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
41 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42 *
43 */
44/*
45 * MkLinux
46 */
47
48/*
49 * POSIX Pthread Library
50 * -- Mutex variable support
51 */
52
964d3577 53#include "resolver.h"
f1a1da6c
A
54#include "internal.h"
55#include "kern/kern_trace.h"
a0619f9c 56
a0619f9c 57#ifndef BUILDING_VARIANT /* [ */
f1a1da6c
A
58
59#ifdef PLOCKSTAT
60#include "plockstat.h"
a0619f9c
A
61/* This function is never called and exists to provide never-fired dtrace
62 * probes so that user d scripts don't get errors.
63 */
64PTHREAD_NOEXPORT PTHREAD_USED
65void
66_plockstat_never_fired(void)
67{
68 PLOCKSTAT_MUTEX_SPIN(NULL);
69 PLOCKSTAT_MUTEX_SPUN(NULL, 0, 0);
70}
f1a1da6c
A
71#else /* !PLOCKSTAT */
72#define PLOCKSTAT_MUTEX_SPIN(x)
73#define PLOCKSTAT_MUTEX_SPUN(x, y, z)
74#define PLOCKSTAT_MUTEX_ERROR(x, y)
75#define PLOCKSTAT_MUTEX_BLOCK(x)
76#define PLOCKSTAT_MUTEX_BLOCKED(x, y)
77#define PLOCKSTAT_MUTEX_ACQUIRE(x, y, z)
78#define PLOCKSTAT_MUTEX_RELEASE(x, y)
79#endif /* PLOCKSTAT */
80
a0619f9c
A
81#define BLOCK_FAIL_PLOCKSTAT 0
82#define BLOCK_SUCCESS_PLOCKSTAT 1
3a6437e6 83
a0619f9c 84#define PTHREAD_MUTEX_INIT_UNUSED 1
f1a1da6c 85
214d78a2
A
86PTHREAD_NOEXPORT PTHREAD_WEAK
87int _pthread_mutex_lock_init_slow(_pthread_mutex *mutex, bool trylock);
88
89PTHREAD_NOEXPORT PTHREAD_WEAK // prevent inlining of return value into callers
90int _pthread_mutex_fairshare_lock_slow(_pthread_mutex *mutex, bool trylock);
91
a0619f9c 92PTHREAD_NOEXPORT PTHREAD_WEAK // prevent inlining of return value into callers
214d78a2 93int _pthread_mutex_firstfit_lock_slow(_pthread_mutex *mutex, bool trylock);
964d3577
A
94
95PTHREAD_NOEXPORT PTHREAD_WEAK // prevent inlining of return value into callers
214d78a2
A
96int _pthread_mutex_fairshare_unlock_slow(_pthread_mutex *mutex);
97
98PTHREAD_NOEXPORT PTHREAD_WEAK // prevent inlining of return value into callers
99int _pthread_mutex_firstfit_unlock_slow(_pthread_mutex *mutex);
964d3577
A
100
101PTHREAD_NOEXPORT PTHREAD_WEAK // prevent inlining of return value into callers
a0619f9c 102int _pthread_mutex_corruption_abort(_pthread_mutex *mutex);
964d3577 103
214d78a2
A
104extern int __pthread_mutex_default_opt_policy PTHREAD_NOEXPORT;
105
106
107int __pthread_mutex_default_opt_policy PTHREAD_NOEXPORT =
108 _PTHREAD_MTX_OPT_POLICY_DEFAULT;
76b7b9a2 109
214d78a2
A
110static inline bool
111_pthread_mutex_policy_validate(int policy)
112{
113 return (policy >= 0 && policy < _PTHREAD_MUTEX_POLICY_LAST);
114}
76b7b9a2 115
214d78a2
A
116static inline int
117_pthread_mutex_policy_to_opt(int policy)
118{
119 switch (policy) {
120 case PTHREAD_MUTEX_POLICY_FAIRSHARE_NP:
121 return _PTHREAD_MTX_OPT_POLICY_FAIRSHARE;
122 case PTHREAD_MUTEX_POLICY_FIRSTFIT_NP:
123 return _PTHREAD_MTX_OPT_POLICY_FIRSTFIT;
124 default:
125 __builtin_unreachable();
126 }
127}
76b7b9a2
A
128
129PTHREAD_NOEXPORT
130void
131_pthread_mutex_global_init(const char *envp[],
132 struct _pthread_registration_data *registration_data)
133{
214d78a2
A
134
135 int opt = _PTHREAD_MTX_OPT_POLICY_DEFAULT;
136 if (registration_data->mutex_default_policy) {
137 int policy = registration_data->mutex_default_policy;
138 if (_pthread_mutex_policy_validate(policy)) {
139 opt = _pthread_mutex_policy_to_opt(policy);
140 }
141 }
142
76b7b9a2 143 const char *envvar = _simple_getenv(envp, "PTHREAD_MUTEX_DEFAULT_POLICY");
214d78a2
A
144 if (envvar) {
145 int policy = envvar[0] - '0';
146 if (_pthread_mutex_policy_validate(policy)) {
147 opt = _pthread_mutex_policy_to_opt(policy);
148 }
149 }
150
151 if (opt != __pthread_mutex_default_opt_policy) {
152 __pthread_mutex_default_opt_policy = opt;
76b7b9a2
A
153 }
154}
155
156
964d3577 157
a0619f9c
A
158PTHREAD_ALWAYS_INLINE
159static inline int _pthread_mutex_init(_pthread_mutex *mutex,
160 const pthread_mutexattr_t *attr, uint32_t static_type);
f1a1da6c 161
a0619f9c
A
162typedef union mutex_seq {
163 uint32_t seq[2];
164 struct { uint32_t lgenval; uint32_t ugenval; };
165 struct { uint32_t mgen; uint32_t ugen; };
166 uint64_t seq_LU;
167 uint64_t _Atomic atomic_seq_LU;
168} mutex_seq;
f1a1da6c 169
a0619f9c
A
170_Static_assert(sizeof(mutex_seq) == 2 * sizeof(uint32_t),
171 "Incorrect mutex_seq size");
f1a1da6c
A
172
173#if !__LITTLE_ENDIAN__
174#error MUTEX_GETSEQ_ADDR assumes little endian layout of 2 32-bit sequence words
175#endif
176
964d3577
A
177PTHREAD_ALWAYS_INLINE
178static inline void
a0619f9c 179MUTEX_GETSEQ_ADDR(_pthread_mutex *mutex, mutex_seq **seqaddr)
f1a1da6c 180{
3a6437e6
A
181 // 64-bit aligned address inside m_seq array (&m_seq[0] for aligned mutex)
182 // We don't require more than byte alignment on OS X. rdar://22278325
a0619f9c 183 *seqaddr = (void *)(((uintptr_t)mutex->m_seq + 0x7ul) & ~0x7ul);
f1a1da6c
A
184}
185
964d3577
A
186PTHREAD_ALWAYS_INLINE
187static inline void
a0619f9c 188MUTEX_GETTID_ADDR(_pthread_mutex *mutex, uint64_t **tidaddr)
f1a1da6c 189{
3a6437e6
A
190 // 64-bit aligned address inside m_tid array (&m_tid[0] for aligned mutex)
191 // We don't require more than byte alignment on OS X. rdar://22278325
a0619f9c 192 *tidaddr = (void*)(((uintptr_t)mutex->m_tid + 0x7ul) & ~0x7ul);
f1a1da6c
A
193}
194
a0619f9c
A
195PTHREAD_ALWAYS_INLINE
196static inline void
197mutex_seq_load(mutex_seq *seqaddr, mutex_seq *oldseqval)
198{
199 oldseqval->seq_LU = seqaddr->seq_LU;
200}
f1a1da6c 201
a0619f9c
A
202#define mutex_seq_atomic_load(seqaddr, oldseqval, m) \
203 mutex_seq_atomic_load_##m(seqaddr, oldseqval)
204
214d78a2 205PTHREAD_ALWAYS_INLINE PTHREAD_USED
a0619f9c
A
206static inline bool
207mutex_seq_atomic_cmpxchgv_relaxed(mutex_seq *seqaddr, mutex_seq *oldseqval,
208 mutex_seq *newseqval)
f1a1da6c 209{
a0619f9c
A
210 return os_atomic_cmpxchgv(&seqaddr->atomic_seq_LU, oldseqval->seq_LU,
211 newseqval->seq_LU, &oldseqval->seq_LU, relaxed);
212}
213
214d78a2 214PTHREAD_ALWAYS_INLINE PTHREAD_USED
a0619f9c
A
215static inline bool
216mutex_seq_atomic_cmpxchgv_acquire(mutex_seq *seqaddr, mutex_seq *oldseqval,
217 mutex_seq *newseqval)
218{
219 return os_atomic_cmpxchgv(&seqaddr->atomic_seq_LU, oldseqval->seq_LU,
220 newseqval->seq_LU, &oldseqval->seq_LU, acquire);
221}
222
214d78a2 223PTHREAD_ALWAYS_INLINE PTHREAD_USED
a0619f9c
A
224static inline bool
225mutex_seq_atomic_cmpxchgv_release(mutex_seq *seqaddr, mutex_seq *oldseqval,
226 mutex_seq *newseqval)
227{
228 return os_atomic_cmpxchgv(&seqaddr->atomic_seq_LU, oldseqval->seq_LU,
229 newseqval->seq_LU, &oldseqval->seq_LU, release);
f1a1da6c 230}
a0619f9c
A
231
232#define mutex_seq_atomic_cmpxchgv(seqaddr, oldseqval, newseqval, m)\
233 mutex_seq_atomic_cmpxchgv_##m(seqaddr, oldseqval, newseqval)
f1a1da6c
A
234
235/*
236 * Initialize a mutex variable, possibly with additional attributes.
237 * Public interface - so don't trust the lock - initialize it first.
238 */
a0619f9c 239PTHREAD_NOEXPORT_VARIANT
f1a1da6c
A
240int
241pthread_mutex_init(pthread_mutex_t *omutex, const pthread_mutexattr_t *attr)
242{
243#if 0
244 /* conformance tests depend on not having this behavior */
245 /* The test for this behavior is optional */
964d3577 246 if (_pthread_mutex_check_signature(mutex))
f1a1da6c
A
247 return EBUSY;
248#endif
249 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
2546420a 250 _PTHREAD_LOCK_INIT(mutex->lock);
f1a1da6c
A
251 return (_pthread_mutex_init(mutex, attr, 0x7));
252}
253
a0619f9c 254PTHREAD_NOEXPORT_VARIANT
f1a1da6c
A
255int
256pthread_mutex_getprioceiling(const pthread_mutex_t *omutex, int *prioceiling)
257{
258 int res = EINVAL;
259 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
964d3577 260 if (_pthread_mutex_check_signature(mutex)) {
2546420a 261 _PTHREAD_LOCK(mutex->lock);
f1a1da6c
A
262 *prioceiling = mutex->prioceiling;
263 res = 0;
2546420a 264 _PTHREAD_UNLOCK(mutex->lock);
f1a1da6c
A
265 }
266 return res;
267}
268
a0619f9c 269PTHREAD_NOEXPORT_VARIANT
f1a1da6c 270int
a0619f9c
A
271pthread_mutex_setprioceiling(pthread_mutex_t *omutex, int prioceiling,
272 int *old_prioceiling)
f1a1da6c
A
273{
274 int res = EINVAL;
275 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
964d3577 276 if (_pthread_mutex_check_signature(mutex)) {
2546420a 277 _PTHREAD_LOCK(mutex->lock);
a0619f9c 278 if (prioceiling >= -999 && prioceiling <= 999) {
f1a1da6c 279 *old_prioceiling = mutex->prioceiling;
a0619f9c 280 mutex->prioceiling = (int16_t)prioceiling;
f1a1da6c
A
281 res = 0;
282 }
2546420a 283 _PTHREAD_UNLOCK(mutex->lock);
f1a1da6c
A
284 }
285 return res;
286}
287
a0619f9c 288
f1a1da6c 289int
a0619f9c
A
290pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr,
291 int *prioceiling)
f1a1da6c
A
292{
293 int res = EINVAL;
294 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
295 *prioceiling = attr->prioceiling;
296 res = 0;
297 }
298 return res;
299}
300
301int
302pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol)
303{
304 int res = EINVAL;
305 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
306 *protocol = attr->protocol;
307 res = 0;
308 }
309 return res;
310}
311
76b7b9a2
A
312int
313pthread_mutexattr_getpolicy_np(const pthread_mutexattr_t *attr, int *policy)
314{
315 int res = EINVAL;
316 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
214d78a2
A
317 switch (attr->opt) {
318 case _PTHREAD_MTX_OPT_POLICY_FAIRSHARE:
319 *policy = PTHREAD_MUTEX_POLICY_FAIRSHARE_NP;
320 res = 0;
321 break;
322 case _PTHREAD_MTX_OPT_POLICY_FIRSTFIT:
323 *policy = PTHREAD_MUTEX_POLICY_FIRSTFIT_NP;
324 res = 0;
325 break;
326 }
76b7b9a2
A
327 }
328 return res;
329}
330
f1a1da6c
A
331int
332pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
333{
334 int res = EINVAL;
335 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
336 *type = attr->type;
337 res = 0;
338 }
339 return res;
340}
341
342int
343pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared)
344{
345 int res = EINVAL;
346 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
347 *pshared = (int)attr->pshared;
348 res = 0;
349 }
350 return res;
351}
352
353int
354pthread_mutexattr_init(pthread_mutexattr_t *attr)
355{
356 attr->prioceiling = _PTHREAD_DEFAULT_PRIOCEILING;
357 attr->protocol = _PTHREAD_DEFAULT_PROTOCOL;
214d78a2 358 attr->opt = __pthread_mutex_default_opt_policy;
f1a1da6c
A
359 attr->type = PTHREAD_MUTEX_DEFAULT;
360 attr->sig = _PTHREAD_MUTEX_ATTR_SIG;
361 attr->pshared = _PTHREAD_DEFAULT_PSHARED;
362 return 0;
363}
364
365int
366pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling)
367{
368 int res = EINVAL;
369 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
a0619f9c 370 if (prioceiling >= -999 && prioceiling <= 999) {
f1a1da6c
A
371 attr->prioceiling = prioceiling;
372 res = 0;
373 }
374 }
375 return res;
376}
377
378int
379pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol)
380{
381 int res = EINVAL;
382 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
383 switch (protocol) {
384 case PTHREAD_PRIO_NONE:
385 case PTHREAD_PRIO_INHERIT:
386 case PTHREAD_PRIO_PROTECT:
387 attr->protocol = protocol;
388 res = 0;
389 break;
390 }
391 }
392 return res;
393}
394
395int
396pthread_mutexattr_setpolicy_np(pthread_mutexattr_t *attr, int policy)
397{
398 int res = EINVAL;
399 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
214d78a2
A
400 // <rdar://problem/35844519> the first-fit implementation was broken
401 // pre-Liberty so this mapping exists to ensure that the old first-fit
402 // define (2) is no longer valid when used on older systems.
f1a1da6c 403 switch (policy) {
214d78a2
A
404 case PTHREAD_MUTEX_POLICY_FAIRSHARE_NP:
405 attr->opt = _PTHREAD_MTX_OPT_POLICY_FAIRSHARE;
406 res = 0;
407 break;
408 case PTHREAD_MUTEX_POLICY_FIRSTFIT_NP:
409 attr->opt = _PTHREAD_MTX_OPT_POLICY_FIRSTFIT;
410 res = 0;
411 break;
f1a1da6c
A
412 }
413 }
414 return res;
415}
416
417int
418pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
419{
420 int res = EINVAL;
421 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
422 switch (type) {
423 case PTHREAD_MUTEX_NORMAL:
424 case PTHREAD_MUTEX_ERRORCHECK:
425 case PTHREAD_MUTEX_RECURSIVE:
426 //case PTHREAD_MUTEX_DEFAULT:
427 attr->type = type;
428 res = 0;
429 break;
430 }
431 }
432 return res;
433}
434
f1a1da6c
A
435int
436pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)
437{
438 int res = EINVAL;
439#if __DARWIN_UNIX03
440 if (__unix_conforming == 0) {
441 __unix_conforming = 1;
442 }
443#endif /* __DARWIN_UNIX03 */
444
445 if (attr->sig == _PTHREAD_MUTEX_ATTR_SIG) {
446#if __DARWIN_UNIX03
a0619f9c
A
447 if (( pshared == PTHREAD_PROCESS_PRIVATE) ||
448 (pshared == PTHREAD_PROCESS_SHARED))
f1a1da6c
A
449#else /* __DARWIN_UNIX03 */
450 if ( pshared == PTHREAD_PROCESS_PRIVATE)
451#endif /* __DARWIN_UNIX03 */
452 {
a0619f9c 453 attr->pshared = pshared;
f1a1da6c
A
454 res = 0;
455 }
456 }
457 return res;
458}
459
a0619f9c 460PTHREAD_NOEXPORT PTHREAD_NOINLINE PTHREAD_NORETURN
964d3577
A
461int
462_pthread_mutex_corruption_abort(_pthread_mutex *mutex)
463{
a0619f9c
A
464 PTHREAD_ABORT("pthread_mutex corruption: mutex owner changed in the "
465 "middle of lock/unlock");
964d3577
A
466}
467
a0619f9c 468
214d78a2
A
469PTHREAD_NOINLINE
470static int
471_pthread_mutex_check_init_slow(_pthread_mutex *mutex)
472{
473 int res = EINVAL;
474
475 if (_pthread_mutex_check_signature_init(mutex)) {
476 _PTHREAD_LOCK(mutex->lock);
477 if (_pthread_mutex_check_signature_init(mutex)) {
478 // initialize a statically initialized mutex to provide
479 // compatibility for misbehaving applications.
480 // (unlock should not be the first operation on a mutex)
481 res = _pthread_mutex_init(mutex, NULL, (mutex->sig & 0xf));
482 } else if (_pthread_mutex_check_signature(mutex)) {
483 res = 0;
484 }
485 _PTHREAD_UNLOCK(mutex->lock);
486 } else if (_pthread_mutex_check_signature(mutex)) {
487 res = 0;
488 }
489 if (res != 0) {
490 PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, res);
491 }
492 return res;
493}
494
495PTHREAD_ALWAYS_INLINE
496static inline int
497_pthread_mutex_check_init(_pthread_mutex *mutex)
498{
499 int res = 0;
500 if (!_pthread_mutex_check_signature(mutex)) {
501 return _pthread_mutex_check_init_slow(mutex);
502 }
503 return res;
504}
505
506PTHREAD_ALWAYS_INLINE
507static inline bool
508_pthread_mutex_is_fairshare(_pthread_mutex *mutex)
509{
510 return (mutex->mtxopts.options.policy == _PTHREAD_MTX_OPT_POLICY_FAIRSHARE);
511}
512
513PTHREAD_ALWAYS_INLINE
514static inline bool
515_pthread_mutex_is_firstfit(_pthread_mutex *mutex)
516{
517 return (mutex->mtxopts.options.policy == _PTHREAD_MTX_OPT_POLICY_FIRSTFIT);
518}
519
520PTHREAD_ALWAYS_INLINE
521static inline bool
522_pthread_mutex_is_recursive(_pthread_mutex *mutex)
523{
524 return (mutex->mtxopts.options.type == PTHREAD_MUTEX_RECURSIVE);
525}
526
527PTHREAD_ALWAYS_INLINE
528static int
529_pthread_mutex_lock_handle_options(_pthread_mutex *mutex, bool trylock,
530 uint64_t *tidaddr)
531{
532 if (mutex->mtxopts.options.type == PTHREAD_MUTEX_NORMAL) {
533 // NORMAL does not do EDEADLK checking
534 return 0;
535 }
536
537 uint64_t selfid = _pthread_selfid_direct();
538 if (os_atomic_load(tidaddr, relaxed) == selfid) {
539 if (_pthread_mutex_is_recursive(mutex)) {
540 if (mutex->mtxopts.options.lock_count < USHRT_MAX) {
541 mutex->mtxopts.options.lock_count += 1;
542 return mutex->mtxopts.options.lock_count;
543 } else {
544 return -EAGAIN;
545 }
546 } else if (trylock) { /* PTHREAD_MUTEX_ERRORCHECK */
547 // <rdar://problem/16261552> as per OpenGroup, trylock cannot
548 // return EDEADLK on a deadlock, it should return EBUSY.
549 return -EBUSY;
550 } else { /* PTHREAD_MUTEX_ERRORCHECK */
551 return -EDEADLK;
552 }
553 }
554
555 // Not recursive, or recursive but first lock.
556 return 0;
557}
558
559PTHREAD_ALWAYS_INLINE
560static int
561_pthread_mutex_unlock_handle_options(_pthread_mutex *mutex, uint64_t *tidaddr)
562{
563 if (mutex->mtxopts.options.type == PTHREAD_MUTEX_NORMAL) {
564 // NORMAL does not do EDEADLK checking
565 return 0;
566 }
567
568 uint64_t selfid = _pthread_selfid_direct();
569 if (os_atomic_load(tidaddr, relaxed) != selfid) {
570 return -EPERM;
571 } else if (_pthread_mutex_is_recursive(mutex) &&
572 --mutex->mtxopts.options.lock_count) {
573 return 1;
574 }
575 return 0;
576}
577
f1a1da6c
A
578/*
579 * Sequence numbers and TID:
580 *
581 * In steady (and uncontended) state, an unlocked mutex will
582 * look like A=[L4 U4 TID0]. When it is being locked, it transitions
583 * to B=[L5+KE U4 TID0] and then C=[L5+KE U4 TID940]. For an uncontended mutex,
584 * the unlock path will then transition to D=[L5 U4 TID0] and then finally
585 * E=[L5 U5 TID0].
586 *
a0619f9c
A
587 * If a contender comes in after B, the mutex will instead transition to
588 * E=[L6+KE U4 TID0] and then F=[L6+KE U4 TID940]. If a contender comes in after
589 * C, it will transition to F=[L6+KE U4 TID940] directly. In both cases, the
590 * contender will enter the kernel with either mutexwait(U4, TID0) or
591 * mutexwait(U4, TID940). The first owner will unlock the mutex by first
592 * updating the owner to G=[L6+KE U4 TID-1] and then doing the actual unlock to
593 * H=[L6+KE U5 TID=-1] before entering the kernel with mutexdrop(U5, -1) to
594 * signal the next waiter (potentially as a prepost). When the waiter comes out
595 * of the kernel, it will update the owner to I=[L6+KE U5 TID941]. An unlock at
596 * this point is simply J=[L6 U5 TID0] and then K=[L6 U6 TID0].
f1a1da6c 597 *
a0619f9c
A
598 * At various points along these timelines, since the sequence words and TID are
599 * written independently, a thread may get preempted and another thread might
600 * see inconsistent data. In the worst case, another thread may see the TID in
601 * the SWITCHING (-1) state or unlocked (0) state for longer because the owning
602 * thread was preempted.
964d3577 603 */
f1a1da6c
A
604
605/*
a0619f9c 606 * Drop the mutex unlock references from cond_wait or mutex_unlock.
f1a1da6c 607 */
964d3577
A
608PTHREAD_ALWAYS_INLINE
609static inline int
214d78a2
A
610_pthread_mutex_fairshare_unlock_updatebits(_pthread_mutex *mutex,
611 uint32_t *flagsp, uint32_t **pmtxp, uint32_t *mgenp, uint32_t *ugenp)
f1a1da6c 612{
a0619f9c 613 uint32_t flags = mutex->mtxopts.value;
f1a1da6c
A
614 flags &= ~_PTHREAD_MTX_OPT_NOTIFY; // no notification by default
615
a0619f9c
A
616 mutex_seq *seqaddr;
617 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
618
619 mutex_seq oldseq, newseq;
620 mutex_seq_load(seqaddr, &oldseq);
621
622 uint64_t *tidaddr;
623 MUTEX_GETTID_ADDR(mutex, &tidaddr);
624 uint64_t oldtid, newtid;
625
214d78a2
A
626 int res = _pthread_mutex_unlock_handle_options(mutex, tidaddr);
627 if (res > 0) {
628 // Valid recursive unlock
629 if (flagsp) {
630 *flagsp = flags;
f1a1da6c 631 }
214d78a2
A
632 PLOCKSTAT_MUTEX_RELEASE((pthread_mutex_t *)mutex, 1);
633 return 0;
634 } else if (res < 0) {
635 PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, -res);
636 return -res;
f1a1da6c
A
637 }
638
214d78a2 639 bool clearnotify, spurious;
f1a1da6c 640 do {
a0619f9c
A
641 newseq = oldseq;
642 oldtid = os_atomic_load(tidaddr, relaxed);
f1a1da6c 643
f1a1da6c
A
644 clearnotify = false;
645 spurious = false;
646
a0619f9c
A
647 // pending waiters
648 int numwaiters = diff_genseq(oldseq.lgenval, oldseq.ugenval);
f1a1da6c 649 if (numwaiters == 0) {
a0619f9c 650 // spurious unlock (unlock of unlocked lock)
f1a1da6c
A
651 spurious = true;
652 } else {
a0619f9c 653 newseq.ugenval += PTHRW_INC;
f1a1da6c 654
a0619f9c
A
655 if ((oldseq.lgenval & PTHRW_COUNT_MASK) ==
656 (newseq.ugenval & PTHRW_COUNT_MASK)) {
657 // our unlock sequence matches to lock sequence, so if the
658 // CAS is successful, the mutex is unlocked
f1a1da6c
A
659
660 /* do not reset Ibit, just K&E */
a0619f9c 661 newseq.lgenval &= ~(PTH_RWL_KBIT | PTH_RWL_EBIT);
f1a1da6c
A
662 clearnotify = true;
663 newtid = 0; // clear owner
664 } else {
214d78a2 665 newtid = PTHREAD_MTX_TID_SWITCHING;
f1a1da6c
A
666 // need to signal others waiting for mutex
667 flags |= _PTHREAD_MTX_OPT_NOTIFY;
668 }
a0619f9c 669
f1a1da6c 670 if (newtid != oldtid) {
a0619f9c
A
671 // We're giving up the mutex one way or the other, so go ahead
672 // and update the owner to 0 so that once the CAS below
673 // succeeds, there is no stale ownership information. If the
674 // CAS of the seqaddr fails, we may loop, but it's still valid
675 // for the owner to be SWITCHING/0
964d3577 676 if (!os_atomic_cmpxchg(tidaddr, oldtid, newtid, relaxed)) {
f1a1da6c 677 // we own this mutex, nobody should be updating it except us
964d3577 678 return _pthread_mutex_corruption_abort(mutex);
f1a1da6c
A
679 }
680 }
681 }
682
683 if (clearnotify || spurious) {
684 flags &= ~_PTHREAD_MTX_OPT_NOTIFY;
f1a1da6c 685 }
a0619f9c 686 } while (!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq, release));
f1a1da6c 687
76b7b9a2
A
688 PTHREAD_TRACE(psynch_mutex_unlock_updatebits, mutex, oldseq.lgenval,
689 newseq.lgenval, oldtid);
690
f1a1da6c 691 if (mgenp != NULL) {
a0619f9c 692 *mgenp = newseq.lgenval;
f1a1da6c
A
693 }
694 if (ugenp != NULL) {
a0619f9c 695 *ugenp = newseq.ugenval;
f1a1da6c
A
696 }
697 if (pmtxp != NULL) {
698 *pmtxp = (uint32_t *)mutex;
699 }
700 if (flagsp != NULL) {
701 *flagsp = flags;
702 }
703
704 return 0;
705}
706
964d3577
A
707PTHREAD_ALWAYS_INLINE
708static inline int
214d78a2 709_pthread_mutex_fairshare_lock_updatebits(_pthread_mutex *mutex, uint64_t selfid)
f1a1da6c 710{
214d78a2 711 bool firstfit = _pthread_mutex_is_firstfit(mutex);
76b7b9a2 712 bool gotlock = true;
f1a1da6c 713
a0619f9c 714 mutex_seq *seqaddr;
f1a1da6c 715 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
a0619f9c
A
716
717 mutex_seq oldseq, newseq;
718 mutex_seq_load(seqaddr, &oldseq);
719
720 uint64_t *tidaddr;
f1a1da6c
A
721 MUTEX_GETTID_ADDR(mutex, &tidaddr);
722
723 do {
a0619f9c 724 newseq = oldseq;
f1a1da6c 725
f1a1da6c 726 if (firstfit) {
76b7b9a2
A
727 // firstfit locks can have the lock stolen out from under a locker
728 // between the unlock from the kernel and this lock path. When this
729 // happens, we still want to set the K bit before leaving the loop
730 // (or notice if the lock unlocks while we try to update).
731 gotlock = !is_rwl_ebit_set(oldseq.lgenval);
732 } else if ((oldseq.lgenval & (PTH_RWL_KBIT | PTH_RWL_EBIT)) ==
733 (PTH_RWL_KBIT | PTH_RWL_EBIT)) {
734 // bit are already set, just update the owner tidaddr
f1a1da6c
A
735 break;
736 }
737
a0619f9c 738 newseq.lgenval |= PTH_RWL_KBIT | PTH_RWL_EBIT;
76b7b9a2 739 } while (!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq,
214d78a2 740 acquire));
f1a1da6c 741
76b7b9a2 742 if (gotlock) {
214d78a2 743 os_atomic_store(tidaddr, selfid, relaxed);
f1a1da6c
A
744 }
745
76b7b9a2 746 PTHREAD_TRACE(psynch_mutex_lock_updatebits, mutex, oldseq.lgenval,
214d78a2 747 newseq.lgenval, 0);
76b7b9a2
A
748
749 // failing to take the lock in firstfit returns 1 to force the caller
750 // to wait in the kernel
751 return gotlock ? 0 : 1;
f1a1da6c
A
752}
753
964d3577
A
754PTHREAD_NOINLINE
755static int
214d78a2 756_pthread_mutex_fairshare_lock_wait(_pthread_mutex *mutex, mutex_seq newseq,
a0619f9c 757 uint64_t oldtid)
964d3577 758{
a0619f9c 759 uint64_t *tidaddr;
964d3577
A
760 MUTEX_GETTID_ADDR(mutex, &tidaddr);
761 uint64_t selfid = _pthread_selfid_direct();
762
214d78a2 763 PLOCKSTAT_MUTEX_BLOCK((pthread_mutex_t *)mutex);
964d3577
A
764 do {
765 uint32_t updateval;
766 do {
214d78a2 767 updateval = __psynch_mutexwait(mutex, newseq.lgenval,
a0619f9c
A
768 newseq.ugenval, oldtid, mutex->mtxopts.value);
769 oldtid = os_atomic_load(tidaddr, relaxed);
964d3577
A
770 } while (updateval == (uint32_t)-1);
771
772 // returns 0 on succesful update; in firstfit it may fail with 1
214d78a2
A
773 } while (_pthread_mutex_fairshare_lock_updatebits(mutex, selfid) == 1);
774 PLOCKSTAT_MUTEX_BLOCKED((pthread_mutex_t *)mutex, BLOCK_SUCCESS_PLOCKSTAT);
964d3577
A
775
776 return 0;
777}
778
a0619f9c 779PTHREAD_NOEXPORT PTHREAD_NOINLINE
964d3577 780int
214d78a2 781_pthread_mutex_fairshare_lock_slow(_pthread_mutex *omutex, bool trylock)
f1a1da6c 782{
a0619f9c 783 int res, recursive = 0;
f1a1da6c
A
784 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
785
a0619f9c
A
786 mutex_seq *seqaddr;
787 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
788
789 mutex_seq oldseq, newseq;
790 mutex_seq_load(seqaddr, &oldseq);
791
792 uint64_t *tidaddr;
f1a1da6c 793 MUTEX_GETTID_ADDR(mutex, &tidaddr);
a0619f9c 794 uint64_t oldtid, selfid = _pthread_selfid_direct();
f1a1da6c 795
214d78a2
A
796 res = _pthread_mutex_lock_handle_options(mutex, trylock, tidaddr);
797 if (res > 0) {
798 recursive = 1;
799 res = 0;
800 goto out;
801 } else if (res < 0) {
802 res = -res;
803 goto out;
f1a1da6c
A
804 }
805
a0619f9c 806 bool gotlock;
f1a1da6c 807 do {
a0619f9c
A
808 newseq = oldseq;
809 oldtid = os_atomic_load(tidaddr, relaxed);
f1a1da6c 810
a0619f9c 811 gotlock = ((oldseq.lgenval & PTH_RWL_EBIT) == 0);
f1a1da6c
A
812
813 if (trylock && !gotlock) {
814 // A trylock on a held lock will fail immediately. But since
815 // we did not load the sequence words atomically, perform a
816 // no-op CAS64 to ensure that nobody has unlocked concurrently.
817 } else {
818 // Increment the lock sequence number and force the lock into E+K
819 // mode, whether "gotlock" is true or not.
a0619f9c
A
820 newseq.lgenval += PTHRW_INC;
821 newseq.lgenval |= PTH_RWL_EBIT | PTH_RWL_KBIT;
f1a1da6c 822 }
a0619f9c 823 } while (!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq, acquire));
f1a1da6c 824
76b7b9a2
A
825 PTHREAD_TRACE(psynch_mutex_lock_updatebits, omutex, oldseq.lgenval,
826 newseq.lgenval, 0);
827
f1a1da6c 828 if (gotlock) {
964d3577 829 os_atomic_store(tidaddr, selfid, relaxed);
f1a1da6c 830 res = 0;
76b7b9a2
A
831 PTHREAD_TRACE(psynch_mutex_ulock, omutex, newseq.lgenval,
832 newseq.ugenval, selfid);
f1a1da6c
A
833 } else if (trylock) {
834 res = EBUSY;
76b7b9a2
A
835 PTHREAD_TRACE(psynch_mutex_utrylock_failed, omutex, newseq.lgenval,
836 newseq.ugenval, oldtid);
f1a1da6c 837 } else {
76b7b9a2
A
838 PTHREAD_TRACE(psynch_mutex_ulock | DBG_FUNC_START, omutex,
839 newseq.lgenval, newseq.ugenval, oldtid);
214d78a2 840 res = _pthread_mutex_fairshare_lock_wait(mutex, newseq, oldtid);
76b7b9a2
A
841 PTHREAD_TRACE(psynch_mutex_ulock | DBG_FUNC_END, omutex,
842 newseq.lgenval, newseq.ugenval, oldtid);
f1a1da6c
A
843 }
844
214d78a2 845 if (res == 0 && _pthread_mutex_is_recursive(mutex)) {
f1a1da6c
A
846 mutex->mtxopts.options.lock_count = 1;
847 }
848
a0619f9c
A
849out:
850#if PLOCKSTAT
851 if (res == 0) {
214d78a2 852 PLOCKSTAT_MUTEX_ACQUIRE((pthread_mutex_t *)mutex, recursive, 0);
a0619f9c 853 } else {
214d78a2 854 PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, res);
a0619f9c
A
855 }
856#endif
f1a1da6c
A
857
858 return res;
859}
860
214d78a2 861PTHREAD_NOINLINE
964d3577 862static inline int
214d78a2 863_pthread_mutex_fairshare_lock(_pthread_mutex *mutex, bool trylock)
964d3577 864{
76b7b9a2 865#if ENABLE_USERSPACE_TRACE
214d78a2 866 return _pthread_mutex_fairshare_lock_slow(mutex, trylock);
76b7b9a2
A
867#elif PLOCKSTAT
868 if (PLOCKSTAT_MUTEX_ACQUIRE_ENABLED() || PLOCKSTAT_MUTEX_ERROR_ENABLED()) {
214d78a2 869 return _pthread_mutex_fairshare_lock_slow(mutex, trylock);
964d3577
A
870 }
871#endif
76b7b9a2 872
a0619f9c 873 uint64_t *tidaddr;
964d3577
A
874 MUTEX_GETTID_ADDR(mutex, &tidaddr);
875 uint64_t selfid = _pthread_selfid_direct();
876
a0619f9c 877 mutex_seq *seqaddr;
964d3577
A
878 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
879
a0619f9c
A
880 mutex_seq oldseq, newseq;
881 mutex_seq_load(seqaddr, &oldseq);
882
883 if (os_unlikely(oldseq.lgenval & PTH_RWL_EBIT)) {
214d78a2 884 return _pthread_mutex_fairshare_lock_slow(mutex, trylock);
a0619f9c 885 }
964d3577 886
a0619f9c 887 bool gotlock;
964d3577 888 do {
a0619f9c 889 newseq = oldseq;
964d3577 890
a0619f9c 891 gotlock = ((oldseq.lgenval & PTH_RWL_EBIT) == 0);
964d3577
A
892
893 if (trylock && !gotlock) {
894 // A trylock on a held lock will fail immediately. But since
895 // we did not load the sequence words atomically, perform a
896 // no-op CAS64 to ensure that nobody has unlocked concurrently.
a0619f9c 897 } else if (os_likely(gotlock)) {
964d3577
A
898 // Increment the lock sequence number and force the lock into E+K
899 // mode, whether "gotlock" is true or not.
a0619f9c
A
900 newseq.lgenval += PTHRW_INC;
901 newseq.lgenval |= PTH_RWL_EBIT | PTH_RWL_KBIT;
902 } else {
214d78a2 903 return _pthread_mutex_fairshare_lock_slow(mutex, trylock);
964d3577 904 }
a0619f9c
A
905 } while (os_unlikely(!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq,
906 acquire)));
964d3577 907
a0619f9c 908 if (os_likely(gotlock)) {
964d3577
A
909 os_atomic_store(tidaddr, selfid, relaxed);
910 return 0;
911 } else if (trylock) {
912 return EBUSY;
913 } else {
a0619f9c 914 __builtin_trap();
964d3577
A
915 }
916}
917
964d3577
A
918PTHREAD_NOINLINE
919static int
214d78a2 920_pthread_mutex_fairshare_unlock_drop(_pthread_mutex *mutex, mutex_seq newseq,
a0619f9c 921 uint32_t flags)
964d3577
A
922{
923 int res;
964d3577 924 uint32_t updateval;
a0619f9c
A
925
926 uint64_t *tidaddr;
964d3577
A
927 MUTEX_GETTID_ADDR(mutex, &tidaddr);
928
214d78a2 929 PTHREAD_TRACE(psynch_mutex_uunlock | DBG_FUNC_START, mutex, newseq.lgenval,
76b7b9a2
A
930 newseq.ugenval, os_atomic_load(tidaddr, relaxed));
931
214d78a2 932 updateval = __psynch_mutexdrop(mutex, newseq.lgenval, newseq.ugenval,
a0619f9c 933 os_atomic_load(tidaddr, relaxed), flags);
964d3577 934
214d78a2 935 PTHREAD_TRACE(psynch_mutex_uunlock | DBG_FUNC_END, mutex, updateval, 0, 0);
76b7b9a2 936
964d3577
A
937 if (updateval == (uint32_t)-1) {
938 res = errno;
939
940 if (res == EINTR) {
941 res = 0;
942 }
943 if (res != 0) {
a0619f9c 944 PTHREAD_ABORT("__psynch_mutexdrop failed with error %d", res);
964d3577
A
945 }
946 return res;
964d3577
A
947 }
948
949 return 0;
950}
951
a0619f9c 952PTHREAD_NOEXPORT PTHREAD_NOINLINE
f1a1da6c 953int
214d78a2 954_pthread_mutex_fairshare_unlock_slow(_pthread_mutex *mutex)
f1a1da6c
A
955{
956 int res;
a0619f9c
A
957 mutex_seq newseq;
958 uint32_t flags;
f1a1da6c 959
214d78a2
A
960 res = _pthread_mutex_fairshare_unlock_updatebits(mutex, &flags, NULL,
961 &newseq.lgenval, &newseq.ugenval);
a0619f9c 962 if (res != 0) return res;
f1a1da6c
A
963
964 if ((flags & _PTHREAD_MTX_OPT_NOTIFY) != 0) {
214d78a2 965 return _pthread_mutex_fairshare_unlock_drop(mutex, newseq, flags);
964d3577 966 } else {
a0619f9c 967 uint64_t *tidaddr;
f1a1da6c 968 MUTEX_GETTID_ADDR(mutex, &tidaddr);
214d78a2 969 PTHREAD_TRACE(psynch_mutex_uunlock, mutex, newseq.lgenval,
76b7b9a2 970 newseq.ugenval, os_atomic_load(tidaddr, relaxed));
964d3577 971 }
f1a1da6c 972
964d3577
A
973 return 0;
974}
f1a1da6c 975
214d78a2
A
976PTHREAD_NOINLINE
977static int
978_pthread_mutex_fairshare_unlock(_pthread_mutex *mutex)
964d3577 979{
76b7b9a2 980#if ENABLE_USERSPACE_TRACE
214d78a2 981 return _pthread_mutex_fairshare_unlock_slow(mutex);
76b7b9a2
A
982#elif PLOCKSTAT
983 if (PLOCKSTAT_MUTEX_RELEASE_ENABLED() || PLOCKSTAT_MUTEX_ERROR_ENABLED()) {
214d78a2 984 return _pthread_mutex_fairshare_unlock_slow(mutex);
964d3577
A
985 }
986#endif
964d3577 987
a0619f9c 988 uint64_t *tidaddr;
964d3577
A
989 MUTEX_GETTID_ADDR(mutex, &tidaddr);
990
a0619f9c 991 mutex_seq *seqaddr;
964d3577
A
992 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
993
a0619f9c
A
994 mutex_seq oldseq, newseq;
995 mutex_seq_load(seqaddr, &oldseq);
964d3577 996
a0619f9c
A
997 int numwaiters = diff_genseq(oldseq.lgenval, oldseq.ugenval);
998 if (os_unlikely(numwaiters == 0)) {
999 // spurious unlock (unlock of unlocked lock)
1000 return 0;
1001 }
964d3577 1002
a0619f9c
A
1003 // We're giving up the mutex one way or the other, so go ahead and
1004 // update the owner to 0 so that once the CAS below succeeds, there
1005 // is no stale ownership information. If the CAS of the seqaddr
1006 // fails, we may loop, but it's still valid for the owner to be
1007 // SWITCHING/0
1008 os_atomic_store(tidaddr, 0, relaxed);
964d3577 1009
a0619f9c
A
1010 do {
1011 newseq = oldseq;
1012 newseq.ugenval += PTHRW_INC;
964d3577 1013
a0619f9c
A
1014 if (os_likely((oldseq.lgenval & PTHRW_COUNT_MASK) ==
1015 (newseq.ugenval & PTHRW_COUNT_MASK))) {
214d78a2
A
1016 // if we succeed in performing the CAS we can be sure of a fast
1017 // path (only needing the CAS) unlock, if:
1018 // a. our lock and unlock sequence are equal
1019 // b. we don't need to clear an unlock prepost from the kernel
964d3577 1020
a0619f9c
A
1021 // do not reset Ibit, just K&E
1022 newseq.lgenval &= ~(PTH_RWL_KBIT | PTH_RWL_EBIT);
1023 } else {
214d78a2 1024 return _pthread_mutex_fairshare_unlock_slow(mutex);
f1a1da6c 1025 }
a0619f9c
A
1026 } while (os_unlikely(!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq,
1027 release)));
f1a1da6c
A
1028
1029 return 0;
1030}
1031
214d78a2
A
1032#pragma mark firstfit
1033
1034PTHREAD_ALWAYS_INLINE
1035static inline int
1036_pthread_mutex_firstfit_unlock_updatebits(_pthread_mutex *mutex,
1037 uint32_t *flagsp, uint32_t **mutexp, uint32_t *lvalp, uint32_t *uvalp)
1038{
1039 uint32_t flags = mutex->mtxopts.value & ~_PTHREAD_MTX_OPT_NOTIFY;
1040 bool kernel_wake;
1041
1042 mutex_seq *seqaddr;
1043 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
1044
1045 mutex_seq oldseq, newseq;
1046 mutex_seq_load(seqaddr, &oldseq);
1047
1048 uint64_t *tidaddr;
1049 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1050 uint64_t oldtid;
1051
1052 int res = _pthread_mutex_unlock_handle_options(mutex, tidaddr);
1053 if (res > 0) {
1054 // Valid recursive unlock
1055 if (flagsp) {
1056 *flagsp = flags;
1057 }
1058 PLOCKSTAT_MUTEX_RELEASE((pthread_mutex_t *)mutex, 1);
1059 return 0;
1060 } else if (res < 0) {
1061 PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, -res);
1062 return -res;
1063 }
1064
1065 do {
1066 newseq = oldseq;
1067 oldtid = os_atomic_load(tidaddr, relaxed);
1068 // More than one kernel waiter means we need to do a wake.
1069 kernel_wake = diff_genseq(oldseq.lgenval, oldseq.ugenval) > 0;
1070 newseq.lgenval &= ~PTH_RWL_EBIT;
1071
1072 if (kernel_wake) {
1073 // Going to the kernel post-unlock removes a single waiter unlock
1074 // from the mutex counts.
1075 newseq.ugenval += PTHRW_INC;
1076 }
1077
1078 if (oldtid != 0) {
1079 if (!os_atomic_cmpxchg(tidaddr, oldtid, 0, relaxed)) {
1080 return _pthread_mutex_corruption_abort(mutex);
1081 }
1082 }
1083 } while (!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq, release));
1084
1085 PTHREAD_TRACE(psynch_ffmutex_unlock_updatebits, mutex, oldseq.lgenval,
1086 newseq.lgenval, newseq.ugenval);
1087
1088 if (kernel_wake) {
1089 // We choose to return this out via flags because the condition
1090 // variable also uses this to determine whether to do a kernel wake
1091 // when beginning a cvwait.
1092 flags |= _PTHREAD_MTX_OPT_NOTIFY;
1093 }
1094 if (lvalp) {
1095 *lvalp = newseq.lgenval;
1096 }
1097 if (uvalp) {
1098 *uvalp = newseq.ugenval;
1099 }
1100 if (mutexp) {
1101 *mutexp = (uint32_t *)mutex;
1102 }
1103 if (flagsp) {
1104 *flagsp = flags;
1105 }
1106 return 0;
1107}
1108
1109PTHREAD_NOEXPORT PTHREAD_NOINLINE
1110static int
1111_pthread_mutex_firstfit_wake(_pthread_mutex *mutex, mutex_seq newseq,
1112 uint32_t flags)
1113{
1114 PTHREAD_TRACE(psynch_ffmutex_wake, mutex, newseq.lgenval, newseq.ugenval,
1115 0);
1116 int res = __psynch_mutexdrop(mutex, newseq.lgenval, newseq.ugenval, 0,
1117 flags);
1118
1119 if (res == -1) {
1120 res = errno;
1121 if (res == EINTR) {
1122 res = 0;
1123 }
1124 if (res != 0) {
1125 PTHREAD_ABORT("__psynch_mutexdrop failed with error %d", res);
1126 }
1127 return res;
1128 }
1129 return 0;
1130}
1131
1132PTHREAD_NOEXPORT PTHREAD_NOINLINE
1133int
1134_pthread_mutex_firstfit_unlock_slow(_pthread_mutex *mutex)
1135{
1136 mutex_seq newseq;
1137 uint32_t flags;
1138 int res;
1139
1140 res = _pthread_mutex_firstfit_unlock_updatebits(mutex, &flags, NULL,
1141 &newseq.lgenval, &newseq.ugenval);
1142 if (res != 0) return res;
1143
1144 if (flags & _PTHREAD_MTX_OPT_NOTIFY) {
1145 return _pthread_mutex_firstfit_wake(mutex, newseq, flags);
1146 }
1147 return 0;
1148}
1149
1150PTHREAD_ALWAYS_INLINE
1151static bool
1152_pthread_mutex_firstfit_lock_updatebits(_pthread_mutex *mutex, uint64_t selfid,
1153 mutex_seq *newseqp)
1154{
1155 bool gotlock;
1156
1157 mutex_seq *seqaddr;
1158 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
1159
1160 mutex_seq oldseq, newseq;
1161 mutex_seq_load(seqaddr, &oldseq);
1162
1163 uint64_t *tidaddr;
1164 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1165
1166 PTHREAD_TRACE(psynch_ffmutex_lock_updatebits | DBG_FUNC_START, mutex,
1167 oldseq.lgenval, oldseq.ugenval, 0);
1168
1169 do {
1170 newseq = oldseq;
1171 gotlock = is_rwl_ebit_clear(oldseq.lgenval);
1172
1173 if (gotlock) {
1174 // If we see the E-bit cleared, we should just attempt to take it.
1175 newseq.lgenval |= PTH_RWL_EBIT;
1176 } else {
1177 // If we failed to get the lock then we need to put ourselves back
1178 // in the queue of waiters. The previous unlocker that woke us out
1179 // of the kernel consumed the S-count for our previous wake. So
1180 // take another ticket on L and go back in the kernel to sleep.
1181 newseq.lgenval += PTHRW_INC;
1182 }
1183 } while (!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq, acquire));
1184
1185 if (gotlock) {
1186 os_atomic_store(tidaddr, selfid, relaxed);
1187 }
1188
1189 PTHREAD_TRACE(psynch_ffmutex_lock_updatebits | DBG_FUNC_END, mutex,
1190 newseq.lgenval, newseq.ugenval, 0);
1191
1192 if (newseqp) {
1193 *newseqp = newseq;
1194 }
1195 return gotlock;
1196}
1197
1198PTHREAD_NOINLINE
1199static int
1200_pthread_mutex_firstfit_lock_wait(_pthread_mutex *mutex, mutex_seq newseq,
1201 uint64_t oldtid)
1202{
1203 uint64_t *tidaddr;
1204 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1205 uint64_t selfid = _pthread_selfid_direct();
1206
1207 PLOCKSTAT_MUTEX_BLOCK((pthread_mutex_t *)mutex);
1208 do {
1209 uint32_t uval;
1210 do {
1211 PTHREAD_TRACE(psynch_ffmutex_wait | DBG_FUNC_START, mutex,
1212 newseq.lgenval, newseq.ugenval, mutex->mtxopts.value);
1213 uval = __psynch_mutexwait(mutex, newseq.lgenval, newseq.ugenval,
1214 oldtid, mutex->mtxopts.value);
1215 PTHREAD_TRACE(psynch_ffmutex_wait | DBG_FUNC_END, mutex,
1216 uval, 0, 0);
1217 oldtid = os_atomic_load(tidaddr, relaxed);
1218 } while (uval == (uint32_t)-1);
1219 } while (!_pthread_mutex_firstfit_lock_updatebits(mutex, selfid, &newseq));
1220 PLOCKSTAT_MUTEX_BLOCKED((pthread_mutex_t *)mutex, BLOCK_SUCCESS_PLOCKSTAT);
1221
1222 return 0;
1223}
1224
1225PTHREAD_NOEXPORT PTHREAD_NOINLINE
1226int
1227_pthread_mutex_firstfit_lock_slow(_pthread_mutex *mutex, bool trylock)
1228{
1229 int res, recursive = 0;
1230
1231 mutex_seq *seqaddr;
1232 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
1233
1234 mutex_seq oldseq, newseq;
1235 mutex_seq_load(seqaddr, &oldseq);
1236
1237 uint64_t *tidaddr;
1238 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1239 uint64_t oldtid, selfid = _pthread_selfid_direct();
1240
1241 res = _pthread_mutex_lock_handle_options(mutex, trylock, tidaddr);
1242 if (res > 0) {
1243 recursive = 1;
1244 res = 0;
1245 goto out;
1246 } else if (res < 0) {
1247 res = -res;
1248 goto out;
1249 }
1250
1251 PTHREAD_TRACE(psynch_ffmutex_lock_updatebits | DBG_FUNC_START, mutex,
1252 oldseq.lgenval, oldseq.ugenval, 0);
1253
1254 bool gotlock;
1255 do {
1256 newseq = oldseq;
1257 oldtid = os_atomic_load(tidaddr, relaxed);
1258
1259 gotlock = is_rwl_ebit_clear(oldseq.lgenval);
1260 if (trylock && !gotlock) {
1261 // We still want to perform the CAS here, even though it won't
1262 // do anything so that it fails if someone unlocked while we were
1263 // in the loop
1264 } else if (gotlock) {
1265 // In first-fit, getting the lock simply adds the E-bit
1266 newseq.lgenval |= PTH_RWL_EBIT;
1267 } else {
1268 // Failed to get the lock, increment the L-val and go to
1269 // the kernel to sleep
1270 newseq.lgenval += PTHRW_INC;
1271 }
1272 } while (!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq, acquire));
1273
1274 PTHREAD_TRACE(psynch_ffmutex_lock_updatebits | DBG_FUNC_END, mutex,
1275 newseq.lgenval, newseq.ugenval, 0);
1276
1277 if (gotlock) {
1278 os_atomic_store(tidaddr, selfid, relaxed);
1279 res = 0;
1280 PTHREAD_TRACE(psynch_mutex_ulock, mutex, newseq.lgenval,
1281 newseq.ugenval, selfid);
1282 } else if (trylock) {
1283 res = EBUSY;
1284 PTHREAD_TRACE(psynch_mutex_utrylock_failed, mutex, newseq.lgenval,
1285 newseq.ugenval, oldtid);
1286 } else {
1287 PTHREAD_TRACE(psynch_mutex_ulock | DBG_FUNC_START, mutex,
1288 newseq.lgenval, newseq.ugenval, oldtid);
1289 res = _pthread_mutex_firstfit_lock_wait(mutex, newseq, oldtid);
1290 PTHREAD_TRACE(psynch_mutex_ulock | DBG_FUNC_END, mutex,
1291 newseq.lgenval, newseq.ugenval, oldtid);
1292 }
1293
1294 if (res == 0 && _pthread_mutex_is_recursive(mutex)) {
1295 mutex->mtxopts.options.lock_count = 1;
1296 }
1297
1298out:
1299#if PLOCKSTAT
1300 if (res == 0) {
1301 PLOCKSTAT_MUTEX_ACQUIRE((pthread_mutex_t *)mutex, recursive, 0);
1302 } else {
1303 PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, res);
1304 }
1305#endif
1306 return res;
1307}
1308
1309#pragma mark fast path
1310
1311PTHREAD_NOEXPORT PTHREAD_NOINLINE
1312int
1313_pthread_mutex_droplock(_pthread_mutex *mutex, uint32_t *flagsp,
1314 uint32_t **pmtxp, uint32_t *mgenp, uint32_t *ugenp)
1315{
1316 if (_pthread_mutex_is_fairshare(mutex)) {
1317 return _pthread_mutex_fairshare_unlock_updatebits(mutex, flagsp,
1318 pmtxp, mgenp, ugenp);
1319 }
1320 return _pthread_mutex_firstfit_unlock_updatebits(mutex, flagsp, pmtxp,
1321 mgenp, ugenp);
1322}
1323
1324PTHREAD_NOEXPORT PTHREAD_NOINLINE
1325int
1326_pthread_mutex_lock_init_slow(_pthread_mutex *mutex, bool trylock)
1327{
1328 int res;
1329
1330 res = _pthread_mutex_check_init(mutex);
1331 if (res != 0) return res;
1332
1333 if (os_unlikely(_pthread_mutex_is_fairshare(mutex))) {
1334 return _pthread_mutex_fairshare_lock_slow(mutex, trylock);
1335 }
1336 return _pthread_mutex_firstfit_lock_slow(mutex, trylock);
1337}
1338
1339PTHREAD_NOEXPORT PTHREAD_NOINLINE
1340static int
1341_pthread_mutex_unlock_init_slow(_pthread_mutex *mutex)
1342{
1343 int res;
1344
1345 // Initialize static mutexes for compatibility with misbehaving
1346 // applications (unlock should not be the first operation on a mutex).
1347 res = _pthread_mutex_check_init(mutex);
1348 if (res != 0) return res;
1349
1350 if (os_unlikely(_pthread_mutex_is_fairshare(mutex))) {
1351 return _pthread_mutex_fairshare_unlock_slow(mutex);
1352 }
1353 return _pthread_mutex_firstfit_unlock_slow(mutex);
1354}
1355
1356PTHREAD_NOEXPORT_VARIANT
1357int
1358pthread_mutex_unlock(pthread_mutex_t *omutex)
1359{
1360 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
1361 if (os_unlikely(!_pthread_mutex_check_signature_fast(mutex))) {
1362 return _pthread_mutex_unlock_init_slow(mutex);
1363 }
1364
1365 if (os_unlikely(_pthread_mutex_is_fairshare(mutex))) {
1366 return _pthread_mutex_fairshare_unlock(mutex);
1367 }
1368
1369#if ENABLE_USERSPACE_TRACE
1370 return _pthread_mutex_firstfit_unlock_slow(mutex);
1371#elif PLOCKSTAT
1372 if (PLOCKSTAT_MUTEX_RELEASE_ENABLED() || PLOCKSTAT_MUTEX_ERROR_ENABLED()) {
1373 return _pthread_mutex_firstfit_unlock_slow(mutex);
1374 }
1375#endif
1376
1377 /*
1378 * This is the first-fit fast path. The fairshare fast-ish path is in
1379 * _pthread_mutex_firstfit_unlock()
1380 */
1381 uint64_t *tidaddr;
1382 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1383
1384 mutex_seq *seqaddr;
1385 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
1386
1387 mutex_seq oldseq, newseq;
1388 mutex_seq_load(seqaddr, &oldseq);
1389
1390 // We're giving up the mutex one way or the other, so go ahead and
1391 // update the owner to 0 so that once the CAS below succeeds, there
1392 // is no stale ownership information. If the CAS of the seqaddr
1393 // fails, we may loop, but it's still valid for the owner to be
1394 // SWITCHING/0
1395 os_atomic_store(tidaddr, 0, relaxed);
1396
1397 do {
1398 newseq = oldseq;
1399
1400 if (diff_genseq(oldseq.lgenval, oldseq.ugenval) == 0) {
1401 // No outstanding waiters in kernel, we can simply drop the E-bit
1402 // and return.
1403 newseq.lgenval &= ~PTH_RWL_EBIT;
1404 } else {
1405 return _pthread_mutex_firstfit_unlock_slow(mutex);
1406 }
1407 } while (os_unlikely(!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq,
1408 release)));
1409
1410 return 0;
1411}
1412
1413PTHREAD_ALWAYS_INLINE
1414static inline int
1415_pthread_mutex_firstfit_lock(pthread_mutex_t *omutex, bool trylock)
1416{
1417 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
1418 if (os_unlikely(!_pthread_mutex_check_signature_fast(mutex))) {
1419 return _pthread_mutex_lock_init_slow(mutex, trylock);
1420 }
1421
1422 if (os_unlikely(_pthread_mutex_is_fairshare(mutex))) {
1423 return _pthread_mutex_fairshare_lock(mutex, trylock);
1424 }
1425
1426#if ENABLE_USERSPACE_TRACE
1427 return _pthread_mutex_firstfit_lock_slow(mutex, trylock);
1428#elif PLOCKSTAT
1429 if (PLOCKSTAT_MUTEX_ACQUIRE_ENABLED() || PLOCKSTAT_MUTEX_ERROR_ENABLED()) {
1430 return _pthread_mutex_firstfit_lock_slow(mutex, trylock);
1431 }
1432#endif
1433
1434 /*
1435 * This is the first-fit fast path. The fairshare fast-ish path is in
1436 * _pthread_mutex_firstfit_lock()
1437 */
1438 uint64_t *tidaddr;
1439 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1440 uint64_t selfid = _pthread_selfid_direct();
1441
1442 mutex_seq *seqaddr;
1443 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
1444
1445 mutex_seq oldseq, newseq;
1446 mutex_seq_load(seqaddr, &oldseq);
1447
1448 if (os_unlikely(oldseq.lgenval & PTH_RWL_EBIT)) {
1449 return _pthread_mutex_firstfit_lock_slow(mutex, trylock);
1450 }
1451
1452 bool gotlock;
1453 do {
1454 newseq = oldseq;
1455 gotlock = is_rwl_ebit_clear(oldseq.lgenval);
1456
1457 if (trylock && !gotlock) {
1458 // A trylock on a held lock will fail immediately. But since
1459 // we did not load the sequence words atomically, perform a
1460 // no-op CAS64 to ensure that nobody has unlocked concurrently.
1461 } else if (os_likely(gotlock)) {
1462 // In first-fit, getting the lock simply adds the E-bit
1463 newseq.lgenval |= PTH_RWL_EBIT;
1464 } else {
1465 return _pthread_mutex_firstfit_lock_slow(mutex, trylock);
1466 }
1467 } while (os_unlikely(!mutex_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq,
1468 acquire)));
1469
1470 if (os_likely(gotlock)) {
1471 os_atomic_store(tidaddr, selfid, relaxed);
1472 return 0;
1473 } else if (trylock) {
1474 return EBUSY;
1475 } else {
1476 __builtin_trap();
1477 }
1478}
1479
1480PTHREAD_NOEXPORT_VARIANT
1481int
1482pthread_mutex_lock(pthread_mutex_t *mutex)
1483{
1484 return _pthread_mutex_firstfit_lock(mutex, false);
1485}
1486
1487PTHREAD_NOEXPORT_VARIANT
1488int
1489pthread_mutex_trylock(pthread_mutex_t *mutex)
1490{
1491 return _pthread_mutex_firstfit_lock(mutex, true);
1492}
1493
964d3577 1494
a0619f9c 1495PTHREAD_ALWAYS_INLINE
964d3577 1496static inline int
3a6437e6
A
1497_pthread_mutex_init(_pthread_mutex *mutex, const pthread_mutexattr_t *attr,
1498 uint32_t static_type)
f1a1da6c 1499{
3a6437e6
A
1500 mutex->mtxopts.value = 0;
1501 mutex->mtxopts.options.mutex = 1;
f1a1da6c
A
1502 if (attr) {
1503 if (attr->sig != _PTHREAD_MUTEX_ATTR_SIG) {
1504 return EINVAL;
1505 }
a0619f9c 1506 mutex->prioceiling = (int16_t)attr->prioceiling;
f1a1da6c 1507 mutex->mtxopts.options.protocol = attr->protocol;
214d78a2 1508 mutex->mtxopts.options.policy = attr->opt;
f1a1da6c
A
1509 mutex->mtxopts.options.type = attr->type;
1510 mutex->mtxopts.options.pshared = attr->pshared;
1511 } else {
1512 switch (static_type) {
1513 case 1:
1514 mutex->mtxopts.options.type = PTHREAD_MUTEX_ERRORCHECK;
1515 break;
1516 case 2:
1517 mutex->mtxopts.options.type = PTHREAD_MUTEX_RECURSIVE;
1518 break;
1519 case 3:
1520 /* firstfit fall thru */
1521 case 7:
1522 mutex->mtxopts.options.type = PTHREAD_MUTEX_DEFAULT;
1523 break;
1524 default:
1525 return EINVAL;
1526 }
1527
1528 mutex->prioceiling = _PTHREAD_DEFAULT_PRIOCEILING;
1529 mutex->mtxopts.options.protocol = _PTHREAD_DEFAULT_PROTOCOL;
1530 if (static_type != 3) {
214d78a2 1531 mutex->mtxopts.options.policy = __pthread_mutex_default_opt_policy;
f1a1da6c 1532 } else {
214d78a2 1533 mutex->mtxopts.options.policy = _PTHREAD_MTX_OPT_POLICY_FIRSTFIT;
f1a1da6c
A
1534 }
1535 mutex->mtxopts.options.pshared = _PTHREAD_DEFAULT_PSHARED;
1536 }
f1a1da6c
A
1537 mutex->priority = 0;
1538
a0619f9c 1539 mutex_seq *seqaddr;
3a6437e6 1540 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
a0619f9c
A
1541
1542 uint64_t *tidaddr;
3a6437e6 1543 MUTEX_GETTID_ADDR(mutex, &tidaddr);
a0619f9c 1544
3a6437e6
A
1545#if PTHREAD_MUTEX_INIT_UNUSED
1546 if ((uint32_t*)tidaddr != mutex->m_tid) {
1547 mutex->mtxopts.options.misalign = 1;
1548 __builtin_memset(mutex->m_tid, 0xff, sizeof(mutex->m_tid));
964d3577 1549 }
3a6437e6
A
1550 __builtin_memset(mutex->m_mis, 0xff, sizeof(mutex->m_mis));
1551#endif // PTHREAD_MUTEX_INIT_UNUSED
1552 *tidaddr = 0;
a0619f9c 1553 *seqaddr = (mutex_seq){ };
964d3577
A
1554
1555 long sig = _PTHREAD_MUTEX_SIG;
1556 if (mutex->mtxopts.options.type == PTHREAD_MUTEX_NORMAL &&
214d78a2
A
1557 (_pthread_mutex_is_fairshare(mutex) ||
1558 _pthread_mutex_is_firstfit(mutex))) {
964d3577
A
1559 // rdar://18148854 _pthread_mutex_lock & pthread_mutex_unlock fastpath
1560 sig = _PTHREAD_MUTEX_SIG_fast;
1561 }
f1a1da6c 1562
3a6437e6
A
1563#if PTHREAD_MUTEX_INIT_UNUSED
1564 // For detecting copied mutexes and smashes during debugging
1565 uint32_t sig32 = (uint32_t)sig;
1566#if defined(__LP64__)
a0619f9c 1567 uintptr_t guard = ~(uintptr_t)mutex; // use ~ to hide from leaks
3a6437e6
A
1568 __builtin_memcpy(mutex->_reserved, &guard, sizeof(guard));
1569 mutex->_reserved[2] = sig32;
1570 mutex->_reserved[3] = sig32;
1571 mutex->_pad = sig32;
1572#else
1573 mutex->_reserved[0] = sig32;
1574#endif
1575#endif // PTHREAD_MUTEX_INIT_UNUSED
964d3577
A
1576
1577 // Ensure all contents are properly set before setting signature.
1578#if defined(__LP64__)
1579 // For binary compatibility reasons we cannot require natural alignment of
1580 // the 64bit 'sig' long value in the struct. rdar://problem/21610439
1581 uint32_t *sig32_ptr = (uint32_t*)&mutex->sig;
1582 uint32_t *sig32_val = (uint32_t*)&sig;
a0619f9c 1583 *(sig32_ptr + 1) = *(sig32_val + 1);
964d3577
A
1584 os_atomic_store(sig32_ptr, *sig32_val, release);
1585#else
1586 os_atomic_store2o(mutex, sig, sig, release);
1587#endif
f1a1da6c
A
1588
1589 return 0;
1590}
1591
a0619f9c 1592PTHREAD_NOEXPORT_VARIANT
f1a1da6c
A
1593int
1594pthread_mutex_destroy(pthread_mutex_t *omutex)
1595{
1596 _pthread_mutex *mutex = (_pthread_mutex *)omutex;
1597
1598 int res = EINVAL;
1599
2546420a 1600 _PTHREAD_LOCK(mutex->lock);
964d3577 1601 if (_pthread_mutex_check_signature(mutex)) {
a0619f9c 1602 mutex_seq *seqaddr;
f1a1da6c 1603 MUTEX_GETSEQ_ADDR(mutex, &seqaddr);
a0619f9c
A
1604
1605 mutex_seq seq;
1606 mutex_seq_load(seqaddr, &seq);
1607
1608 uint64_t *tidaddr;
f1a1da6c
A
1609 MUTEX_GETTID_ADDR(mutex, &tidaddr);
1610
a0619f9c
A
1611 if ((os_atomic_load(tidaddr, relaxed) == 0) &&
1612 (seq.lgenval & PTHRW_COUNT_MASK) ==
1613 (seq.ugenval & PTHRW_COUNT_MASK)) {
f1a1da6c
A
1614 mutex->sig = _PTHREAD_NO_SIG;
1615 res = 0;
1616 } else {
1617 res = EBUSY;
1618 }
964d3577 1619 } else if (_pthread_mutex_check_signature_init(mutex)) {
f1a1da6c
A
1620 mutex->sig = _PTHREAD_NO_SIG;
1621 res = 0;
1622 }
2546420a 1623 _PTHREAD_UNLOCK(mutex->lock);
f1a1da6c 1624
a0619f9c
A
1625 return res;
1626}
964d3577 1627
f1a1da6c
A
1628#endif /* !BUILDING_VARIANT ] */
1629
1630/*
1631 * Destroy a mutex attribute structure.
1632 */
1633int
1634pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
1635{
1636#if __DARWIN_UNIX03
1637 if (__unix_conforming == 0) {
1638 __unix_conforming = 1;
1639 }
1640 if (attr->sig != _PTHREAD_MUTEX_ATTR_SIG) {
1641 return EINVAL;
1642 }
1643#endif /* __DARWIN_UNIX03 */
1644
1645 attr->sig = _PTHREAD_NO_SIG;
1646 return 0;
1647}
1648