2 * Copyright (c) 2000-2003, 2007, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1998 Alex Nash
25 * All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * $FreeBSD: src/lib/libc_r/uthread/uthread_rwlock.c,v 1.6 2001/04/10 04:19:20 deischen Exp $
52 * POSIX Pthread Library
53 * -- Read Write Lock support
61 #include <platform/compat.h> // for bzero
65 #include "plockstat.h"
66 #else /* !PLOCKSTAT */
67 #define PLOCKSTAT_RW_ERROR(x, y, z)
68 #define PLOCKSTAT_RW_BLOCK(x, y)
69 #define PLOCKSTAT_RW_BLOCKED(x, y, z)
70 #define PLOCKSTAT_RW_ACQUIRE(x, y)
71 #define PLOCKSTAT_RW_RELEASE(x, y)
72 #endif /* PLOCKSTAT */
74 #define READ_LOCK_PLOCKSTAT 0
75 #define WRITE_LOCK_PLOCKSTAT 1
77 #define BLOCK_FAIL_PLOCKSTAT 0
78 #define BLOCK_SUCCESS_PLOCKSTAT 1
80 #define PTHREAD_RWLOCK_INIT_UNUSED 1
82 // maximum number of times a read lock may be obtained
83 #define MAX_READ_LOCKS (INT_MAX - 1)
85 union rwlock_seq
; // forward declaration
86 enum rwlock_seqfields
; // forward declaration
88 PTHREAD_NOEXPORT PTHREAD_WEAK
// prevent inlining of return value into callers
89 int _pthread_rwlock_lock_slow(pthread_rwlock_t
*orwlock
, bool readlock
,
92 PTHREAD_NOEXPORT PTHREAD_WEAK
// prevent inlining of return value into callers
93 int _pthread_rwlock_unlock_slow(pthread_rwlock_t
*orwlock
,
94 enum rwlock_seqfields updated_seqfields
);
98 #define RWLOCK_USE_INT128 1
101 typedef union rwlock_seq
{
103 struct { uint32_t lcntval
; uint32_t rw_seq
; uint32_t ucntval
; };
104 struct { uint32_t lgen
; uint32_t rw_wc
; uint32_t ugen
; };
105 #if RWLOCK_USE_INT128
106 unsigned __int128 seq_LSU
;
107 unsigned __int128 _Atomic atomic_seq_LSU
;
115 uint64_t _Atomic atomic_seq_LS
;
116 uint32_t _Atomic atomic_seq_U
;
117 uint32_t _Atomic _atomic_pad
;
121 _Static_assert(sizeof(rwlock_seq
) == 4 * sizeof(uint32_t),
122 "Incorrect rwlock_seq size");
124 typedef enum rwlock_seqfields
{
128 RWLOCK_SEQ_LSU
= RWLOCK_SEQ_LS
| RWLOCK_SEQ_U
,
131 #if PTHREAD_DEBUG_LOG
132 #define RWLOCK_DEBUG_SEQ(op, rwlock, oldseq, newseq, updateval, f) \
133 if (_pthread_debuglog >= 0) { \
134 _simple_dprintf(_pthread_debuglog, "rw_" #op " %p tck %7llu thr %llx " \
135 "L %x -> %x S %x -> %x U %x -> %x updt %x\n", rwlock, \
136 mach_absolute_time() - _pthread_debugstart, _pthread_selfid_direct(), \
137 (f) & RWLOCK_SEQ_LS ? (oldseq).lcntval : 0, \
138 (f) & RWLOCK_SEQ_LS ? (newseq).lcntval : 0, \
139 (f) & RWLOCK_SEQ_LS ? (oldseq).rw_seq : 0, \
140 (f) & RWLOCK_SEQ_LS ? (newseq).rw_seq : 0, \
141 (f) & RWLOCK_SEQ_U ? (oldseq).ucntval : 0, \
142 (f) & RWLOCK_SEQ_U ? (newseq).ucntval : 0, updateval); }
144 #define RWLOCK_DEBUG_SEQ(m, rwlock, oldseq, newseq, updateval, f)
147 #if !__LITTLE_ENDIAN__
148 #error RWLOCK_GETSEQ_ADDR assumes little endian layout of sequence words
151 PTHREAD_ALWAYS_INLINE
153 RWLOCK_GETSEQ_ADDR(_pthread_rwlock
*rwlock
, rwlock_seq
**seqaddr
)
155 // 128-bit aligned address inside rw_seq & rw_mis arrays
156 *seqaddr
= (void*)(((uintptr_t)rwlock
->rw_seq
+ 0xful
) & ~0xful
);
159 PTHREAD_ALWAYS_INLINE
161 RWLOCK_GETTID_ADDR(_pthread_rwlock
*rwlock
, uint64_t **tidaddr
)
163 // 64-bit aligned address inside rw_tid array (&rw_tid[0] for aligned lock)
164 *tidaddr
= (void*)(((uintptr_t)rwlock
->rw_tid
+ 0x7ul
) & ~0x7ul
);
167 PTHREAD_ALWAYS_INLINE
169 rwlock_seq_load(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
170 const rwlock_seqfields seqfields
)
174 #if RWLOCK_USE_INT128
175 oldseqval
->seq_LSU
= seqaddr
->seq_LSU
;
177 oldseqval
->seq_LS
= seqaddr
->seq_LS
;
178 oldseqval
->seq_U
= seqaddr
->seq_U
;
182 oldseqval
->seq_LS
= seqaddr
->seq_LS
;
186 oldseqval
->seq_U
= seqaddr
->seq_U
;
194 PTHREAD_ALWAYS_INLINE
196 rwlock_seq_atomic_load_relaxed(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
197 const rwlock_seqfields seqfields
)
201 #if RWLOCK_USE_INT128
202 oldseqval
->seq_LSU
= os_atomic_load(&seqaddr
->atomic_seq_LSU
, relaxed
);
204 oldseqval
->seq_LS
= os_atomic_load(&seqaddr
->atomic_seq_LS
, relaxed
);
205 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
209 oldseqval
->seq_LS
= os_atomic_load(&seqaddr
->atomic_seq_LS
, relaxed
);
213 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
221 #define rwlock_seq_atomic_load(seqaddr, oldseqval, seqfields, m) \
222 rwlock_seq_atomic_load_##m(seqaddr, oldseqval, seqfields)
224 PTHREAD_ALWAYS_INLINE
225 static inline rwlock_seqfields
226 rwlock_seq_atomic_cmpxchgv_relaxed(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
227 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
230 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
234 #if RWLOCK_USE_INT128
235 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
236 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, relaxed
);
237 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
239 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
240 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
242 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
243 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
244 if (!r
) oldseqval
->seq_LS
= newseqval
->seq_LS
;
245 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_LS
;
247 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
252 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
253 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
254 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
258 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
259 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
260 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
265 return updated_seqfields
;
268 PTHREAD_ALWAYS_INLINE
269 static inline rwlock_seqfields
270 rwlock_seq_atomic_cmpxchgv_acquire(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
271 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
274 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
278 #if RWLOCK_USE_INT128
279 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
280 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, acquire
);
281 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
283 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
284 newseqval
->seq_LS
, &oldseqval
->seq_LS
, acquire
);
286 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
287 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
288 if (!r
) oldseqval
->seq_LS
= newseqval
->seq_LS
;
289 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_LS
;
291 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
296 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
297 newseqval
->seq_U
, &oldseqval
->seq_U
, acquire
);
298 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
302 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
303 newseqval
->seq_LS
, &oldseqval
->seq_LS
, acquire
);
304 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
309 return updated_seqfields
;
312 PTHREAD_ALWAYS_INLINE
313 static inline rwlock_seqfields
314 rwlock_seq_atomic_cmpxchgv_release(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
315 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
318 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
321 #if RWLOCK_USE_INT128
322 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
323 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, release
);
324 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
326 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
327 newseqval
->seq_U
, &oldseqval
->seq_U
, release
);
329 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
330 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
331 if (!r
) oldseqval
->seq_U
= newseqval
->seq_U
;
332 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_U
;
334 oldseqval
->seq_LS
= os_atomic_load(&seqaddr
->atomic_seq_LS
,relaxed
);
339 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
340 newseqval
->seq_LS
, &oldseqval
->seq_LS
, release
);
341 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
345 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
346 newseqval
->seq_U
, &oldseqval
->seq_U
, release
);
347 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
353 return updated_seqfields
;
356 #define rwlock_seq_atomic_cmpxchgv(seqaddr, oldseqval, newseqval, seqfields, m)\
357 rwlock_seq_atomic_cmpxchgv_##m(seqaddr, oldseqval, newseqval, seqfields)
359 #ifndef BUILDING_VARIANT /* [ */
362 pthread_rwlockattr_init(pthread_rwlockattr_t
*attr
)
364 attr
->sig
= _PTHREAD_RWLOCK_ATTR_SIG
;
365 attr
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
370 pthread_rwlockattr_destroy(pthread_rwlockattr_t
*attr
)
372 attr
->sig
= _PTHREAD_NO_SIG
;
378 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t
*attr
, int *pshared
)
381 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
382 *pshared
= (int)attr
->pshared
;
389 pthread_rwlockattr_setpshared(pthread_rwlockattr_t
* attr
, int pshared
)
392 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
394 if (( pshared
== PTHREAD_PROCESS_PRIVATE
) ||
395 (pshared
== PTHREAD_PROCESS_SHARED
))
396 #else /* __DARWIN_UNIX03 */
397 if ( pshared
== PTHREAD_PROCESS_PRIVATE
)
398 #endif /* __DARWIN_UNIX03 */
400 attr
->pshared
= pshared
;
407 #endif /* !BUILDING_VARIANT ] */
409 PTHREAD_ALWAYS_INLINE
411 _pthread_rwlock_init(_pthread_rwlock
*rwlock
, const pthread_rwlockattr_t
*attr
)
414 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
417 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
419 #if PTHREAD_RWLOCK_INIT_UNUSED
420 if ((uint32_t*)tidaddr
!= rwlock
->rw_tid
) {
421 rwlock
->misalign
= 1;
422 __builtin_memset(rwlock
->rw_tid
, 0xff, sizeof(rwlock
->rw_tid
));
424 if ((uint32_t*)seqaddr
!= rwlock
->rw_seq
) {
425 __builtin_memset(rwlock
->rw_seq
, 0xff, sizeof(rwlock
->rw_seq
));
427 __builtin_memset(rwlock
->rw_mis
, 0xff, sizeof(rwlock
->rw_mis
));
428 #endif // PTHREAD_MUTEX_INIT_UNUSED
430 *seqaddr
= (rwlock_seq
){
431 .lcntval
= PTHRW_RWLOCK_INIT
,
432 .rw_seq
= PTHRW_RWS_INIT
,
436 if (attr
!= NULL
&& attr
->pshared
== PTHREAD_PROCESS_SHARED
) {
437 rwlock
->pshared
= PTHREAD_PROCESS_SHARED
;
438 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_SHARED
;
440 rwlock
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
441 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_PRIVATE
;
444 long sig
= _PTHREAD_RWLOCK_SIG
;
447 bzero(rwlock
->_reserved
, sizeof(rwlock
->_reserved
));
449 #if PTHREAD_RWLOCK_INIT_UNUSED
450 // For detecting copied rwlocks and smashes during debugging
451 uint32_t sig32
= (uint32_t)sig
;
452 uintptr_t guard
= ~(uintptr_t)rwlock
; // use ~ to hide from leaks
453 __builtin_memcpy(rwlock
->_reserved
, &guard
, sizeof(guard
));
454 #define countof(x) (sizeof(x) / sizeof(x[0]))
455 rwlock
->_reserved
[countof(rwlock
->_reserved
) - 1] = sig32
;
456 #if defined(__LP64__)
457 rwlock
->_pad
= sig32
;
459 #endif // PTHREAD_RWLOCK_INIT_UNUSED
461 // Ensure all contents are properly set before setting signature.
462 #if defined(__LP64__)
463 // For binary compatibility reasons we cannot require natural alignment of
464 // the 64bit 'sig' long value in the struct. rdar://problem/21610439
465 uint32_t *sig32_ptr
= (uint32_t*)&rwlock
->sig
;
466 uint32_t *sig32_val
= (uint32_t*)&sig
;
467 *(sig32_ptr
+ 1) = *(sig32_val
+ 1);
468 os_atomic_store(sig32_ptr
, *sig32_val
, release
);
470 os_atomic_store2o(rwlock
, sig
, sig
, release
);
477 _pthread_rwlock_modbits(uint32_t lgenval
, uint32_t updateval
, uint32_t savebits
)
479 uint32_t lval
= lgenval
& PTHRW_BIT_MASK
;
480 uint32_t uval
= updateval
& PTHRW_BIT_MASK
;
481 uint32_t rval
, nlval
;
483 nlval
= (lval
| uval
) & ~(PTH_RWL_MBIT
);
485 // reconcile bits on the lock with what kernel needs to set
486 if ((uval
& PTH_RWL_KBIT
) == 0 && (lval
& PTH_RWL_WBIT
) == 0) {
487 nlval
&= ~PTH_RWL_KBIT
;
491 if ((savebits
& PTH_RWS_WSVBIT
) != 0 && (nlval
& PTH_RWL_WBIT
) == 0 &&
492 (nlval
& PTH_RWL_EBIT
) == 0) {
493 nlval
|= (PTH_RWL_WBIT
| PTH_RWL_KBIT
);
496 rval
= (lgenval
& PTHRW_COUNT_MASK
) | nlval
;
500 PTHREAD_ALWAYS_INLINE
502 _pthread_rwlock_updateval(_pthread_rwlock
*rwlock
, uint32_t updateval
)
504 bool isoverlap
= (updateval
& PTH_RWL_MBIT
) != 0;
506 // TBD: restore U bit
508 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
510 rwlock_seq oldseq
, newseq
;
511 rwlock_seq_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LS
);
514 if (isoverlap
|| is_rws_unlockinit_set(oldseq
.rw_seq
)) {
515 // Set S word to the specified value
516 uint32_t savebits
= (oldseq
.rw_seq
& PTHRW_RWS_SAVEMASK
);
517 newseq
.lcntval
= _pthread_rwlock_modbits(oldseq
.lcntval
, updateval
,
519 newseq
.rw_seq
+= (updateval
& PTHRW_COUNT_MASK
);
521 newseq
.rw_seq
&= PTHRW_COUNT_MASK
;
523 newseq
.rw_seq
&= ~PTHRW_RWS_SAVEMASK
;
525 } while (!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
526 RWLOCK_SEQ_LS
, relaxed
));
527 RWLOCK_DEBUG_SEQ(update
, rwlock
, oldseq
, newseq
, updateval
, RWLOCK_SEQ_LS
);
531 PTHREAD_ALWAYS_INLINE
533 _pthread_rwlock_check_busy(_pthread_rwlock
*rwlock
)
538 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
541 rwlock_seq_atomic_load(seqaddr
, &seq
, RWLOCK_SEQ_LSU
, relaxed
);
542 if ((seq
.lcntval
& PTHRW_COUNT_MASK
) != seq
.ucntval
) {
548 #endif /* __DARWIN_UNIX03 */
550 PTHREAD_NOEXPORT_VARIANT
552 pthread_rwlock_destroy(pthread_rwlock_t
*orwlock
)
555 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
557 _PTHREAD_LOCK(rwlock
->lock
);
558 if (_pthread_rwlock_check_signature(rwlock
)) {
560 res
= _pthread_rwlock_check_busy(rwlock
);
561 #endif /* __DARWIN_UNIX03 */
562 } else if (!_pthread_rwlock_check_signature_init(rwlock
)) {
566 rwlock
->sig
= _PTHREAD_NO_SIG
;
568 _PTHREAD_UNLOCK(rwlock
->lock
);
572 PTHREAD_NOEXPORT_VARIANT
574 pthread_rwlock_init(pthread_rwlock_t
*orwlock
, const pthread_rwlockattr_t
*attr
)
577 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
580 if (attr
&& attr
->sig
!= _PTHREAD_RWLOCK_ATTR_SIG
) {
584 if (res
== 0 && _pthread_rwlock_check_signature(rwlock
)) {
585 res
= _pthread_rwlock_check_busy(rwlock
);
589 _PTHREAD_LOCK_INIT(rwlock
->lock
);
590 res
= _pthread_rwlock_init(rwlock
, attr
);
597 _pthread_rwlock_check_init_slow(pthread_rwlock_t
*orwlock
)
600 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
602 if (_pthread_rwlock_check_signature_init(rwlock
)) {
603 _PTHREAD_LOCK(rwlock
->lock
);
604 if (_pthread_rwlock_check_signature_init(rwlock
)) {
605 res
= _pthread_rwlock_init(rwlock
, NULL
);
606 } else if (_pthread_rwlock_check_signature(rwlock
)){
609 _PTHREAD_UNLOCK(rwlock
->lock
);
610 } else if (_pthread_rwlock_check_signature(rwlock
)){
614 PLOCKSTAT_RW_ERROR(orwlock
, READ_LOCK_PLOCKSTAT
, res
);
619 PTHREAD_ALWAYS_INLINE
621 _pthread_rwlock_check_init(pthread_rwlock_t
*orwlock
)
624 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
626 if (!_pthread_rwlock_check_signature(rwlock
)) {
627 return _pthread_rwlock_check_init_slow(orwlock
);
634 _pthread_rwlock_lock_wait(pthread_rwlock_t
*orwlock
, bool readlock
,
638 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
641 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
645 RWLOCK_DEBUG_SEQ(rdlock
, rwlock
, oldseq
, newseq
, gotlock
,
648 RWLOCK_DEBUG_SEQ(wrlock
, rwlock
, oldseq
, newseq
, gotlock
,
654 PLOCKSTAT_RW_BLOCK(orwlock
, plockstat
);
658 updateval
= __psynch_rw_rdlock(orwlock
, newseq
.lcntval
,
659 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
661 updateval
= __psynch_rw_wrlock(orwlock
, newseq
.lcntval
,
662 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
664 if (updateval
== (uint32_t)-1) {
669 } while (res
== EINTR
);
672 _pthread_rwlock_updateval(rwlock
, updateval
);
673 PLOCKSTAT_RW_BLOCKED(orwlock
, plockstat
, BLOCK_SUCCESS_PLOCKSTAT
);
675 PLOCKSTAT_RW_BLOCKED(orwlock
, plockstat
, BLOCK_FAIL_PLOCKSTAT
);
676 PTHREAD_ABORT("kernel rwlock returned unknown error %x: "
677 "tid %llx\n", res
, _pthread_selfid_direct());
683 PTHREAD_NOEXPORT PTHREAD_NOINLINE
685 _pthread_rwlock_lock_slow(pthread_rwlock_t
*orwlock
, bool readlock
,
689 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
692 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
695 res
= _pthread_rwlock_check_init(orwlock
);
696 if (res
!= 0) return res
;
699 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
701 rwlock_seq oldseq
, newseq
;
702 rwlock_seq_atomic_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LSU
, relaxed
);
706 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
707 uint64_t selfid
= _pthread_selfid_direct();
708 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
709 if (os_atomic_load(tidaddr
, relaxed
) == selfid
) return EDEADLK
;
711 #endif /* __DARWIN_UNIX03 */
720 // if W and K bit are clear or U bit is on, acquire lock in userland
722 gotlock
= (oldseq
.lcntval
& (PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0;
724 gotlock
= (oldseq
.lcntval
& PTH_RWL_UBIT
) != 0;
727 if (trylock
&& !gotlock
) {
728 // A trylock on a held lock will fail immediately. But since
729 // we did not load the sequence words atomically, perform a
730 // no-op CAS to ensure that nobody has unlocked concurrently.
731 } else if (gotlock
) {
733 if (diff_genseq(oldseq
.lcntval
, oldseq
.ucntval
) >=
735 // since ucntval may be newer, just redo
737 if (retry_count
> 1024) {
743 rwlock_seq_atomic_load(seqaddr
, &oldseq
,
744 RWLOCK_SEQ_LSU
, relaxed
);
748 // Need to update L (remove U bit) and S word
749 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
751 newseq
.lcntval
&= PTHRW_COUNT_MASK
;
752 newseq
.lcntval
|= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
754 newseq
.lcntval
+= PTHRW_INC
;
755 newseq
.rw_seq
+= PTHRW_INC
;
758 // Need to block in kernel. Remove U bit.
759 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
761 newseq
.lcntval
|= PTH_RWL_KBIT
| PTH_RWL_WBIT
;
763 newseq
.lcntval
+= PTHRW_INC
;
764 if (is_rws_sbit_set(oldseq
.rw_seq
)) {
765 // Clear the S bit and set S to L
766 newseq
.rw_seq
&= (PTHRW_BIT_MASK
& ~PTH_RWS_SBIT
);
767 newseq
.rw_seq
|= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
770 } while (!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
771 RWLOCK_SEQ_LS
, acquire
));
775 if (!readlock
) os_atomic_store(tidaddr
, selfid
, relaxed
);
776 #endif /* __DARWIN_UNIX03 */
778 } else if (trylock
) {
781 res
= _pthread_rwlock_lock_wait(orwlock
, readlock
, newseq
);
787 PLOCKSTAT_RW_ACQUIRE(orwlock
, plockstat
);
789 PLOCKSTAT_RW_ERROR(orwlock
, plockstat
, res
);
796 PTHREAD_ALWAYS_INLINE
798 _pthread_rwlock_lock(pthread_rwlock_t
*orwlock
, bool readlock
, bool trylock
)
800 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
802 if (PLOCKSTAT_RW_ACQUIRE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) {
803 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
807 if (os_unlikely(!_pthread_rwlock_check_signature(rwlock
))) {
808 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
812 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
814 rwlock_seq oldseq
, newseq
;
815 // no need to perform a single-copy-atomic 128-bit load in the fastpath,
816 // if stores to L and U are seen out of order, we will fallback to the
817 // slowpath below (which has rwlock_seq_atomic_load)
818 rwlock_seq_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LSU
);
821 if (os_unlikely(is_rwl_ebit_set(oldseq
.lcntval
))) {
822 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
824 #endif /* __DARWIN_UNIX03 */
830 // if W and K bit are clear or U bit is on, acquire lock in userland
832 gotlock
= (oldseq
.lcntval
& (PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0;
834 gotlock
= (oldseq
.lcntval
& PTH_RWL_UBIT
) != 0;
837 if (trylock
&& !gotlock
) {
838 // A trylock on a held lock will fail immediately. But since
839 // we did not load the sequence words atomically, perform a
840 // no-op CAS to ensure that nobody has unlocked concurrently.
841 } else if (os_likely(gotlock
)) {
843 if (os_unlikely(diff_genseq(oldseq
.lcntval
, oldseq
.ucntval
) >=
844 PTHRW_MAX_READERS
)) {
845 return _pthread_rwlock_lock_slow(orwlock
, readlock
,trylock
);
847 // Need to update L (remove U bit) and S word
848 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
850 newseq
.lcntval
&= PTHRW_COUNT_MASK
;
851 newseq
.lcntval
|= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
853 newseq
.lcntval
+= PTHRW_INC
;
854 newseq
.rw_seq
+= PTHRW_INC
;
856 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
858 } while (os_unlikely(!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
859 RWLOCK_SEQ_LS
, acquire
)));
861 if (os_likely(gotlock
)) {
865 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
866 uint64_t selfid
= _pthread_selfid_direct();
867 os_atomic_store(tidaddr
, selfid
, relaxed
);
869 #endif /* __DARWIN_UNIX03 */
871 } else if (trylock
) {
878 PTHREAD_NOEXPORT_VARIANT
880 pthread_rwlock_rdlock(pthread_rwlock_t
*orwlock
)
883 return _pthread_rwlock_lock(orwlock
, true, false);
886 PTHREAD_NOEXPORT_VARIANT
888 pthread_rwlock_tryrdlock(pthread_rwlock_t
*orwlock
)
890 // read lock, try lock
891 return _pthread_rwlock_lock(orwlock
, true, true);
894 PTHREAD_NOEXPORT_VARIANT
896 pthread_rwlock_wrlock(pthread_rwlock_t
*orwlock
)
898 // write lock, no try
899 return _pthread_rwlock_lock(orwlock
, false, false);
902 PTHREAD_NOEXPORT_VARIANT
904 pthread_rwlock_trywrlock(pthread_rwlock_t
*orwlock
)
906 // write lock, try lock
907 return _pthread_rwlock_lock(orwlock
, false, true);
912 _pthread_rwlock_unlock_drop(pthread_rwlock_t
*orwlock
, rwlock_seq oldseq
,
916 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
918 RWLOCK_DEBUG_SEQ(unlock
, rwlock
, oldseq
, newseq
, !droplock
, RWLOCK_SEQ_LSU
);
921 updateval
= __psynch_rw_unlock(orwlock
, oldseq
.lcntval
,
922 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
923 if (updateval
== (uint32_t)-1) {
927 RWLOCK_DEBUG_SEQ(wakeup
, rwlock
, oldseq
, newseq
, updateval
,
930 } while (res
== EINTR
);
933 PTHREAD_ABORT("kernel rwunlock returned unknown error %x: "
934 "tid %llx\n", res
, _pthread_selfid_direct());
940 PTHREAD_NOEXPORT PTHREAD_NOINLINE
942 _pthread_rwlock_unlock_slow(pthread_rwlock_t
*orwlock
,
943 rwlock_seqfields updated_seqfields
)
946 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
947 rwlock_seqfields seqfields
= RWLOCK_SEQ_LSU
;
952 res
= _pthread_rwlock_check_init(orwlock
);
953 if (res
!= 0) return res
;
956 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
958 rwlock_seq oldseq
, newseq
;
959 rwlock_seq_load(seqaddr
, &oldseq
, seqfields
);
961 if ((oldseq
.lcntval
& PTH_RWL_UBIT
) != 0) {
962 // spurious unlock (unlock of unlocked lock)
966 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
972 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
973 os_atomic_store(tidaddr
, 0, relaxed
);
974 #endif /* __DARWIN_UNIX03 */
979 // stop loading & updating fields that have successfully been stored
980 seqfields
&= ~updated_seqfields
;
983 if (seqfields
& RWLOCK_SEQ_U
) {
984 newseq
.ucntval
+= PTHRW_INC
;
988 uint32_t oldlcnt
= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
989 if (newseq
.ucntval
== oldlcnt
) {
990 // last unlock, set L with U and init bits and set S to L with S bit
991 newseq
.lcntval
= oldlcnt
| PTHRW_RWLOCK_INIT
;
992 newseq
.rw_seq
= oldlcnt
| PTHRW_RWS_INIT
;
994 // no L/S update if lock is not exclusive or no writer pending
995 if ((oldseq
.lcntval
&
996 (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0) {
1000 // kernel transition only needed if U == S
1001 if (newseq
.ucntval
!= (oldseq
.rw_seq
& PTHRW_COUNT_MASK
)) {
1006 // reset all bits and set K
1007 newseq
.lcntval
= oldlcnt
| PTH_RWL_KBIT
;
1008 // set I bit on S word
1009 newseq
.rw_seq
|= PTH_RWS_IBIT
;
1010 if ((oldseq
.lcntval
& PTH_RWL_WBIT
) != 0) {
1011 newseq
.rw_seq
|= PTH_RWS_WSVBIT
;
1014 } while (seqfields
!= (updated_seqfields
= rwlock_seq_atomic_cmpxchgv(
1015 seqaddr
, &oldseq
, &newseq
, seqfields
, release
)));
1018 res
= _pthread_rwlock_unlock_drop(orwlock
, oldseq
, newseq
);
1021 PLOCKSTAT_RW_RELEASE(orwlock
, wrlock
);
1026 PTHREAD_NOEXPORT_VARIANT
1028 pthread_rwlock_unlock(pthread_rwlock_t
*orwlock
)
1030 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
1031 rwlock_seqfields seqfields
= RWLOCK_SEQ_LSU
;
1032 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
1035 if (PLOCKSTAT_RW_RELEASE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) {
1036 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1040 if (os_unlikely(!_pthread_rwlock_check_signature(rwlock
))) {
1041 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1044 rwlock_seq
*seqaddr
;
1045 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
1047 rwlock_seq oldseq
, newseq
;
1048 rwlock_seq_load(seqaddr
, &oldseq
, seqfields
);
1050 if (os_unlikely(oldseq
.lcntval
& PTH_RWL_UBIT
)) {
1051 // spurious unlock (unlock of unlocked lock)
1055 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
1058 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
1059 os_atomic_store(tidaddr
, 0, relaxed
);
1060 #endif /* __DARWIN_UNIX03 */
1064 if (updated_seqfields
) {
1065 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1069 if (seqfields
& RWLOCK_SEQ_U
) {
1070 newseq
.ucntval
+= PTHRW_INC
;
1073 uint32_t oldlcnt
= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
1074 if (os_likely(newseq
.ucntval
== oldlcnt
)) {
1075 // last unlock, set L with U and init bits and set S to L with S bit
1076 newseq
.lcntval
= oldlcnt
| PTHRW_RWLOCK_INIT
;
1077 newseq
.rw_seq
= oldlcnt
| PTHRW_RWS_INIT
;
1079 if (os_likely((oldseq
.lcntval
&
1080 (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0 ||
1081 newseq
.ucntval
!= (oldseq
.rw_seq
& PTHRW_COUNT_MASK
))) {
1082 // no L/S update if lock is not exclusive or no writer pending
1083 // kernel transition only needed if U == S
1085 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1088 } while (os_unlikely(seqfields
!= (updated_seqfields
=
1089 rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
, seqfields
,