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
62 #include "plockstat.h"
63 #else /* !PLOCKSTAT */
64 #define PLOCKSTAT_RW_ERROR(x, y, z)
65 #define PLOCKSTAT_RW_BLOCK(x, y)
66 #define PLOCKSTAT_RW_BLOCKED(x, y, z)
67 #define PLOCKSTAT_RW_ACQUIRE(x, y)
68 #define PLOCKSTAT_RW_RELEASE(x, y)
69 #endif /* PLOCKSTAT */
71 #define READ_LOCK_PLOCKSTAT 0
72 #define WRITE_LOCK_PLOCKSTAT 1
74 #define BLOCK_FAIL_PLOCKSTAT 0
75 #define BLOCK_SUCCESS_PLOCKSTAT 1
77 #define PTHREAD_RWLOCK_INIT_UNUSED 1
79 // maximum number of times a read lock may be obtained
80 #define MAX_READ_LOCKS (INT_MAX - 1)
84 #define RWLOCK_USE_INT128 1
87 typedef union rwlock_seq
{
89 struct { uint32_t lcntval
; uint32_t rw_seq
; uint32_t ucntval
; };
90 struct { uint32_t lgen
; uint32_t rw_wc
; uint32_t ugen
; };
92 unsigned __int128 seq_LSU
;
93 unsigned __int128 _Atomic atomic_seq_LSU
;
101 uint64_t _Atomic atomic_seq_LS
;
102 uint32_t _Atomic atomic_seq_U
;
103 uint32_t _Atomic _atomic_pad
;
107 _Static_assert(sizeof(rwlock_seq
) == 4 * sizeof(uint32_t),
108 "Incorrect rwlock_seq size");
110 typedef enum rwlock_seqfields
{
114 RWLOCK_SEQ_LSU
= RWLOCK_SEQ_LS
| RWLOCK_SEQ_U
,
117 #if PTHREAD_DEBUG_LOG
118 #define RWLOCK_DEBUG_SEQ(op, rwlock, oldseq, newseq, updateval, f) \
119 if (_pthread_debuglog >= 0) { \
120 _simple_dprintf(_pthread_debuglog, "rw_" #op " %p tck %7llu thr %llx " \
121 "L %x -> %x S %x -> %x U %x -> %x updt %x\n", rwlock, \
122 mach_absolute_time() - _pthread_debugstart, _pthread_threadid_self_np_direct(), \
123 (f) & RWLOCK_SEQ_LS ? (oldseq).lcntval : 0, \
124 (f) & RWLOCK_SEQ_LS ? (newseq).lcntval : 0, \
125 (f) & RWLOCK_SEQ_LS ? (oldseq).rw_seq : 0, \
126 (f) & RWLOCK_SEQ_LS ? (newseq).rw_seq : 0, \
127 (f) & RWLOCK_SEQ_U ? (oldseq).ucntval : 0, \
128 (f) & RWLOCK_SEQ_U ? (newseq).ucntval : 0, updateval); }
130 #define RWLOCK_DEBUG_SEQ(m, rwlock, oldseq, newseq, updateval, f)
133 #if !__LITTLE_ENDIAN__
134 #error RWLOCK_GETSEQ_ADDR assumes little endian layout of sequence words
139 RWLOCK_GETSEQ_ADDR(pthread_rwlock_t
*rwlock
, rwlock_seq
**seqaddr
)
141 // 128-bit aligned address inside rw_seq & rw_mis arrays
142 *seqaddr
= (void*)(((uintptr_t)rwlock
->rw_seq
+ 0xful
) & ~0xful
);
147 RWLOCK_GETTID_ADDR(pthread_rwlock_t
*rwlock
, uint64_t **tidaddr
)
149 // 64-bit aligned address inside rw_tid array (&rw_tid[0] for aligned lock)
150 *tidaddr
= (void*)(((uintptr_t)rwlock
->rw_tid
+ 0x7ul
) & ~0x7ul
);
155 rwlock_seq_load(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
156 const rwlock_seqfields seqfields
)
160 #if RWLOCK_USE_INT128
161 oldseqval
->seq_LSU
= seqaddr
->seq_LSU
;
163 oldseqval
->seq_LS
= seqaddr
->seq_LS
;
164 oldseqval
->seq_U
= seqaddr
->seq_U
;
168 oldseqval
->seq_LS
= seqaddr
->seq_LS
;
172 oldseqval
->seq_U
= seqaddr
->seq_U
;
182 rwlock_seq_atomic_load_relaxed(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
183 const rwlock_seqfields seqfields
)
187 #if RWLOCK_USE_INT128
188 #if defined(__arm64__) && defined(__ARM_ARCH_8_2__)
189 // Workaround clang armv81 codegen bug for 128bit os_atomic_load
190 // rdar://problem/31213932
191 oldseqval
->seq_LSU
= seqaddr
->seq_LSU
;
192 while (!os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
,
193 oldseqval
->seq_LSU
, oldseqval
->seq_LSU
, &oldseqval
->seq_LSU
,
196 oldseqval
->seq_LSU
= os_atomic_load_wide(&seqaddr
->atomic_seq_LSU
, relaxed
);
199 oldseqval
->seq_LS
= os_atomic_load_wide(&seqaddr
->atomic_seq_LS
, relaxed
);
200 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
204 oldseqval
->seq_LS
= os_atomic_load_wide(&seqaddr
->atomic_seq_LS
, relaxed
);
208 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
216 #define rwlock_seq_atomic_load(seqaddr, oldseqval, seqfields, m) \
217 rwlock_seq_atomic_load_##m(seqaddr, oldseqval, seqfields)
220 static inline rwlock_seqfields
221 rwlock_seq_atomic_cmpxchgv_relaxed(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
222 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
225 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
229 #if RWLOCK_USE_INT128
230 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
231 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, relaxed
);
232 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
234 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
235 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
237 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
238 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
239 if (!r
) oldseqval
->seq_LS
= newseqval
->seq_LS
;
240 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_LS
;
242 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
247 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
248 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
249 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
253 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
254 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
255 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
260 return updated_seqfields
;
264 static inline rwlock_seqfields
265 rwlock_seq_atomic_cmpxchgv_acquire(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
266 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
269 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
273 #if RWLOCK_USE_INT128
274 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
275 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, acquire
);
276 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
278 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
279 newseqval
->seq_LS
, &oldseqval
->seq_LS
, acquire
);
281 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
282 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
283 if (!r
) oldseqval
->seq_LS
= newseqval
->seq_LS
;
284 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_LS
;
286 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
291 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
292 newseqval
->seq_U
, &oldseqval
->seq_U
, acquire
);
293 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
297 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
298 newseqval
->seq_LS
, &oldseqval
->seq_LS
, acquire
);
299 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
304 return updated_seqfields
;
308 static inline rwlock_seqfields
309 rwlock_seq_atomic_cmpxchgv_release(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
310 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
313 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
316 #if RWLOCK_USE_INT128
317 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
318 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, release
);
319 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
321 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
322 newseqval
->seq_U
, &oldseqval
->seq_U
, release
);
324 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
325 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
326 if (!r
) oldseqval
->seq_U
= newseqval
->seq_U
;
327 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_U
;
329 oldseqval
->seq_LS
= os_atomic_load_wide(&seqaddr
->atomic_seq_LS
,
335 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
336 newseqval
->seq_LS
, &oldseqval
->seq_LS
, release
);
337 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
341 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
342 newseqval
->seq_U
, &oldseqval
->seq_U
, release
);
343 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
349 return updated_seqfields
;
352 #define rwlock_seq_atomic_cmpxchgv(seqaddr, oldseqval, newseqval, seqfields, m)\
353 rwlock_seq_atomic_cmpxchgv_##m(seqaddr, oldseqval, newseqval, seqfields)
355 #ifndef BUILDING_VARIANT /* [ */
358 pthread_rwlockattr_init(pthread_rwlockattr_t
*attr
)
360 attr
->sig
= _PTHREAD_RWLOCK_ATTR_SIG
;
361 attr
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
366 pthread_rwlockattr_destroy(pthread_rwlockattr_t
*attr
)
368 attr
->sig
= _PTHREAD_NO_SIG
;
374 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t
*attr
, int *pshared
)
377 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
378 *pshared
= (int)attr
->pshared
;
385 pthread_rwlockattr_setpshared(pthread_rwlockattr_t
* attr
, int pshared
)
388 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
389 if (( pshared
== PTHREAD_PROCESS_PRIVATE
) ||
390 (pshared
== PTHREAD_PROCESS_SHARED
))
392 attr
->pshared
= pshared
;
399 #endif /* !BUILDING_VARIANT ] */
403 _pthread_rwlock_init(pthread_rwlock_t
*rwlock
, const pthread_rwlockattr_t
*attr
)
406 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
409 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
411 #if PTHREAD_RWLOCK_INIT_UNUSED
412 if ((uint32_t*)tidaddr
!= rwlock
->rw_tid
) {
413 rwlock
->misalign
= 1;
414 __builtin_memset(rwlock
->rw_tid
, 0xff, sizeof(rwlock
->rw_tid
));
416 if ((uint32_t*)seqaddr
!= rwlock
->rw_seq
) {
417 __builtin_memset(rwlock
->rw_seq
, 0xff, sizeof(rwlock
->rw_seq
));
419 __builtin_memset(rwlock
->rw_mis
, 0xff, sizeof(rwlock
->rw_mis
));
420 #endif // PTHREAD_MUTEX_INIT_UNUSED
422 *seqaddr
= (rwlock_seq
){
423 .lcntval
= PTHRW_RWLOCK_INIT
,
424 .rw_seq
= PTHRW_RWS_INIT
,
428 if (attr
!= NULL
&& attr
->pshared
== PTHREAD_PROCESS_SHARED
) {
429 rwlock
->pshared
= PTHREAD_PROCESS_SHARED
;
430 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_SHARED
;
432 rwlock
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
433 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_PRIVATE
;
436 long sig
= _PTHREAD_RWLOCK_SIG
;
439 bzero(rwlock
->_reserved
, sizeof(rwlock
->_reserved
));
441 #if PTHREAD_RWLOCK_INIT_UNUSED
442 // For detecting copied rwlocks and smashes during debugging
443 uint32_t sig32
= (uint32_t)sig
;
444 uintptr_t guard
= ~(uintptr_t)rwlock
; // use ~ to hide from leaks
445 __builtin_memcpy(rwlock
->_reserved
, &guard
, sizeof(guard
));
446 #define countof(x) (sizeof(x) / sizeof(x[0]))
447 rwlock
->_reserved
[countof(rwlock
->_reserved
) - 1] = sig32
;
448 #if defined(__LP64__)
449 rwlock
->_pad
= sig32
;
451 #endif // PTHREAD_RWLOCK_INIT_UNUSED
453 // Ensure all contents are properly set before setting signature.
454 #if defined(__LP64__)
455 // For binary compatibility reasons we cannot require natural alignment of
456 // the 64bit 'sig' long value in the struct. rdar://problem/21610439
457 uint32_t *sig32_ptr
= (uint32_t*)&rwlock
->sig
;
458 uint32_t *sig32_val
= (uint32_t*)&sig
;
459 *(sig32_ptr
+ 1) = *(sig32_val
+ 1);
460 os_atomic_store(sig32_ptr
, *sig32_val
, release
);
462 os_atomic_store(&rwlock
->sig
, sig
, release
);
469 _pthread_rwlock_modbits(uint32_t lgenval
, uint32_t updateval
, uint32_t savebits
)
471 uint32_t lval
= lgenval
& PTHRW_BIT_MASK
;
472 uint32_t uval
= updateval
& PTHRW_BIT_MASK
;
473 uint32_t rval
, nlval
;
475 nlval
= (lval
| uval
) & ~(PTH_RWL_MBIT
);
477 // reconcile bits on the lock with what kernel needs to set
478 if ((uval
& PTH_RWL_KBIT
) == 0 && (lval
& PTH_RWL_WBIT
) == 0) {
479 nlval
&= ~PTH_RWL_KBIT
;
483 if ((savebits
& PTH_RWS_WSVBIT
) != 0 && (nlval
& PTH_RWL_WBIT
) == 0 &&
484 (nlval
& PTH_RWL_EBIT
) == 0) {
485 nlval
|= (PTH_RWL_WBIT
| PTH_RWL_KBIT
);
488 rval
= (lgenval
& PTHRW_COUNT_MASK
) | nlval
;
494 _pthread_rwlock_updateval(pthread_rwlock_t
*rwlock
, uint32_t updateval
)
496 bool isoverlap
= (updateval
& PTH_RWL_MBIT
) != 0;
498 // TBD: restore U bit
500 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
502 rwlock_seq oldseq
, newseq
;
503 rwlock_seq_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LS
);
506 if (isoverlap
|| is_rws_unlockinit_set(oldseq
.rw_seq
)) {
507 // Set S word to the specified value
508 uint32_t savebits
= (oldseq
.rw_seq
& PTHRW_RWS_SAVEMASK
);
509 newseq
.lcntval
= _pthread_rwlock_modbits(oldseq
.lcntval
, updateval
,
511 newseq
.rw_seq
+= (updateval
& PTHRW_COUNT_MASK
);
513 newseq
.rw_seq
&= PTHRW_COUNT_MASK
;
515 newseq
.rw_seq
&= ~PTHRW_RWS_SAVEMASK
;
517 } while (!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
518 RWLOCK_SEQ_LS
, relaxed
));
519 RWLOCK_DEBUG_SEQ(update
, rwlock
, oldseq
, newseq
, updateval
, RWLOCK_SEQ_LS
);
524 _pthread_rwlock_check_busy(pthread_rwlock_t
*rwlock
)
529 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
532 rwlock_seq_atomic_load(seqaddr
, &seq
, RWLOCK_SEQ_LSU
, relaxed
);
533 if ((seq
.lcntval
& PTHRW_COUNT_MASK
) != seq
.ucntval
) {
540 PTHREAD_NOEXPORT_VARIANT
542 pthread_rwlock_destroy(pthread_rwlock_t
*rwlock
)
546 _pthread_lock_lock(&rwlock
->lock
);
547 if (_pthread_rwlock_check_signature(rwlock
)) {
548 res
= _pthread_rwlock_check_busy(rwlock
);
549 } else if (!_pthread_rwlock_check_signature_init(rwlock
)) {
553 rwlock
->sig
= _PTHREAD_NO_SIG
;
555 _pthread_lock_unlock(&rwlock
->lock
);
559 PTHREAD_NOEXPORT_VARIANT
561 pthread_rwlock_init(pthread_rwlock_t
*rwlock
, const pthread_rwlockattr_t
*attr
)
565 if (attr
&& attr
->sig
!= _PTHREAD_RWLOCK_ATTR_SIG
) {
569 if (res
== 0 && _pthread_rwlock_check_signature(rwlock
)) {
570 res
= _pthread_rwlock_check_busy(rwlock
);
573 _pthread_lock_init(&rwlock
->lock
);
574 res
= _pthread_rwlock_init(rwlock
, attr
);
581 _pthread_rwlock_check_init_slow(pthread_rwlock_t
*rwlock
)
585 if (_pthread_rwlock_check_signature_init(rwlock
)) {
586 _pthread_lock_lock(&rwlock
->lock
);
587 if (_pthread_rwlock_check_signature_init(rwlock
)) {
588 res
= _pthread_rwlock_init(rwlock
, NULL
);
589 } else if (_pthread_rwlock_check_signature(rwlock
)){
592 _pthread_lock_unlock(&rwlock
->lock
);
593 } else if (_pthread_rwlock_check_signature(rwlock
)){
597 PLOCKSTAT_RW_ERROR(rwlock
, READ_LOCK_PLOCKSTAT
, res
);
604 _pthread_rwlock_check_init(pthread_rwlock_t
*rwlock
)
608 if (!_pthread_rwlock_check_signature(rwlock
)) {
609 return _pthread_rwlock_check_init_slow(rwlock
);
616 _pthread_rwlock_lock_wait(pthread_rwlock_t
*rwlock
, bool readlock
,
622 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
626 RWLOCK_DEBUG_SEQ(rdlock
, rwlock
, oldseq
, newseq
, gotlock
,
629 RWLOCK_DEBUG_SEQ(wrlock
, rwlock
, oldseq
, newseq
, gotlock
,
635 PLOCKSTAT_RW_BLOCK(rwlock
, plockstat
);
639 updateval
= __psynch_rw_rdlock(rwlock
, newseq
.lcntval
,
640 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
642 updateval
= __psynch_rw_wrlock(rwlock
, newseq
.lcntval
,
643 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
645 if (updateval
== (uint32_t)-1) {
650 } while (res
== EINTR
);
653 _pthread_rwlock_updateval(rwlock
, updateval
);
654 PLOCKSTAT_RW_BLOCKED(rwlock
, plockstat
, BLOCK_SUCCESS_PLOCKSTAT
);
656 PLOCKSTAT_RW_BLOCKED(rwlock
, plockstat
, BLOCK_FAIL_PLOCKSTAT
);
657 PTHREAD_INTERNAL_CRASH(res
, "kernel rwlock returned unknown error");
665 _pthread_rwlock_lock_slow(pthread_rwlock_t
*rwlock
, bool readlock
,
671 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
674 res
= _pthread_rwlock_check_init(rwlock
);
675 if (res
!= 0) return res
;
678 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
680 rwlock_seq oldseq
, newseq
;
681 rwlock_seq_atomic_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LSU
, relaxed
);
684 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
685 uint64_t selfid
= _pthread_threadid_self_np_direct();
686 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
687 if (os_atomic_load_wide(tidaddr
, relaxed
) == selfid
) return EDEADLK
;
697 // if W and K bit are clear or U bit is on, acquire lock in userland
699 gotlock
= (oldseq
.lcntval
& (PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0;
701 gotlock
= (oldseq
.lcntval
& PTH_RWL_UBIT
) != 0;
704 if (trylock
&& !gotlock
) {
705 // A trylock on a held lock will fail immediately. But since
706 // we did not load the sequence words atomically, perform a
707 // no-op CAS to ensure that nobody has unlocked concurrently.
708 } else if (gotlock
) {
710 if (diff_genseq(oldseq
.lcntval
, oldseq
.ucntval
) >=
712 // since ucntval may be newer, just redo
714 if (retry_count
> 1024) {
720 rwlock_seq_atomic_load(seqaddr
, &oldseq
,
721 RWLOCK_SEQ_LSU
, relaxed
);
725 // Need to update L (remove U bit) and S word
726 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
728 newseq
.lcntval
&= PTHRW_COUNT_MASK
;
729 newseq
.lcntval
|= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
731 newseq
.lcntval
+= PTHRW_INC
;
732 newseq
.rw_seq
+= PTHRW_INC
;
735 // Need to block in kernel. Remove U bit.
736 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
738 newseq
.lcntval
|= PTH_RWL_KBIT
| PTH_RWL_WBIT
;
740 newseq
.lcntval
+= PTHRW_INC
;
741 if (is_rws_sbit_set(oldseq
.rw_seq
)) {
742 // Clear the S bit and set S to L
743 newseq
.rw_seq
&= (PTHRW_BIT_MASK
& ~PTH_RWS_SBIT
);
744 newseq
.rw_seq
|= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
747 } while (!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
748 RWLOCK_SEQ_LS
, acquire
));
751 if (!readlock
) os_atomic_store_wide(tidaddr
, selfid
, relaxed
);
753 } else if (trylock
) {
756 res
= _pthread_rwlock_lock_wait(rwlock
, readlock
, newseq
);
762 PLOCKSTAT_RW_ACQUIRE(rwlock
, plockstat
);
764 PLOCKSTAT_RW_ERROR(rwlock
, plockstat
, res
);
773 _pthread_rwlock_lock(pthread_rwlock_t
*rwlock
, bool readlock
, bool trylock
)
776 if (PLOCKSTAT_RW_ACQUIRE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) {
777 return _pthread_rwlock_lock_slow(rwlock
, readlock
, trylock
);
781 if (os_unlikely(!_pthread_rwlock_check_signature(rwlock
))) {
782 return _pthread_rwlock_lock_slow(rwlock
, readlock
, trylock
);
786 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
788 rwlock_seq oldseq
, newseq
;
789 // no need to perform a single-copy-atomic 128-bit load in the fastpath,
790 // if stores to L and U are seen out of order, we will fallback to the
791 // slowpath below (which has rwlock_seq_atomic_load)
792 rwlock_seq_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LSU
);
794 if (os_unlikely(is_rwl_ebit_set(oldseq
.lcntval
))) {
795 return _pthread_rwlock_lock_slow(rwlock
, readlock
, trylock
);
802 // if W and K bit are clear or U bit is on, acquire lock in userland
804 gotlock
= (oldseq
.lcntval
& (PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0;
806 gotlock
= (oldseq
.lcntval
& PTH_RWL_UBIT
) != 0;
809 if (trylock
&& !gotlock
) {
810 // A trylock on a held lock will fail immediately. But since
811 // we did not load the sequence words atomically, perform a
812 // no-op CAS to ensure that nobody has unlocked concurrently.
813 } else if (os_likely(gotlock
)) {
815 if (os_unlikely(diff_genseq(oldseq
.lcntval
, oldseq
.ucntval
) >=
816 PTHRW_MAX_READERS
)) {
817 return _pthread_rwlock_lock_slow(rwlock
, readlock
, trylock
);
819 // Need to update L (remove U bit) and S word
820 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
822 newseq
.lcntval
&= PTHRW_COUNT_MASK
;
823 newseq
.lcntval
|= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
825 newseq
.lcntval
+= PTHRW_INC
;
826 newseq
.rw_seq
+= PTHRW_INC
;
828 return _pthread_rwlock_lock_slow(rwlock
, readlock
, trylock
);
830 } while (os_unlikely(!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
831 RWLOCK_SEQ_LS
, acquire
)));
833 if (os_likely(gotlock
)) {
836 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
837 uint64_t selfid
= _pthread_threadid_self_np_direct();
838 os_atomic_store_wide(tidaddr
, selfid
, relaxed
);
841 } else if (trylock
) {
848 PTHREAD_NOEXPORT_VARIANT
850 pthread_rwlock_rdlock(pthread_rwlock_t
*rwlock
)
853 return _pthread_rwlock_lock(rwlock
, true, false);
856 PTHREAD_NOEXPORT_VARIANT
858 pthread_rwlock_tryrdlock(pthread_rwlock_t
*rwlock
)
860 // read lock, try lock
861 return _pthread_rwlock_lock(rwlock
, true, true);
864 PTHREAD_NOEXPORT_VARIANT
866 pthread_rwlock_wrlock(pthread_rwlock_t
*rwlock
)
868 // write lock, no try
869 return _pthread_rwlock_lock(rwlock
, false, false);
872 PTHREAD_NOEXPORT_VARIANT
874 pthread_rwlock_trywrlock(pthread_rwlock_t
*rwlock
)
876 // write lock, try lock
877 return _pthread_rwlock_lock(rwlock
, false, true);
882 _pthread_rwlock_unlock_drop(pthread_rwlock_t
*rwlock
, rwlock_seq oldseq
,
887 RWLOCK_DEBUG_SEQ(unlock
, rwlock
, oldseq
, newseq
, !droplock
, RWLOCK_SEQ_LSU
);
890 updateval
= __psynch_rw_unlock(rwlock
, oldseq
.lcntval
,
891 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
892 if (updateval
== (uint32_t)-1) {
896 RWLOCK_DEBUG_SEQ(wakeup
, rwlock
, oldseq
, newseq
, updateval
,
899 } while (res
== EINTR
);
902 PTHREAD_INTERNAL_CRASH(res
, "kernel rwunlock returned unknown error");
910 _pthread_rwlock_unlock_slow(pthread_rwlock_t
*rwlock
,
911 rwlock_seqfields updated_seqfields
)
914 rwlock_seqfields seqfields
= RWLOCK_SEQ_LSU
;
919 res
= _pthread_rwlock_check_init(rwlock
);
920 if (res
!= 0) return res
;
923 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
925 rwlock_seq oldseq
, newseq
;
926 rwlock_seq_load(seqaddr
, &oldseq
, seqfields
);
928 if ((oldseq
.lcntval
& PTH_RWL_UBIT
) != 0) {
929 // spurious unlock (unlock of unlocked lock)
933 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
938 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
939 os_atomic_store_wide(tidaddr
, 0, relaxed
);
944 // stop loading & updating fields that have successfully been stored
945 seqfields
&= ~updated_seqfields
;
948 if (seqfields
& RWLOCK_SEQ_U
) {
949 newseq
.ucntval
+= PTHRW_INC
;
953 uint32_t oldlcnt
= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
954 if (newseq
.ucntval
== oldlcnt
) {
955 // last unlock, set L with U and init bits and set S to L with S bit
956 newseq
.lcntval
= oldlcnt
| PTHRW_RWLOCK_INIT
;
957 newseq
.rw_seq
= oldlcnt
| PTHRW_RWS_INIT
;
959 // no L/S update if lock is not exclusive or no writer pending
960 if ((oldseq
.lcntval
&
961 (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0) {
965 // kernel transition only needed if U == S
966 if (newseq
.ucntval
!= (oldseq
.rw_seq
& PTHRW_COUNT_MASK
)) {
971 // reset all bits and set K
972 newseq
.lcntval
= oldlcnt
| PTH_RWL_KBIT
;
973 // set I bit on S word
974 newseq
.rw_seq
|= PTH_RWS_IBIT
;
975 if ((oldseq
.lcntval
& PTH_RWL_WBIT
) != 0) {
976 newseq
.rw_seq
|= PTH_RWS_WSVBIT
;
979 } while (seqfields
!= (updated_seqfields
= rwlock_seq_atomic_cmpxchgv(
980 seqaddr
, &oldseq
, &newseq
, seqfields
, release
)));
983 res
= _pthread_rwlock_unlock_drop(rwlock
, oldseq
, newseq
);
986 PLOCKSTAT_RW_RELEASE(rwlock
, wrlock
);
991 PTHREAD_NOEXPORT_VARIANT
993 pthread_rwlock_unlock(pthread_rwlock_t
*rwlock
)
995 rwlock_seqfields seqfields
= RWLOCK_SEQ_LSU
;
996 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
999 if (PLOCKSTAT_RW_RELEASE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) {
1000 return _pthread_rwlock_unlock_slow(rwlock
, updated_seqfields
);
1004 if (os_unlikely(!_pthread_rwlock_check_signature(rwlock
))) {
1005 return _pthread_rwlock_unlock_slow(rwlock
, updated_seqfields
);
1008 rwlock_seq
*seqaddr
;
1009 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
1011 rwlock_seq oldseq
, newseq
;
1012 rwlock_seq_load(seqaddr
, &oldseq
, seqfields
);
1014 if (os_unlikely(oldseq
.lcntval
& PTH_RWL_UBIT
)) {
1015 // spurious unlock (unlock of unlocked lock)
1019 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
1021 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
1022 os_atomic_store_wide(tidaddr
, 0, relaxed
);
1026 if (updated_seqfields
) {
1027 return _pthread_rwlock_unlock_slow(rwlock
, updated_seqfields
);
1031 if (seqfields
& RWLOCK_SEQ_U
) {
1032 newseq
.ucntval
+= PTHRW_INC
;
1035 uint32_t oldlcnt
= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
1036 if (os_likely(newseq
.ucntval
== oldlcnt
)) {
1037 // last unlock, set L with U and init bits and set S to L with S bit
1038 newseq
.lcntval
= oldlcnt
| PTHRW_RWLOCK_INIT
;
1039 newseq
.rw_seq
= oldlcnt
| PTHRW_RWS_INIT
;
1041 if (os_likely((oldseq
.lcntval
&
1042 (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0 ||
1043 newseq
.ucntval
!= (oldseq
.rw_seq
& PTHRW_COUNT_MASK
))) {
1044 // no L/S update if lock is not exclusive or no writer pending
1045 // kernel transition only needed if U == S
1047 return _pthread_rwlock_unlock_slow(rwlock
, updated_seqfields
);
1050 } while (os_unlikely(seqfields
!= (updated_seqfields
=
1051 rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
, seqfields
,