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
64 extern int __unix_conforming
;
67 #include "plockstat.h"
68 #else /* !PLOCKSTAT */
69 #define PLOCKSTAT_RW_ERROR(x, y, z)
70 #define PLOCKSTAT_RW_BLOCK(x, y)
71 #define PLOCKSTAT_RW_BLOCKED(x, y, z)
72 #define PLOCKSTAT_RW_ACQUIRE(x, y)
73 #define PLOCKSTAT_RW_RELEASE(x, y)
74 #endif /* PLOCKSTAT */
76 #define READ_LOCK_PLOCKSTAT 0
77 #define WRITE_LOCK_PLOCKSTAT 1
79 #define BLOCK_FAIL_PLOCKSTAT 0
80 #define BLOCK_SUCCESS_PLOCKSTAT 1
82 #define PTHREAD_RWLOCK_INIT_UNUSED 1
84 // maximum number of times a read lock may be obtained
85 #define MAX_READ_LOCKS (INT_MAX - 1)
87 union rwlock_seq
; // forward declaration
88 enum rwlock_seqfields
; // forward declaration
90 PTHREAD_NOEXPORT PTHREAD_WEAK
// prevent inlining of return value into callers
91 int _pthread_rwlock_lock_slow(pthread_rwlock_t
*orwlock
, bool readlock
,
94 PTHREAD_NOEXPORT PTHREAD_WEAK
// prevent inlining of return value into callers
95 int _pthread_rwlock_unlock_slow(pthread_rwlock_t
*orwlock
,
96 enum rwlock_seqfields updated_seqfields
);
100 #define RWLOCK_USE_INT128 1
103 typedef union rwlock_seq
{
105 struct { uint32_t lcntval
; uint32_t rw_seq
; uint32_t ucntval
; };
106 struct { uint32_t lgen
; uint32_t rw_wc
; uint32_t ugen
; };
107 #if RWLOCK_USE_INT128
108 unsigned __int128 seq_LSU
;
109 unsigned __int128 _Atomic atomic_seq_LSU
;
117 uint64_t _Atomic atomic_seq_LS
;
118 uint32_t _Atomic atomic_seq_U
;
119 uint32_t _Atomic _atomic_pad
;
123 _Static_assert(sizeof(rwlock_seq
) == 4 * sizeof(uint32_t),
124 "Incorrect rwlock_seq size");
126 typedef enum rwlock_seqfields
{
130 RWLOCK_SEQ_LSU
= RWLOCK_SEQ_LS
| RWLOCK_SEQ_U
,
133 #if PTHREAD_DEBUG_LOG
134 #define RWLOCK_DEBUG_SEQ(op, rwlock, oldseq, newseq, updateval, f) \
135 if (_pthread_debuglog >= 0) { \
136 _simple_dprintf(_pthread_debuglog, "rw_" #op " %p tck %7llu thr %llx " \
137 "L %x -> %x S %x -> %x U %x -> %x updt %x\n", rwlock, \
138 mach_absolute_time() - _pthread_debugstart, _pthread_selfid_direct(), \
139 (f) & RWLOCK_SEQ_LS ? (oldseq).lcntval : 0, \
140 (f) & RWLOCK_SEQ_LS ? (newseq).lcntval : 0, \
141 (f) & RWLOCK_SEQ_LS ? (oldseq).rw_seq : 0, \
142 (f) & RWLOCK_SEQ_LS ? (newseq).rw_seq : 0, \
143 (f) & RWLOCK_SEQ_U ? (oldseq).ucntval : 0, \
144 (f) & RWLOCK_SEQ_U ? (newseq).ucntval : 0, updateval); }
146 #define RWLOCK_DEBUG_SEQ(m, rwlock, oldseq, newseq, updateval, f)
149 #if !__LITTLE_ENDIAN__
150 #error RWLOCK_GETSEQ_ADDR assumes little endian layout of sequence words
153 PTHREAD_ALWAYS_INLINE
155 RWLOCK_GETSEQ_ADDR(_pthread_rwlock
*rwlock
, rwlock_seq
**seqaddr
)
157 // 128-bit aligned address inside rw_seq & rw_mis arrays
158 *seqaddr
= (void*)(((uintptr_t)rwlock
->rw_seq
+ 0xful
) & ~0xful
);
161 PTHREAD_ALWAYS_INLINE
163 RWLOCK_GETTID_ADDR(_pthread_rwlock
*rwlock
, uint64_t **tidaddr
)
165 // 64-bit aligned address inside rw_tid array (&rw_tid[0] for aligned lock)
166 *tidaddr
= (void*)(((uintptr_t)rwlock
->rw_tid
+ 0x7ul
) & ~0x7ul
);
169 PTHREAD_ALWAYS_INLINE
171 rwlock_seq_load(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
172 const rwlock_seqfields seqfields
)
176 #if RWLOCK_USE_INT128
177 oldseqval
->seq_LSU
= seqaddr
->seq_LSU
;
179 oldseqval
->seq_LS
= seqaddr
->seq_LS
;
180 oldseqval
->seq_U
= seqaddr
->seq_U
;
184 oldseqval
->seq_LS
= seqaddr
->seq_LS
;
188 oldseqval
->seq_U
= seqaddr
->seq_U
;
196 PTHREAD_ALWAYS_INLINE
198 rwlock_seq_atomic_load_relaxed(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
199 const rwlock_seqfields seqfields
)
203 #if RWLOCK_USE_INT128
204 oldseqval
->seq_LSU
= os_atomic_load(&seqaddr
->atomic_seq_LSU
, relaxed
);
206 oldseqval
->seq_LS
= os_atomic_load(&seqaddr
->atomic_seq_LS
, relaxed
);
207 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
211 oldseqval
->seq_LS
= os_atomic_load(&seqaddr
->atomic_seq_LS
, relaxed
);
215 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
223 #define rwlock_seq_atomic_load(seqaddr, oldseqval, seqfields, m) \
224 rwlock_seq_atomic_load_##m(seqaddr, oldseqval, seqfields)
226 PTHREAD_ALWAYS_INLINE
227 static inline rwlock_seqfields
228 rwlock_seq_atomic_cmpxchgv_relaxed(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
229 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
232 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
236 #if RWLOCK_USE_INT128
237 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
238 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, relaxed
);
239 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
241 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
242 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
244 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
245 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
246 if (!r
) oldseqval
->seq_LS
= newseqval
->seq_LS
;
247 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_LS
;
249 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
254 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
255 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
256 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
260 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
261 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
262 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
267 return updated_seqfields
;
270 PTHREAD_ALWAYS_INLINE
271 static inline rwlock_seqfields
272 rwlock_seq_atomic_cmpxchgv_acquire(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
273 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
276 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
280 #if RWLOCK_USE_INT128
281 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
282 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, acquire
);
283 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
285 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
286 newseqval
->seq_LS
, &oldseqval
->seq_LS
, acquire
);
288 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
289 newseqval
->seq_U
, &oldseqval
->seq_U
, relaxed
);
290 if (!r
) oldseqval
->seq_LS
= newseqval
->seq_LS
;
291 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_LS
;
293 oldseqval
->seq_U
= os_atomic_load(&seqaddr
->atomic_seq_U
, relaxed
);
298 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
299 newseqval
->seq_U
, &oldseqval
->seq_U
, acquire
);
300 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
304 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
305 newseqval
->seq_LS
, &oldseqval
->seq_LS
, acquire
);
306 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
311 return updated_seqfields
;
314 PTHREAD_ALWAYS_INLINE
315 static inline rwlock_seqfields
316 rwlock_seq_atomic_cmpxchgv_release(rwlock_seq
*seqaddr
, rwlock_seq
*oldseqval
,
317 rwlock_seq
*newseqval
, const rwlock_seqfields seqfields
)
320 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
323 #if RWLOCK_USE_INT128
324 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LSU
, oldseqval
->seq_LSU
,
325 newseqval
->seq_LSU
, &oldseqval
->seq_LSU
, release
);
326 if (r
) updated_seqfields
= RWLOCK_SEQ_LSU
;
328 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
329 newseqval
->seq_U
, &oldseqval
->seq_U
, release
);
331 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
332 newseqval
->seq_LS
, &oldseqval
->seq_LS
, relaxed
);
333 if (!r
) oldseqval
->seq_U
= newseqval
->seq_U
;
334 updated_seqfields
= r
? RWLOCK_SEQ_LSU
: RWLOCK_SEQ_U
;
336 oldseqval
->seq_LS
= os_atomic_load(&seqaddr
->atomic_seq_LS
,relaxed
);
341 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_LS
, oldseqval
->seq_LS
,
342 newseqval
->seq_LS
, &oldseqval
->seq_LS
, release
);
343 if (r
) updated_seqfields
= RWLOCK_SEQ_LS
;
347 r
= os_atomic_cmpxchgv(&seqaddr
->atomic_seq_U
, oldseqval
->seq_U
,
348 newseqval
->seq_U
, &oldseqval
->seq_U
, release
);
349 if (r
) updated_seqfields
= RWLOCK_SEQ_U
;
355 return updated_seqfields
;
358 #define rwlock_seq_atomic_cmpxchgv(seqaddr, oldseqval, newseqval, seqfields, m)\
359 rwlock_seq_atomic_cmpxchgv_##m(seqaddr, oldseqval, newseqval, seqfields)
361 #ifndef BUILDING_VARIANT /* [ */
364 pthread_rwlockattr_init(pthread_rwlockattr_t
*attr
)
366 attr
->sig
= _PTHREAD_RWLOCK_ATTR_SIG
;
367 attr
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
372 pthread_rwlockattr_destroy(pthread_rwlockattr_t
*attr
)
374 attr
->sig
= _PTHREAD_NO_SIG
;
380 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t
*attr
, int *pshared
)
383 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
384 *pshared
= (int)attr
->pshared
;
391 pthread_rwlockattr_setpshared(pthread_rwlockattr_t
* attr
, int pshared
)
394 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
396 if (( pshared
== PTHREAD_PROCESS_PRIVATE
) ||
397 (pshared
== PTHREAD_PROCESS_SHARED
))
398 #else /* __DARWIN_UNIX03 */
399 if ( pshared
== PTHREAD_PROCESS_PRIVATE
)
400 #endif /* __DARWIN_UNIX03 */
402 attr
->pshared
= pshared
;
409 #endif /* !BUILDING_VARIANT ] */
411 PTHREAD_ALWAYS_INLINE
413 _pthread_rwlock_init(_pthread_rwlock
*rwlock
, const pthread_rwlockattr_t
*attr
)
416 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
419 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
421 #if PTHREAD_RWLOCK_INIT_UNUSED
422 if ((uint32_t*)tidaddr
!= rwlock
->rw_tid
) {
423 rwlock
->misalign
= 1;
424 __builtin_memset(rwlock
->rw_tid
, 0xff, sizeof(rwlock
->rw_tid
));
426 if ((uint32_t*)seqaddr
!= rwlock
->rw_seq
) {
427 __builtin_memset(rwlock
->rw_seq
, 0xff, sizeof(rwlock
->rw_seq
));
429 __builtin_memset(rwlock
->rw_mis
, 0xff, sizeof(rwlock
->rw_mis
));
430 #endif // PTHREAD_MUTEX_INIT_UNUSED
432 *seqaddr
= (rwlock_seq
){
433 .lcntval
= PTHRW_RWLOCK_INIT
,
434 .rw_seq
= PTHRW_RWS_INIT
,
438 if (attr
!= NULL
&& attr
->pshared
== PTHREAD_PROCESS_SHARED
) {
439 rwlock
->pshared
= PTHREAD_PROCESS_SHARED
;
440 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_SHARED
;
442 rwlock
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
443 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_PRIVATE
;
446 long sig
= _PTHREAD_RWLOCK_SIG
;
449 bzero(rwlock
->_reserved
, sizeof(rwlock
->_reserved
));
451 #if PTHREAD_RWLOCK_INIT_UNUSED
452 // For detecting copied rwlocks and smashes during debugging
453 uint32_t sig32
= (uint32_t)sig
;
454 uintptr_t guard
= ~(uintptr_t)rwlock
; // use ~ to hide from leaks
455 __builtin_memcpy(rwlock
->_reserved
, &guard
, sizeof(guard
));
456 #define countof(x) (sizeof(x) / sizeof(x[0]))
457 rwlock
->_reserved
[countof(rwlock
->_reserved
) - 1] = sig32
;
458 #if defined(__LP64__)
459 rwlock
->_pad
= sig32
;
461 #endif // PTHREAD_RWLOCK_INIT_UNUSED
463 // Ensure all contents are properly set before setting signature.
464 #if defined(__LP64__)
465 // For binary compatibility reasons we cannot require natural alignment of
466 // the 64bit 'sig' long value in the struct. rdar://problem/21610439
467 uint32_t *sig32_ptr
= (uint32_t*)&rwlock
->sig
;
468 uint32_t *sig32_val
= (uint32_t*)&sig
;
469 *(sig32_ptr
+ 1) = *(sig32_val
+ 1);
470 os_atomic_store(sig32_ptr
, *sig32_val
, release
);
472 os_atomic_store2o(rwlock
, sig
, sig
, release
);
479 _pthread_rwlock_modbits(uint32_t lgenval
, uint32_t updateval
, uint32_t savebits
)
481 uint32_t lval
= lgenval
& PTHRW_BIT_MASK
;
482 uint32_t uval
= updateval
& PTHRW_BIT_MASK
;
483 uint32_t rval
, nlval
;
485 nlval
= (lval
| uval
) & ~(PTH_RWL_MBIT
);
487 // reconcile bits on the lock with what kernel needs to set
488 if ((uval
& PTH_RWL_KBIT
) == 0 && (lval
& PTH_RWL_WBIT
) == 0) {
489 nlval
&= ~PTH_RWL_KBIT
;
493 if ((savebits
& PTH_RWS_WSVBIT
) != 0 && (nlval
& PTH_RWL_WBIT
) == 0 &&
494 (nlval
& PTH_RWL_EBIT
) == 0) {
495 nlval
|= (PTH_RWL_WBIT
| PTH_RWL_KBIT
);
498 rval
= (lgenval
& PTHRW_COUNT_MASK
) | nlval
;
502 PTHREAD_ALWAYS_INLINE
504 _pthread_rwlock_updateval(_pthread_rwlock
*rwlock
, uint32_t updateval
)
506 bool isoverlap
= (updateval
& PTH_RWL_MBIT
) != 0;
508 // TBD: restore U bit
510 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
512 rwlock_seq oldseq
, newseq
;
513 rwlock_seq_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LS
);
516 if (isoverlap
|| is_rws_setunlockinit(oldseq
.rw_seq
) != 0) {
517 // Set S word to the specified value
518 uint32_t savebits
= (oldseq
.rw_seq
& PTHRW_RWS_SAVEMASK
);
519 newseq
.lcntval
= _pthread_rwlock_modbits(oldseq
.lcntval
, updateval
,
521 newseq
.rw_seq
+= (updateval
& PTHRW_COUNT_MASK
);
523 newseq
.rw_seq
&= PTHRW_COUNT_MASK
;
525 newseq
.rw_seq
&= ~PTHRW_RWS_SAVEMASK
;
527 } while (!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
528 RWLOCK_SEQ_LS
, relaxed
));
529 RWLOCK_DEBUG_SEQ(update
, rwlock
, oldseq
, newseq
, updateval
, RWLOCK_SEQ_LS
);
533 PTHREAD_ALWAYS_INLINE
535 _pthread_rwlock_check_busy(_pthread_rwlock
*rwlock
)
540 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
543 rwlock_seq_atomic_load(seqaddr
, &seq
, RWLOCK_SEQ_LSU
, relaxed
);
544 if ((seq
.lcntval
& PTHRW_COUNT_MASK
) != seq
.ucntval
) {
550 #endif /* __DARWIN_UNIX03 */
552 PTHREAD_NOEXPORT_VARIANT
554 pthread_rwlock_destroy(pthread_rwlock_t
*orwlock
)
557 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
559 _PTHREAD_LOCK(rwlock
->lock
);
560 if (_pthread_rwlock_check_signature(rwlock
)) {
562 res
= _pthread_rwlock_check_busy(rwlock
);
563 #endif /* __DARWIN_UNIX03 */
564 } else if (!_pthread_rwlock_check_signature_init(rwlock
)) {
568 rwlock
->sig
= _PTHREAD_NO_SIG
;
570 _PTHREAD_UNLOCK(rwlock
->lock
);
574 PTHREAD_NOEXPORT_VARIANT
576 pthread_rwlock_init(pthread_rwlock_t
*orwlock
, const pthread_rwlockattr_t
*attr
)
579 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
582 if (attr
&& attr
->sig
!= _PTHREAD_RWLOCK_ATTR_SIG
) {
586 if (res
== 0 && _pthread_rwlock_check_signature(rwlock
)) {
587 res
= _pthread_rwlock_check_busy(rwlock
);
591 _PTHREAD_LOCK_INIT(rwlock
->lock
);
592 res
= _pthread_rwlock_init(rwlock
, attr
);
599 _pthread_rwlock_check_init_slow(pthread_rwlock_t
*orwlock
)
602 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
604 if (_pthread_rwlock_check_signature_init(rwlock
)) {
605 _PTHREAD_LOCK(rwlock
->lock
);
606 if (_pthread_rwlock_check_signature_init(rwlock
)) {
607 res
= _pthread_rwlock_init(rwlock
, NULL
);
608 } else if (_pthread_rwlock_check_signature(rwlock
)){
611 _PTHREAD_UNLOCK(rwlock
->lock
);
612 } else if (_pthread_rwlock_check_signature(rwlock
)){
616 PLOCKSTAT_RW_ERROR(orwlock
, READ_LOCK_PLOCKSTAT
, res
);
621 PTHREAD_ALWAYS_INLINE
623 _pthread_rwlock_check_init(pthread_rwlock_t
*orwlock
)
626 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
628 if (!_pthread_rwlock_check_signature(rwlock
)) {
629 return _pthread_rwlock_check_init_slow(orwlock
);
636 _pthread_rwlock_lock_wait(pthread_rwlock_t
*orwlock
, bool readlock
,
640 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
643 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
647 RWLOCK_DEBUG_SEQ(rdlock
, rwlock
, oldseq
, newseq
, gotlock
,
650 RWLOCK_DEBUG_SEQ(wrlock
, rwlock
, oldseq
, newseq
, gotlock
,
656 PLOCKSTAT_RW_BLOCK(orwlock
, plockstat
);
660 updateval
= __psynch_rw_rdlock(orwlock
, newseq
.lcntval
,
661 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
663 updateval
= __psynch_rw_wrlock(orwlock
, newseq
.lcntval
,
664 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
666 if (updateval
== (uint32_t)-1) {
671 } while (res
== EINTR
);
674 _pthread_rwlock_updateval(rwlock
, updateval
);
675 PLOCKSTAT_RW_BLOCKED(orwlock
, plockstat
, BLOCK_SUCCESS_PLOCKSTAT
);
677 PLOCKSTAT_RW_BLOCKED(orwlock
, plockstat
, BLOCK_FAIL_PLOCKSTAT
);
678 PTHREAD_ABORT("kernel rwlock returned unknown error %x: "
679 "tid %llx\n", res
, _pthread_selfid_direct());
685 PTHREAD_NOEXPORT PTHREAD_NOINLINE
687 _pthread_rwlock_lock_slow(pthread_rwlock_t
*orwlock
, bool readlock
,
691 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
694 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
697 res
= _pthread_rwlock_check_init(orwlock
);
698 if (res
!= 0) return res
;
701 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
703 rwlock_seq oldseq
, newseq
;
704 rwlock_seq_atomic_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LSU
, relaxed
);
708 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
709 uint64_t selfid
= _pthread_selfid_direct();
710 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
711 if (os_atomic_load(tidaddr
, relaxed
) == selfid
) return EDEADLK
;
713 #endif /* __DARWIN_UNIX03 */
722 // if W and K bit are clear or U bit is on, acquire lock in userland
724 gotlock
= (oldseq
.lcntval
& (PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0;
726 gotlock
= (oldseq
.lcntval
& PTH_RWL_UBIT
) != 0;
729 if (trylock
&& !gotlock
) {
730 // A trylock on a held lock will fail immediately. But since
731 // we did not load the sequence words atomically, perform a
732 // no-op CAS to ensure that nobody has unlocked concurrently.
733 } else if (gotlock
) {
735 if (diff_genseq(oldseq
.lcntval
, oldseq
.ucntval
) >=
737 // since ucntval may be newer, just redo
739 if (retry_count
> 1024) {
745 rwlock_seq_atomic_load(seqaddr
, &oldseq
,
746 RWLOCK_SEQ_LSU
, relaxed
);
750 // Need to update L (remove U bit) and S word
751 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
753 newseq
.lcntval
&= PTHRW_COUNT_MASK
;
754 newseq
.lcntval
|= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
756 newseq
.lcntval
+= PTHRW_INC
;
757 newseq
.rw_seq
+= PTHRW_INC
;
760 // Need to block in kernel. Remove U bit.
761 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
763 newseq
.lcntval
|= PTH_RWL_KBIT
| PTH_RWL_WBIT
;
765 newseq
.lcntval
+= PTHRW_INC
;
766 if (is_rws_setseq(oldseq
.rw_seq
)) {
767 // Clear the S bit and set S to L
768 newseq
.rw_seq
&= (PTHRW_BIT_MASK
& ~PTH_RWS_SBIT
);
769 newseq
.rw_seq
|= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
772 } while (!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
773 RWLOCK_SEQ_LS
, acquire
));
777 if (!readlock
) os_atomic_store(tidaddr
, selfid
, relaxed
);
778 #endif /* __DARWIN_UNIX03 */
780 } else if (trylock
) {
783 res
= _pthread_rwlock_lock_wait(orwlock
, readlock
, newseq
);
789 PLOCKSTAT_RW_ACQUIRE(orwlock
, plockstat
);
791 PLOCKSTAT_RW_ERROR(orwlock
, plockstat
, res
);
798 PTHREAD_ALWAYS_INLINE
800 _pthread_rwlock_lock(pthread_rwlock_t
*orwlock
, bool readlock
, bool trylock
)
802 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
804 if (PLOCKSTAT_RW_ACQUIRE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) {
805 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
809 if (os_unlikely(!_pthread_rwlock_check_signature(rwlock
))) {
810 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
814 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
816 rwlock_seq oldseq
, newseq
;
817 // no need to perform a single-copy-atomic 128-bit load in the fastpath,
818 // if stores to L and U are seen out of order, we will fallback to the
819 // slowpath below (which has rwlock_seq_atomic_load)
820 rwlock_seq_load(seqaddr
, &oldseq
, RWLOCK_SEQ_LSU
);
823 if (os_unlikely(is_rwl_ebit_set(oldseq
.lcntval
))) {
824 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
826 #endif /* __DARWIN_UNIX03 */
832 // if W and K bit are clear or U bit is on, acquire lock in userland
834 gotlock
= (oldseq
.lcntval
& (PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0;
836 gotlock
= (oldseq
.lcntval
& PTH_RWL_UBIT
) != 0;
839 if (trylock
&& !gotlock
) {
840 // A trylock on a held lock will fail immediately. But since
841 // we did not load the sequence words atomically, perform a
842 // no-op CAS to ensure that nobody has unlocked concurrently.
843 } else if (os_likely(gotlock
)) {
845 if (os_unlikely(diff_genseq(oldseq
.lcntval
, oldseq
.ucntval
) >=
846 PTHRW_MAX_READERS
)) {
847 return _pthread_rwlock_lock_slow(orwlock
, readlock
,trylock
);
849 // Need to update L (remove U bit) and S word
850 newseq
.lcntval
&= ~PTH_RWL_UBIT
;
852 newseq
.lcntval
&= PTHRW_COUNT_MASK
;
853 newseq
.lcntval
|= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
855 newseq
.lcntval
+= PTHRW_INC
;
856 newseq
.rw_seq
+= PTHRW_INC
;
858 return _pthread_rwlock_lock_slow(orwlock
, readlock
, trylock
);
860 } while (os_unlikely(!rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
,
861 RWLOCK_SEQ_LS
, acquire
)));
863 if (os_likely(gotlock
)) {
867 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
868 uint64_t selfid
= _pthread_selfid_direct();
869 os_atomic_store(tidaddr
, selfid
, relaxed
);
871 #endif /* __DARWIN_UNIX03 */
873 } else if (trylock
) {
880 PTHREAD_NOEXPORT_VARIANT
882 pthread_rwlock_rdlock(pthread_rwlock_t
*orwlock
)
885 return _pthread_rwlock_lock(orwlock
, true, false);
888 PTHREAD_NOEXPORT_VARIANT
890 pthread_rwlock_tryrdlock(pthread_rwlock_t
*orwlock
)
892 // read lock, try lock
893 return _pthread_rwlock_lock(orwlock
, true, true);
896 PTHREAD_NOEXPORT_VARIANT
898 pthread_rwlock_wrlock(pthread_rwlock_t
*orwlock
)
900 // write lock, no try
901 return _pthread_rwlock_lock(orwlock
, false, false);
904 PTHREAD_NOEXPORT_VARIANT
906 pthread_rwlock_trywrlock(pthread_rwlock_t
*orwlock
)
908 // write lock, try lock
909 return _pthread_rwlock_lock(orwlock
, false, true);
914 _pthread_rwlock_unlock_drop(pthread_rwlock_t
*orwlock
, rwlock_seq oldseq
,
918 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
920 RWLOCK_DEBUG_SEQ(unlock
, rwlock
, oldseq
, newseq
, !droplock
, RWLOCK_SEQ_LSU
);
923 updateval
= __psynch_rw_unlock(orwlock
, oldseq
.lcntval
,
924 newseq
.ucntval
, newseq
.rw_seq
, rwlock
->rw_flags
);
925 if (updateval
== (uint32_t)-1) {
929 RWLOCK_DEBUG_SEQ(wakeup
, rwlock
, oldseq
, newseq
, updateval
,
932 } while (res
== EINTR
);
935 PTHREAD_ABORT("kernel rwunlock returned unknown error %x: "
936 "tid %llx\n", res
, _pthread_selfid_direct());
942 PTHREAD_NOEXPORT PTHREAD_NOINLINE
944 _pthread_rwlock_unlock_slow(pthread_rwlock_t
*orwlock
,
945 rwlock_seqfields updated_seqfields
)
948 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
949 rwlock_seqfields seqfields
= RWLOCK_SEQ_LSU
;
954 res
= _pthread_rwlock_check_init(orwlock
);
955 if (res
!= 0) return res
;
958 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
960 rwlock_seq oldseq
, newseq
;
961 rwlock_seq_load(seqaddr
, &oldseq
, seqfields
);
963 if ((oldseq
.lcntval
& PTH_RWL_UBIT
) != 0) {
964 // spurious unlock (unlock of unlocked lock)
968 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
974 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
975 os_atomic_store(tidaddr
, 0, relaxed
);
976 #endif /* __DARWIN_UNIX03 */
981 // stop loading & updating fields that have successfully been stored
982 seqfields
&= ~updated_seqfields
;
985 if (seqfields
& RWLOCK_SEQ_U
) {
986 newseq
.ucntval
+= PTHRW_INC
;
990 uint32_t oldlcnt
= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
991 if (newseq
.ucntval
== oldlcnt
) {
992 // last unlock, set L with U and init bits and set S to L with S bit
993 newseq
.lcntval
= oldlcnt
| PTHRW_RWLOCK_INIT
;
994 newseq
.rw_seq
= oldlcnt
| PTHRW_RWS_INIT
;
996 // no L/S update if lock is not exclusive or no writer pending
997 if ((oldseq
.lcntval
&
998 (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0) {
1002 // kernel transition only needed if U == S
1003 if (newseq
.ucntval
!= (oldseq
.rw_seq
& PTHRW_COUNT_MASK
)) {
1008 // reset all bits and set K
1009 newseq
.lcntval
= oldlcnt
| PTH_RWL_KBIT
;
1010 // set I bit on S word
1011 newseq
.rw_seq
|= PTH_RWS_IBIT
;
1012 if ((oldseq
.lcntval
& PTH_RWL_WBIT
) != 0) {
1013 newseq
.rw_seq
|= PTH_RWS_WSVBIT
;
1016 } while (seqfields
!= (updated_seqfields
= rwlock_seq_atomic_cmpxchgv(
1017 seqaddr
, &oldseq
, &newseq
, seqfields
, release
)));
1020 res
= _pthread_rwlock_unlock_drop(orwlock
, oldseq
, newseq
);
1023 PLOCKSTAT_RW_RELEASE(orwlock
, wrlock
);
1028 PTHREAD_NOEXPORT_VARIANT
1030 pthread_rwlock_unlock(pthread_rwlock_t
*orwlock
)
1032 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
1033 rwlock_seqfields seqfields
= RWLOCK_SEQ_LSU
;
1034 rwlock_seqfields updated_seqfields
= RWLOCK_SEQ_NONE
;
1037 if (PLOCKSTAT_RW_RELEASE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) {
1038 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1042 if (os_unlikely(!_pthread_rwlock_check_signature(rwlock
))) {
1043 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1046 rwlock_seq
*seqaddr
;
1047 RWLOCK_GETSEQ_ADDR(rwlock
, &seqaddr
);
1049 rwlock_seq oldseq
, newseq
;
1050 rwlock_seq_load(seqaddr
, &oldseq
, seqfields
);
1052 if (os_unlikely(oldseq
.lcntval
& PTH_RWL_UBIT
)) {
1053 // spurious unlock (unlock of unlocked lock)
1057 if (is_rwl_ebit_set(oldseq
.lcntval
)) {
1060 RWLOCK_GETTID_ADDR(rwlock
, &tidaddr
);
1061 os_atomic_store(tidaddr
, 0, relaxed
);
1062 #endif /* __DARWIN_UNIX03 */
1066 if (updated_seqfields
) {
1067 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1071 if (seqfields
& RWLOCK_SEQ_U
) {
1072 newseq
.ucntval
+= PTHRW_INC
;
1075 uint32_t oldlcnt
= (oldseq
.lcntval
& PTHRW_COUNT_MASK
);
1076 if (os_likely(newseq
.ucntval
== oldlcnt
)) {
1077 // last unlock, set L with U and init bits and set S to L with S bit
1078 newseq
.lcntval
= oldlcnt
| PTHRW_RWLOCK_INIT
;
1079 newseq
.rw_seq
= oldlcnt
| PTHRW_RWS_INIT
;
1081 if (os_likely((oldseq
.lcntval
&
1082 (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0 ||
1083 newseq
.ucntval
!= (oldseq
.rw_seq
& PTHRW_COUNT_MASK
))) {
1084 // no L/S update if lock is not exclusive or no writer pending
1085 // kernel transition only needed if U == S
1087 return _pthread_rwlock_unlock_slow(orwlock
, updated_seqfields
);
1090 } while (os_unlikely(seqfields
!= (updated_seqfields
=
1091 rwlock_seq_atomic_cmpxchgv(seqaddr
, &oldseq
, &newseq
, seqfields
,