pthread read-write locks
rwlock_seq_load source
OS_ALWAYS_INLINE static inline void rwlock_seq_load(rwlock_seq *seqaddr, rwlock_seq *oldseqval, const rwlock_seqfields seqfields) { switch (seqfields) { case RWLOCK_SEQ_LSU: #if RWLOCK_USE_INT128 oldseqval->seq_LSU = seqaddr->seq_LSU; #else oldseqval->seq_LS = seqaddr->seq_LS; oldseqval->seq_U = seqaddr->seq_U; #endif break; case RWLOCK_SEQ_LS: oldseqval->seq_LS = seqaddr->seq_LS; break; #if DEBUG // unused case RWLOCK_SEQ_U: oldseqval->seq_U = seqaddr->seq_U; break; #endif // unused default: __builtin_trap(); } }
rwlock_seq_atomic_load_relaxed source
OS_ALWAYS_INLINE static inline void rwlock_seq_atomic_load_relaxed(rwlock_seq *seqaddr, rwlock_seq *oldseqval, const rwlock_seqfields seqfields) { switch (seqfields) { case RWLOCK_SEQ_LSU: #if RWLOCK_USE_INT128 #if defined(__arm64__) && defined(__ARM_ARCH_8_2__) // Workaround clang armv81 codegen bug for 128bit os_atomic_load // rdar://problem/31213932 oldseqval->seq_LSU = seqaddr->seq_LSU; while (!os_atomic_cmpxchgv(&seqaddr->atomic_seq_LSU, oldseqval->seq_LSU, oldseqval->seq_LSU, &oldseqval->seq_LSU, relaxed)); #else oldseqval->seq_LSU = os_atomic_load_wide(&seqaddr->atomic_seq_LSU, relaxed); #endif #else oldseqval->seq_LS = os_atomic_load_wide(&seqaddr->atomic_seq_LS, relaxed); oldseqval->seq_U = os_atomic_load(&seqaddr->atomic_seq_U, relaxed); #endif break; case RWLOCK_SEQ_LS: oldseqval->seq_LS = os_atomic_load_wide(&seqaddr->atomic_seq_LS, relaxed); break; #if DEBUG // unused case RWLOCK_SEQ_U: oldseqval->seq_U = os_atomic_load(&seqaddr->atomic_seq_U, relaxed); break; … more in source
rwlock_seq_atomic_cmpxchgv_relaxed source
OS_ALWAYS_INLINE static inline rwlock_seqfields rwlock_seq_atomic_cmpxchgv_relaxed(rwlock_seq *seqaddr, rwlock_seq *oldseqval, rwlock_seq *newseqval, const rwlock_seqfields seqfields) { bool r; rwlock_seqfields updated_seqfields = RWLOCK_SEQ_NONE; switch (seqfields) { #if DEBUG // unused case RWLOCK_SEQ_LSU: #if RWLOCK_USE_INT128 r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LSU, oldseqval->seq_LSU, newseqval->seq_LSU, &oldseqval->seq_LSU, relaxed); if (r) updated_seqfields = RWLOCK_SEQ_LSU; #else r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LS, oldseqval->seq_LS, newseqval->seq_LS, &oldseqval->seq_LS, relaxed); if (r) { r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_U, oldseqval->seq_U, newseqval->seq_U, &oldseqval->seq_U, relaxed); if (!r) oldseqval->seq_LS = newseqval->seq_LS; updated_seqfields = r ? RWLOCK_SEQ_LSU : RWLOCK_SEQ_LS; } else { oldseqval->seq_U = os_atomic_load(&seqaddr->atomic_seq_U, relaxed); } #endif break; case RWLOCK_SEQ_U: r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_U, oldseqval->seq_U, newseqval->seq_U, &oldseqval->seq_U, relaxed); … more in source
rwlock_seq_atomic_cmpxchgv_acquire source
OS_ALWAYS_INLINE static inline rwlock_seqfields rwlock_seq_atomic_cmpxchgv_acquire(rwlock_seq *seqaddr, rwlock_seq *oldseqval, rwlock_seq *newseqval, const rwlock_seqfields seqfields) { bool r; rwlock_seqfields updated_seqfields = RWLOCK_SEQ_NONE; switch (seqfields) { #if DEBUG // unused case RWLOCK_SEQ_LSU: #if RWLOCK_USE_INT128 r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LSU, oldseqval->seq_LSU, newseqval->seq_LSU, &oldseqval->seq_LSU, acquire); if (r) updated_seqfields = RWLOCK_SEQ_LSU; #else r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LS, oldseqval->seq_LS, newseqval->seq_LS, &oldseqval->seq_LS, acquire); if (r) { r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_U, oldseqval->seq_U, newseqval->seq_U, &oldseqval->seq_U, relaxed); if (!r) oldseqval->seq_LS = newseqval->seq_LS; updated_seqfields = r ? RWLOCK_SEQ_LSU : RWLOCK_SEQ_LS; } else { oldseqval->seq_U = os_atomic_load(&seqaddr->atomic_seq_U, relaxed); } #endif break; case RWLOCK_SEQ_U: r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_U, oldseqval->seq_U, newseqval->seq_U, &oldseqval->seq_U, acquire); … more in source
rwlock_seq_atomic_cmpxchgv_release source
OS_ALWAYS_INLINE static inline rwlock_seqfields rwlock_seq_atomic_cmpxchgv_release(rwlock_seq *seqaddr, rwlock_seq *oldseqval, rwlock_seq *newseqval, const rwlock_seqfields seqfields) { bool r; rwlock_seqfields updated_seqfields = RWLOCK_SEQ_NONE; switch (seqfields) { case RWLOCK_SEQ_LSU: #if RWLOCK_USE_INT128 r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LSU, oldseqval->seq_LSU, newseqval->seq_LSU, &oldseqval->seq_LSU, release); if (r) updated_seqfields = RWLOCK_SEQ_LSU; #else r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_U, oldseqval->seq_U, newseqval->seq_U, &oldseqval->seq_U, release); if (r) { r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LS, oldseqval->seq_LS, newseqval->seq_LS, &oldseqval->seq_LS, relaxed); if (!r) oldseqval->seq_U = newseqval->seq_U; updated_seqfields = r ? RWLOCK_SEQ_LSU : RWLOCK_SEQ_U; } else { oldseqval->seq_LS = os_atomic_load_wide(&seqaddr->atomic_seq_LS, relaxed); } #endif break; case RWLOCK_SEQ_LS: r = os_atomic_cmpxchgv(&seqaddr->atomic_seq_LS, oldseqval->seq_LS, newseqval->seq_LS, &oldseqval->seq_LS, release); … more in source
pthread_rwlockattr_init source · pthread_rwlockattr_init reference
[
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { attr->sig = _PTHREAD_RWLOCK_ATTR_SIG; attr->pshared = _PTHREAD_DEFAULT_PSHARED; return 0; }
pthread_rwlockattr_destroy source · pthread_rwlockattr_destroy reference
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) { attr->sig = _PTHREAD_NO_SIG; attr->pshared = 0; return 0; }
_pthread_rwlock_init source
!BUILDING_VARIANT ]
OS_ALWAYS_INLINE static inline int _pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { uint64_t *tidaddr; RWLOCK_GETTID_ADDR(rwlock, &tidaddr); rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); #if PTHREAD_RWLOCK_INIT_UNUSED if ((uint32_t*)tidaddr != rwlock->rw_tid) { rwlock->misalign = 1; __builtin_memset(rwlock->rw_tid, 0xff, sizeof(rwlock->rw_tid)); } if ((uint32_t*)seqaddr != rwlock->rw_seq) { __builtin_memset(rwlock->rw_seq, 0xff, sizeof(rwlock->rw_seq)); } __builtin_memset(rwlock->rw_mis, 0xff, sizeof(rwlock->rw_mis)); #endif // PTHREAD_MUTEX_INIT_UNUSED *tidaddr = 0; *seqaddr = (rwlock_seq){ .lcntval = PTHRW_RWLOCK_INIT, .rw_seq = PTHRW_RWS_INIT, .ucntval = 0, }; if (attr != NULL && attr->pshared == PTHREAD_PROCESS_SHARED) { rwlock->pshared = PTHREAD_PROCESS_SHARED; rwlock->rw_flags = PTHRW_KERN_PROCESS_SHARED; … more in source
_pthread_rwlock_modbits source
static uint32_t _pthread_rwlock_modbits(uint32_t lgenval, uint32_t updateval, uint32_t savebits) { uint32_t lval = lgenval & PTHRW_BIT_MASK; uint32_t uval = updateval & PTHRW_BIT_MASK; uint32_t rval, nlval; nlval = (lval | uval) & ~(PTH_RWL_MBIT); // reconcile bits on the lock with what kernel needs to set if ((uval & PTH_RWL_KBIT) == 0 && (lval & PTH_RWL_WBIT) == 0) { nlval &= ~PTH_RWL_KBIT; } if (savebits != 0) { if ((savebits & PTH_RWS_WSVBIT) != 0 && (nlval & PTH_RWL_WBIT) == 0 && (nlval & PTH_RWL_EBIT) == 0) { nlval |= (PTH_RWL_WBIT | PTH_RWL_KBIT); } } rval = (lgenval & PTHRW_COUNT_MASK) | nlval; return(rval); }
_pthread_rwlock_updateval source
OS_ALWAYS_INLINE static inline void _pthread_rwlock_updateval(pthread_rwlock_t *rwlock, uint32_t updateval) { bool isoverlap = (updateval & PTH_RWL_MBIT) != 0; // TBD: restore U bit rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); rwlock_seq oldseq, newseq; rwlock_seq_load(seqaddr, &oldseq, RWLOCK_SEQ_LS); do { newseq = oldseq; if (isoverlap || is_rws_unlockinit_set(oldseq.rw_seq)) { // Set S word to the specified value uint32_t savebits = (oldseq.rw_seq & PTHRW_RWS_SAVEMASK); newseq.lcntval = _pthread_rwlock_modbits(oldseq.lcntval, updateval, savebits); newseq.rw_seq += (updateval & PTHRW_COUNT_MASK); if (!isoverlap) { newseq.rw_seq &= PTHRW_COUNT_MASK; } newseq.rw_seq &= ~PTHRW_RWS_SAVEMASK; } } while (!rwlock_seq_atomic_cmpxchgv(seqaddr, &oldseq, &newseq, RWLOCK_SEQ_LS, relaxed)); RWLOCK_DEBUG_SEQ(update, rwlock, oldseq, newseq, updateval, RWLOCK_SEQ_LS); }
_pthread_rwlock_check_busy source
OS_ALWAYS_INLINE static inline int _pthread_rwlock_check_busy(pthread_rwlock_t *rwlock) { int res = 0; rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); rwlock_seq seq; rwlock_seq_atomic_load(seqaddr, &seq, RWLOCK_SEQ_LSU, relaxed); if ((seq.lcntval & PTHRW_COUNT_MASK) != seq.ucntval) { res = EBUSY; } return res; }
pthread_rwlock_destroy source · pthread_rwlock_destroy reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { int res = 0; _pthread_lock_lock(&rwlock->lock); if (_pthread_rwlock_check_signature(rwlock)) { res = _pthread_rwlock_check_busy(rwlock); } else if (!_pthread_rwlock_check_signature_init(rwlock)) { res = EINVAL; } if (res == 0) { rwlock->sig = _PTHREAD_NO_SIG; } _pthread_lock_unlock(&rwlock->lock); return res; }
pthread_rwlock_init source · pthread_rwlock_init reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { int res = 0; if (attr && attr->sig != _PTHREAD_RWLOCK_ATTR_SIG) { res = EINVAL; } if (res == 0 && _pthread_rwlock_check_signature(rwlock)) { res = _pthread_rwlock_check_busy(rwlock); } if (res == 0) { _pthread_lock_init(&rwlock->lock); res = _pthread_rwlock_init(rwlock, attr); } return res; }
_pthread_rwlock_check_init_slow source
OS_NOINLINE static int _pthread_rwlock_check_init_slow(pthread_rwlock_t *rwlock) { int res = EINVAL; if (_pthread_rwlock_check_signature_init(rwlock)) { _pthread_lock_lock(&rwlock->lock); if (_pthread_rwlock_check_signature_init(rwlock)) { res = _pthread_rwlock_init(rwlock, NULL); } else if (_pthread_rwlock_check_signature(rwlock)){ res = 0; } _pthread_lock_unlock(&rwlock->lock); } else if (_pthread_rwlock_check_signature(rwlock)){ res = 0; } if (res != 0) { PLOCKSTAT_RW_ERROR(rwlock, READ_LOCK_PLOCKSTAT, res); } return res; }
_pthread_rwlock_check_init source
OS_ALWAYS_INLINE static inline int _pthread_rwlock_check_init(pthread_rwlock_t *rwlock) { int res = 0; if (!_pthread_rwlock_check_signature(rwlock)) { return _pthread_rwlock_check_init_slow(rwlock); } return res; }
_pthread_rwlock_lock_wait source
OS_NOINLINE static int _pthread_rwlock_lock_wait(pthread_rwlock_t *rwlock, bool readlock, rwlock_seq newseq) { int res; #ifdef PLOCKSTAT int plockstat = readlock ? READ_LOCK_PLOCKSTAT : WRITE_LOCK_PLOCKSTAT; #endif if (readlock) { RWLOCK_DEBUG_SEQ(rdlock, rwlock, oldseq, newseq, gotlock, RWLOCK_SEQ_LSU); } else { RWLOCK_DEBUG_SEQ(wrlock, rwlock, oldseq, newseq, gotlock, RWLOCK_SEQ_LSU); } uint32_t updateval; PLOCKSTAT_RW_BLOCK(rwlock, plockstat); do { if (readlock) { updateval = __psynch_rw_rdlock(rwlock, newseq.lcntval, newseq.ucntval, newseq.rw_seq, rwlock->rw_flags); } else { updateval = __psynch_rw_wrlock(rwlock, newseq.lcntval, newseq.ucntval, newseq.rw_seq, rwlock->rw_flags); … more in source
_pthread_rwlock_lock_slow source
OS_NOINLINE int _pthread_rwlock_lock_slow(pthread_rwlock_t *rwlock, bool readlock, bool trylock) { int res; #ifdef PLOCKSTAT int plockstat = readlock ? READ_LOCK_PLOCKSTAT : WRITE_LOCK_PLOCKSTAT; #endif res = _pthread_rwlock_check_init(rwlock); if (res != 0) return res; rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); rwlock_seq oldseq, newseq; rwlock_seq_atomic_load(seqaddr, &oldseq, RWLOCK_SEQ_LSU, relaxed); uint64_t *tidaddr; RWLOCK_GETTID_ADDR(rwlock, &tidaddr); uint64_t selfid = _pthread_threadid_self_np_direct(); if (is_rwl_ebit_set(oldseq.lcntval)) { if (os_atomic_load_wide(tidaddr, relaxed) == selfid) return EDEADLK; } int retry_count; bool gotlock; do { … more in source
_pthread_rwlock_lock source
OS_ALWAYS_INLINE static inline int _pthread_rwlock_lock(pthread_rwlock_t *rwlock, bool readlock, bool trylock) { #if PLOCKSTAT if (PLOCKSTAT_RW_ACQUIRE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) { return _pthread_rwlock_lock_slow(rwlock, readlock, trylock); } #endif if (os_unlikely(!_pthread_rwlock_check_signature(rwlock))) { return _pthread_rwlock_lock_slow(rwlock, readlock, trylock); } rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); rwlock_seq oldseq, newseq; // no need to perform a single-copy-atomic 128-bit load in the fastpath, // if stores to L and U are seen out of order, we will fallback to the // slowpath below (which has rwlock_seq_atomic_load) rwlock_seq_load(seqaddr, &oldseq, RWLOCK_SEQ_LSU); if (os_unlikely(is_rwl_ebit_set(oldseq.lcntval))) { return _pthread_rwlock_lock_slow(rwlock, readlock, trylock); } bool gotlock; do { newseq = oldseq; … more in source
pthread_rwlock_rdlock source · pthread_rwlock_rdlock reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { // read lock, no try return _pthread_rwlock_lock(rwlock, true, false); }
pthread_rwlock_tryrdlock source · pthread_rwlock_tryrdlock reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { // read lock, try lock return _pthread_rwlock_lock(rwlock, true, true); }
pthread_rwlock_wrlock source · pthread_rwlock_wrlock reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { // write lock, no try return _pthread_rwlock_lock(rwlock, false, false); }
pthread_rwlock_trywrlock source · pthread_rwlock_trywrlock reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { // write lock, try lock return _pthread_rwlock_lock(rwlock, false, true); }
_pthread_rwlock_unlock_drop source
OS_NOINLINE static int _pthread_rwlock_unlock_drop(pthread_rwlock_t *rwlock, rwlock_seq oldseq, rwlock_seq newseq) { int res; RWLOCK_DEBUG_SEQ(unlock, rwlock, oldseq, newseq, !droplock, RWLOCK_SEQ_LSU); uint32_t updateval; do { updateval = __psynch_rw_unlock(rwlock, oldseq.lcntval, newseq.ucntval, newseq.rw_seq, rwlock->rw_flags); if (updateval == (uint32_t)-1) { res = errno; } else { res = 0; RWLOCK_DEBUG_SEQ(wakeup, rwlock, oldseq, newseq, updateval, RWLOCK_SEQ_LSU); } } while (res == EINTR); if (res != 0) { PTHREAD_INTERNAL_CRASH(res, "kernel rwunlock returned unknown error"); } return res; }
_pthread_rwlock_unlock_slow source
OS_NOINLINE int _pthread_rwlock_unlock_slow(pthread_rwlock_t *rwlock, rwlock_seqfields updated_seqfields) { int res; rwlock_seqfields seqfields = RWLOCK_SEQ_LSU; #ifdef PLOCKSTAT int wrlock = 0; #endif res = _pthread_rwlock_check_init(rwlock); if (res != 0) return res; rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); rwlock_seq oldseq, newseq; rwlock_seq_load(seqaddr, &oldseq, seqfields); if ((oldseq.lcntval & PTH_RWL_UBIT) != 0) { // spurious unlock (unlock of unlocked lock) return 0; } if (is_rwl_ebit_set(oldseq.lcntval)) { #ifdef PLOCKSTAT wrlock = 1; #endif uint64_t *tidaddr; … more in source
pthread_rwlock_unlock source · pthread_rwlock_unlock reference
PTHREAD_NOEXPORT_VARIANT int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { rwlock_seqfields seqfields = RWLOCK_SEQ_LSU; rwlock_seqfields updated_seqfields = RWLOCK_SEQ_NONE; #if PLOCKSTAT if (PLOCKSTAT_RW_RELEASE_ENABLED() || PLOCKSTAT_RW_ERROR_ENABLED()) { return _pthread_rwlock_unlock_slow(rwlock, updated_seqfields); } #endif if (os_unlikely(!_pthread_rwlock_check_signature(rwlock))) { return _pthread_rwlock_unlock_slow(rwlock, updated_seqfields); } rwlock_seq *seqaddr; RWLOCK_GETSEQ_ADDR(rwlock, &seqaddr); rwlock_seq oldseq, newseq; rwlock_seq_load(seqaddr, &oldseq, seqfields); if (os_unlikely(oldseq.lcntval & PTH_RWL_UBIT)) { // spurious unlock (unlock of unlocked lock) return 0; } if (is_rwl_ebit_set(oldseq.lcntval)) { uint64_t *tidaddr; … more in source
rwlock_seq source typedef
typedef union rwlock_seq { uint32_t seq[4]; struct { uint32_t lcntval; uint32_t rw_seq; uint32_t ucntval; }; struct { uint32_t lgen; uint32_t rw_wc; uint32_t ugen; }; #if RWLOCK_USE_INT128 unsigned __int128 seq_LSU; unsigned __int128 _Atomic atomic_seq_LSU; #endif struct { uint64_t seq_LS; uint32_t seq_U; uint32_t _pad; }; struct { uint64_t _Atomic atomic_seq_LS; uint32_t _Atomic atomic_seq_U; uint32_t _Atomic _atomic_pad; }; } rwlock_seq;
rwlock_seqfields source typedef
typedef enum rwlock_seqfields { RWLOCK_SEQ_NONE = 0, RWLOCK_SEQ_LS = 1, RWLOCK_SEQ_U = 2, RWLOCK_SEQ_LSU = RWLOCK_SEQ_LS | RWLOCK_SEQ_U, } rwlock_seqfields;