pthread read-write locks

libpthread/src/pthread_rwlock.c · 1057 lines · browse source
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_rwlockattr_getpshared source · pthread_rwlockattr_getpshared reference
int
pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared)
{
	int res = EINVAL;
	if (attr->sig == _PTHREAD_RWLOCK_ATTR_SIG) {
		*pshared = (int)attr->pshared;
		res = 0;
	}
	return res;
}
pthread_rwlockattr_setpshared source · pthread_rwlockattr_setpshared reference
int
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * attr, int pshared)
{
	int res = EINVAL;
	if (attr->sig == _PTHREAD_RWLOCK_ATTR_SIG) {
		if (( pshared == PTHREAD_PROCESS_PRIVATE) ||
				(pshared == PTHREAD_PROCESS_SHARED))
		{
			attr->pshared = pshared ;
			res = 0;
		}
	}
	return res;
}
_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;