// A rwlockattr is implemented as a 32-bit integer which has following fields: // bits name description // 1 rwlock_kind have rwlock preference like PTHREAD_RWLOCK_PREFER_READER_NP. // 0 process_shared set to 1 if the rwlock is shared between processes.
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t* attr, int pref) { switch (pref) { case PTHREAD_RWLOCK_PREFER_READER_NP: // Fall through. case PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
__rwlockattr_setkind(attr, pref); return0; default: return EINVAL;
}
}
// A rwlock state is implemented as a 32-bit integer which has following rules: // bits name description // 31 owned_by_writer_flag set to 1 if the lock is owned by a writer now. // 30-2 reader_count the count of readers holding the lock. // 1 have_pending_writers set to 1 if having pending writers. // 0 have_pending_readers set to 1 if having pending readers.
// When a reader thread plans to suspend on the rwlock, it will add STATE_HAVE_PENDING_READERS_FLAG // in state, increase pending_reader_count, and wait on pending_reader_wakeup_serial. After woken // up, the reader thread decreases pending_reader_count, and the last pending reader thread should // remove STATE_HAVE_PENDING_READERS_FLAG in state. A pending writer thread works in a similar way, // except that it uses flag and members for writer threads.
Lock pending_lock; // All pending members below are protected by pending_lock.
uint32_t pending_reader_count; // Count of pending reader threads.
uint32_t pending_writer_count; // Count of pending writer threads.
uint32_t pending_reader_wakeup_serial; // Pending reader threads wait on this address by futex_wait.
uint32_t pending_writer_wakeup_serial; // Pending writer threads wait on this address by futex_wait.
staticinline __always_inline bool __state_owned_by_readers(int state) { // If state >= 0, the owned_by_writer_flag is not set. // And if state >= STATE_READER_COUNT_CHANGE_STEP, the reader_count field is not empty. return state >= STATE_READER_COUNT_CHANGE_STEP;
}
staticinline __always_inline bool __state_owned_by_readers_or_writer(int state) { return state < 0 || state >= STATE_READER_COUNT_CHANGE_STEP;
}
staticinline __always_inline int __state_add_writer_flag(int state) { return state | STATE_OWNED_BY_WRITER_FLAG;
}
static_assert(sizeof(pthread_rwlock_t) == sizeof(pthread_rwlock_internal_t), "pthread_rwlock_t should actually be pthread_rwlock_internal_t in implementation.");
// For binary compatibility with old version of pthread_rwlock_t, we can't use more strict // alignment than 4-byte alignment.
static_assert(alignof(pthread_rwlock_t) == 4, "pthread_rwlock_t should fulfill the alignment requirement of pthread_rwlock_internal_t.");
staticinline __always_inline bool __can_acquire_read_lock(int old_state, bool writer_nonrecursive_preferred) { // If writer is preferred with nonrecursive reader, we prevent further readers from acquiring // the lock when there are writers waiting for the lock. bool cannot_apply = __state_owned_by_writer(old_state) ||
(writer_nonrecursive_preferred && __state_have_pending_writers(old_state)); return !cannot_apply;
}
staticinline __always_inline int __pthread_rwlock_tryrdlock(pthread_rwlock_internal_t* rwlock) { int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
while (__predict_true(__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred))) {
int new_state = old_state + STATE_READER_COUNT_CHANGE_STEP; if (__predict_false(!__state_owned_by_readers(new_state))) { // Happens when reader count overflows. return EAGAIN;
} if (__predict_true(atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state, new_state,
memory_order_acquire, memory_order_relaxed))) { return0;
}
} return EBUSY;
}
while (true) { int result = __pthread_rwlock_tryrdlock(rwlock); if (result == 0 || result == EAGAIN) { return result;
}
result = check_timespec(abs_timeout_or_null, true); if (result != 0) { return result;
}
int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed); if (__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) { continue;
}
// We rely on the fact that all atomic exchange operations on the same object (here it is // rwlock->state) always appear to occur in a single total order. If the pending flag is added // before unlocking, the unlocking thread will wakeup the waiter. Otherwise, we will see the // state is unlocked and will not wait anymore.
old_state = atomic_fetch_or_explicit(&rwlock->state, STATE_HAVE_PENDING_READERS_FLAG,
memory_order_relaxed);
int old_serial = rwlock->pending_reader_wakeup_serial;
rwlock->pending_lock.unlock();
int futex_result = 0; if (!__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
futex_result = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared,
old_serial, use_realtime_clock, abs_timeout_or_null);
}
staticinline __always_inline int __pthread_rwlock_trywrlock(pthread_rwlock_internal_t* rwlock) { int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
while (__predict_true(__can_acquire_write_lock(old_state))) { if (__predict_true(atomic_compare_exchange_weak_explicit(&rwlock->state, &old_state,
__state_add_writer_flag(old_state), memory_order_acquire, memory_order_relaxed))) {
// Wake up pending readers or writers.
rwlock->pending_lock.lock(); if (rwlock->pending_writer_count != 0) {
rwlock->pending_writer_wakeup_serial++;
rwlock->pending_lock.unlock();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.