/* a mutex attribute holds the following fields * *bits:namedescription *0-3typetypeofmutex *4sharedprocess-sharedflag *5protocolwhetheritisapriorityinheritmutex.
*/ #define MUTEXATTR_TYPE_MASK 0x000f #define MUTEXATTR_SHARED_MASK 0x0010 #define MUTEXATTR_PROTOCOL_MASK 0x0020
#define MUTEXATTR_PROTOCOL_SHIFT 5
int pthread_mutexattr_init(pthread_mutexattr_t *attr)
{
*attr = PTHREAD_MUTEX_DEFAULT; return0;
}
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
{
*attr = -1; return0;
}
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type_p)
{ int type = (*attr & MUTEXATTR_TYPE_MASK);
if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK) { return EINVAL;
}
*type_p = type; return0;
}
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
{ if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK ) { return EINVAL;
}
/* process-shared mutexes are not supported at the moment */
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)
{ switch (pshared) { case PTHREAD_PROCESS_PRIVATE:
*attr &= ~MUTEXATTR_SHARED_MASK; return0;
case PTHREAD_PROCESS_SHARED: /* our current implementation of pthread actually supports shared *mutexesbutwon'tcleanupifaprocessdieswiththemutexheld. *Nevertheless,it'sbetterthannothing.Sharedmutexesareused *bysurfaceflingerandaudioflinger.
*/
*attr |= MUTEXATTR_SHARED_MASK; return0;
} return EINVAL;
}
// Priority Inheritance mutex implementation struct PIMutex { // mutex type, can be 0 (normal), 1 (recursive), 2 (errorcheck), constant during lifetime
uint8_t type; // process-shared flag, constant during lifetime bool shared; // <number of times a thread holding a recursive PI mutex> - 1
uint16_t counter; // owner_tid is read/written by both userspace code and kernel code. It includes three fields: // FUTEX_WAITERS, FUTEX_OWNER_DIED and FUTEX_TID_MASK.
atomic_int owner_tid;
};
staticinline __always_inline int PIMutexTryLock(PIMutex& mutex) {
pid_t tid = __get_thread()->tid; // Handle common case first. int old_owner = 0; if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex.owner_tid,
&old_owner, tid,
memory_order_acquire,
memory_order_relaxed))) { return0;
} if (tid == (old_owner & FUTEX_TID_MASK)) { // We already own this mutex. if (mutex.type == PTHREAD_MUTEX_NORMAL) { return EBUSY;
} if (mutex.type == PTHREAD_MUTEX_ERRORCHECK) { return EDEADLK;
} if (mutex.counter == 0xffff) { return EAGAIN;
}
mutex.counter++; return0;
} return EBUSY;
}
// Inlining this function in pthread_mutex_lock() adds the cost of stack frame instructions on // ARM/ARM64, which increases at most 20 percent overhead. So make it noinline. staticint __attribute__((noinline)) PIMutexTimedLock(PIMutex& mutex, bool use_realtime_clock, const timespec* abs_timeout) { int ret = PIMutexTryLock(mutex); if (__predict_true(ret == 0)) { return0;
} if (ret == EBUSY) { char trace_msg[64]; const pid_t owner = atomic_load_explicit(&mutex.owner_tid, memory_order_relaxed)
& FUTEX_TID_MASK;
snprintf(trace_msg, sizeof(trace_msg), "Contending for pthread mutex owned by tid: %d", owner);
ScopedTrace trace(trace_msg);
ret = -__futex_pi_lock_ex(&mutex.owner_tid, mutex.shared, use_realtime_clock, abs_timeout);
} return ret;
}
staticint PIMutexUnlock(PIMutex& mutex) {
pid_t tid = __get_thread()->tid; int old_owner = tid; // Handle common case first. if (__predict_true(mutex.type == PTHREAD_MUTEX_NORMAL)) { if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex.owner_tid,
&old_owner, 0,
memory_order_release,
memory_order_relaxed))) { return0;
}
} else {
old_owner = atomic_load_explicit(&mutex.owner_tid, memory_order_relaxed);
}
if (tid != (old_owner & FUTEX_TID_MASK)) { // The mutex can only be unlocked by the thread who owns it. return EPERM;
} if (mutex.type == PTHREAD_MUTEX_RECURSIVE) { if (mutex.counter != 0u) {
--mutex.counter; return0;
}
} if (old_owner == tid) { // No thread is waiting. if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex.owner_tid,
&old_owner, 0,
memory_order_release,
memory_order_relaxed))) { return0;
}
} return -__futex_pi_unlock(&mutex.owner_tid, mutex.shared);
}
staticint PIMutexDestroy(PIMutex& mutex) { // The mutex should be in unlocked state (owner_tid == 0) when destroyed. // Store 0xffffffff to make the mutex unusable. int old_owner = 0; if (atomic_compare_exchange_strong_explicit(&mutex.owner_tid, &old_owner, 0xffffffff,
memory_order_relaxed, memory_order_relaxed)) { return0;
} return EBUSY;
}
#if !defined(__LP64__)
namespace PIMutexAllocator { // pthread_mutex_t has only 4 bytes in 32-bit programs, which are not enough to hold PIMutex. // So we use malloc to allocate PIMutexes and use 16-bit of pthread_mutex_t as indexes to find // the allocated PIMutexes. This allows at most 65536 PI mutexes. // When calling operations like pthread_mutex_lock/unlock, the 16-bit index is mapped to the // corresponding PIMutex. To make the map operation fast, we use a lockless mapping method: // Once a PIMutex is allocated, all the data used to map index to the PIMutex isn't changed until // it is destroyed. // Below are the data structures: // // struct Node contains a PIMutex. // typedef Node NodeArray[256]; // typedef NodeArray* NodeArrayP; // NodeArrayP nodes[256]; // // A 16-bit index is mapped to Node as below: // (*nodes[index >> 8])[index & 0xff] // // Also use a free list to allow O(1) finding recycled PIMutexes.
union Node {
PIMutex mutex; int next_free_id; // If not -1, refer to the next node in the free PIMutex list.
}; typedef Node NodeArray[256]; typedef NodeArray* NodeArrayP;
// lock_ protects below items. static Lock lock; static NodeArrayP* nodes; staticint next_to_alloc_id; staticint first_free_id = -1; // If not -1, refer to the first node in the free PIMutex list.
staticint AllocIdLocked() { if (first_free_id != -1) { int result = first_free_id;
first_free_id = IdToNode(result).next_free_id; return result;
} if (next_to_alloc_id >= 0x10000) { return -1;
} int array_pos = next_to_alloc_id >> 8; int node_pos = next_to_alloc_id & 0xff; if (node_pos == 0) { if (array_pos == 0) {
nodes = static_cast<NodeArray**>(calloc(256, sizeof(NodeArray*))); if (nodes == nullptr) { return -1;
}
}
nodes[array_pos] = static_cast<NodeArray*>(malloc(sizeof(NodeArray))); if (nodes[array_pos] == nullptr) { return -1;
}
} return next_to_alloc_id++;
}
// If succeed, return an id referring to a PIMutex, otherwise return -1. // A valid id is in range [0, 0xffff]. staticint AllocId() {
lock.lock(); int result = AllocIdLocked();
lock.unlock(); if (result != -1) {
IdToPIMutex(result) = {};
} return result;
}
/* Convenience macro, creates a mask of 'bits' bits that starts from *the'shift'-thleastsignificantbitina32-bitword. * *Examples:FIELD_MASK(0,4)->0xf *FIELD_MASK(16,9)->0x1ff0000
*/ #define FIELD_MASK(shift,bits) (((1 << (bits))-1) << (shift))
/* This one is used to create a bit pattern from a given field value */ #define FIELD_TO_BITS(val,shift,bits) (((val) & ((1 << (bits))-1)) << (shift))
/* And this one does the opposite, i.e. extract a field's value from a bit pattern */ #define FIELD_FROM_BITS(val,shift,bits) (((val) >> (shift)) & ((1 << (bits))-1))
#define MUTEX_STATE_UNLOCKED 0/* must be 0 to match PTHREAD_MUTEX_INITIALIZER */ #define MUTEX_STATE_LOCKED_UNCONTENDED 1/* must be 1 due to atomic dec in unlock operation */ #define MUTEX_STATE_LOCKED_CONTENDED 2/* must be 1 + LOCKED_UNCONTENDED due to atomic dec */
// Return true iff the mutex is unlocked. #define MUTEX_STATE_BITS_IS_UNLOCKED(v) (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_UNLOCKED)
// Return true iff the mutex is locked with no waiters. #define MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(v) (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_LOCKED_UNCONTENDED)
// return true iff the mutex is locked with maybe waiters. #define MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(v) (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_LOCKED_CONTENDED)
/* used to flip from LOCKED_UNCONTENDED to LOCKED_CONTENDED */ #define MUTEX_STATE_BITS_FLIP_CONTENTION(v) ((v) ^ (MUTEX_STATE_BITS_LOCKED_CONTENDED ^ MUTEX_STATE_BITS_LOCKED_UNCONTENDED))
/* Used to increment the counter directly after overflow has been checked */ #define MUTEX_COUNTER_BITS_ONE FIELD_TO_BITS(1, MUTEX_COUNTER_SHIFT,MUTEX_COUNTER_LEN)
/* Mutex shared bit flag * *Thisflagissettoindicatethatthemutexissharedamongprocesses. *Thischangesthefutexopcodeweuseforfutexwait/wakeoperations *(non-sharedoperationsaremuchfaster).
*/ #define MUTEX_SHARED_SHIFT 13 #define MUTEX_SHARED_MASK FIELD_MASK(MUTEX_SHARED_SHIFT,1)
#define MUTEX_TYPE_BITS_NORMAL MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_NORMAL) #define MUTEX_TYPE_BITS_RECURSIVE MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_RECURSIVE) #define MUTEX_TYPE_BITS_ERRORCHECK MUTEX_TYPE_TO_BITS(PTHREAD_MUTEX_ERRORCHECK) // Use a special mutex type to mark priority inheritance mutexes. #define PI_MUTEX_STATE MUTEX_TYPE_TO_BITS(3)
// For a PI mutex, it includes below fields: // Atomic(uint16_t) state; // PIMutex pi_mutex; // uint16_t pi_mutex_id in 32-bit programs // // state holds the following fields: // // bits: name description // 15-14 type mutex type, should be 3 // 13-0 padding should be 0 // // pi_mutex holds the state of a PI mutex. // pi_mutex_id holds an integer to find the state of a PI mutex. // // For a Non-PI mutex, it includes below fields: // Atomic(uint16_t) state; // atomic_int owner_tid; // Atomic(uint16_t) in 32-bit programs // // state holds the following fields: // // bits: name description // 15-14 type mutex type, can be 0 (normal), 1 (recursive), 2 (errorcheck) // 13 shared process-shared flag // 12-2 counter <number of times a thread holding a recursive Non-PI mutex> - 1 // 1-0 state lock state (0, 1 or 2) // // bits 15-13 are constant during the lifetime of the mutex. // // owner_tid is used only in recursive and errorcheck Non-PI mutexes to hold the mutex owner // thread id. // // PI mutexes and Non-PI mutexes are distinguished by checking type field in state. #ifdefined(__LP64__) struct pthread_mutex_internal_t {
_Atomic(uint16_t) state;
uint16_t __pad; union {
atomic_int owner_tid;
PIMutex pi_mutex;
}; char __reserved[28];
static_assert(sizeof(pthread_mutex_t) == sizeof(pthread_mutex_internal_t), "pthread_mutex_t should actually be pthread_mutex_internal_t in implementation.");
// For binary compatibility with old version of pthread_mutex_t, we can't use more strict alignment // than 4-byte alignment.
static_assert(alignof(pthread_mutex_t) == 4, "pthread_mutex_t should fulfill the alignment of pthread_mutex_internal_t.");
if (__predict_true(attr == nullptr)) {
atomic_store_explicit(&mutex->state, MUTEX_TYPE_BITS_NORMAL, memory_order_relaxed); return0;
}
uint16_t state = 0; if ((*attr & MUTEXATTR_SHARED_MASK) != 0) {
state |= MUTEX_SHARED_MASK;
}
switch (*attr & MUTEXATTR_TYPE_MASK) { case PTHREAD_MUTEX_NORMAL:
state |= MUTEX_TYPE_BITS_NORMAL; break; case PTHREAD_MUTEX_RECURSIVE:
state |= MUTEX_TYPE_BITS_RECURSIVE; break; case PTHREAD_MUTEX_ERRORCHECK:
state |= MUTEX_TYPE_BITS_ERRORCHECK; break; default: return EINVAL;
}
// We want to go to sleep until the mutex is available, which requires // promoting it to locked_contended. We need to swap in the new state // and then wait until somebody wakes us up. // An atomic_exchange is used to compete with other threads for the lock. // If it returns unlocked, we have acquired the lock, otherwise another // thread still holds the lock and we should wait again. // If lock is acquired, an acquire fence is needed to make all memory accesses // made by other threads visible to the current CPU. while (atomic_exchange_explicit(&mutex->state, locked_contended,
memory_order_acquire) != unlocked) { if (__futex_wait_ex(&mutex->state, shared, locked_contended, use_realtime_clock,
abs_timeout_or_null) == -ETIMEDOUT) { return ETIMEDOUT;
}
} return0;
}
// We use an atomic_exchange to release the lock. If locked_contended state // is returned, some threads is waiting for the lock and we need to wake up // one of them. // A release fence is required to make previous stores visible to next // lock owner threads. if (atomic_exchange_explicit(&mutex->state, unlocked,
memory_order_release) == locked_contended) { // Wake up one waiting thread. We don't know which thread will be // woken or when it'll start executing -- futexes make no guarantees // here. There may not even be a thread waiting. // // The newly-woken thread will replace the unlocked state we just set above // with locked_contended state, which means that when it eventually releases // the mutex it will also call FUTEX_WAKE. This results in one extra wake // call whenever a lock is contended, but let us avoid forgetting anyone // without requiring us to track the number of sleepers. // // It's possible for another thread to sneak in and grab the lock between // the exchange above and the wake call below. If the new thread is "slow" // and holds the lock for a while, we'll wake up a sleeper, which will swap // in locked_uncontended state and then go back to sleep since the lock is // still held. If the new thread is "fast", running to completion before // we call wake, the thread we eventually wake will find an unlocked mutex // and will execute. Either way we have correct behavior and nobody is // orphaned on the wait queue. // // The pthread_mutex_internal_t object may have been deallocated between the // atomic exchange and the wake call. In that case, this wake call could // target unmapped memory or memory used by an otherwise unrelated futex // operation. Even if the kernel avoids spurious futex wakeups from its // point of view, this wake call could trigger a spurious wakeup in any // futex accessible from this process. References: // - https://lkml.org/lkml/2014/11/27/472 // - http://austingroupbugs.net/view.php?id=811#c2267
__futex_wake_ex(&mutex->state, shared, 1);
}
}
/* This common inlined function is used to increment the counter of a recursive Non-PI mutex. * *Ifthecounteroverflows,itwillreturnEAGAIN. *Otherwise,itatomicallyincrementsthecounterandreturns0. *
*/ staticinline __always_inline int RecursiveIncrement(pthread_mutex_internal_t* mutex,
uint16_t old_state) { // Detect recursive lock overflow and return EAGAIN. // This is safe because only the owner thread can modify the // counter bits in the mutex value. if (MUTEX_COUNTER_BITS_WILL_OVERFLOW(old_state)) { return EAGAIN;
}
// Other threads are able to change the lower bits (e.g. promoting it to "contended"), // but the mutex counter will not overflow. So we use atomic_fetch_add operation here. // The mutex is already locked by current thread, so we don't need an acquire fence.
atomic_fetch_add_explicit(&mutex->state, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return0;
}
// Wait on a recursive or errorcheck Non-PI mutex. staticinline __always_inline int RecursiveOrErrorcheckMutexWait(pthread_mutex_internal_t* mutex,
uint16_t shared,
uint16_t old_state, bool use_realtime_clock, const timespec* abs_timeout) { // __futex_wait always waits on a 32-bit value. But state is 16-bit. For a normal mutex, the owner_tid // field in mutex is not used. On 64-bit devices, the __pad field in mutex is not used. // But when a recursive or errorcheck mutex is used on 32-bit devices, we need to add the // owner_tid value in the value argument for __futex_wait, otherwise we may always get EAGAIN error.
#else // This implementation works only when the layout of pthread_mutex_internal_t matches below expectation. // And it is based on the assumption that Android is always in little-endian devices.
static_assert(offsetof(pthread_mutex_internal_t, state) == 0, "");
static_assert(offsetof(pthread_mutex_internal_t, owner_tid) == 2, "");
// Handle common case first. if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) { return NormalMutexLock(mutex, shared, use_realtime_clock, abs_timeout_or_null);
}
// Do we already own this recursive or error-check mutex?
pid_t tid = __get_thread()->tid; if (tid == atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed)) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EDEADLK;
} return RecursiveIncrement(mutex, old_state);
}
// First, if the mutex is unlocked, try to quickly acquire it. // In the optimistic case where this works, set the state to locked_uncontended. if (old_state == unlocked) { // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state,
locked_uncontended, memory_order_acquire, memory_order_relaxed))) {
atomic_store_explicit(&mutex->owner_tid, tid, memory_order_relaxed); return0;
}
}
ScopedTrace trace("Contending for pthread mutex");
while (true) { if (old_state == unlocked) { // NOTE: We put the state to locked_contended since we _know_ there // is contention when we are in this loop. This ensures all waiters // will be unlocked.
// If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU. if (__predict_true(atomic_compare_exchange_weak_explicit(&mutex->state,
&old_state, locked_contended,
memory_order_acquire,
memory_order_relaxed))) {
atomic_store_explicit(&mutex->owner_tid, tid, memory_order_relaxed); return0;
} continue;
} elseif (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(old_state)) { // We should set it to locked_contended beforing going to sleep. This can make // sure waiters will be woken up eventually.
int new_state = MUTEX_STATE_BITS_FLIP_CONTENTION(old_state); if (__predict_false(!atomic_compare_exchange_weak_explicit(&mutex->state,
&old_state, new_state,
memory_order_relaxed,
memory_order_relaxed))) { continue;
}
old_state = new_state;
}
int result = check_timespec(abs_timeout_or_null, true); if (result != 0) { return result;
} // We are in locked_contended state, sleep until someone wakes us up. if (RecursiveOrErrorcheckMutexWait(mutex, shared, old_state, use_realtime_clock,
abs_timeout_or_null) == -ETIMEDOUT) { return ETIMEDOUT;
}
old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
}
}
// Inlining this function in pthread_mutex_lock() adds the cost of stack frame instructions on // ARM64. So make it noinline. staticint __attribute__((noinline)) HandleUsingDestroyedMutex(pthread_mutex_t* mutex, constchar* function_name) { if (android_get_application_target_sdk_version() >= 28) {
__fortify_fatal("%s called on a destroyed mutex (%p)", function_name, mutex);
} return EBUSY;
}
int pthread_mutex_lock(pthread_mutex_t* mutex_interface) { #if !defined(__LP64__) // Some apps depend on being able to pass NULL as a mutex and get EINVAL // back. Don't need to worry about it for LP64 since the ABI is brand new, // but keep compatibility for LP32. http://b/19995172. if (mutex_interface == nullptr) { return EINVAL;
} #endif
pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface);
uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
uint16_t mtype = (old_state & MUTEX_TYPE_MASK); // Avoid slowing down fast path of normal mutex lock operation. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) {
uint16_t shared = (old_state & MUTEX_SHARED_MASK); if (__predict_true(NonPI::NormalMutexTryLock(mutex, shared) == 0)) { return0;
}
} if (old_state == PI_MUTEX_STATE) {
PIMutex& m = mutex->ToPIMutex(); // Handle common case first. if (__predict_true(PIMutexTryLock(m) == 0)) { return0;
} return PIMutexTimedLock(mutex->ToPIMutex(), false, nullptr);
} if (__predict_false(IsMutexDestroyed(old_state))) { return HandleUsingDestroyedMutex(mutex_interface, __FUNCTION__);
} return NonPI::MutexLockWithTimeout(mutex, false, nullptr);
}
int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) { #if !defined(__LP64__) // Some apps depend on being able to pass NULL as a mutex and get EINVAL // back. Don't need to worry about it for LP64 since the ABI is brand new, // but keep compatibility for LP32. http://b/19995172. if (mutex_interface == nullptr) { return EINVAL;
} #endif
// Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) {
NonPI::NormalMutexUnlock(mutex, shared); return0;
} if (old_state == PI_MUTEX_STATE) { return PIMutexUnlock(mutex->ToPIMutex());
} if (__predict_false(IsMutexDestroyed(old_state))) { return HandleUsingDestroyedMutex(mutex_interface, __FUNCTION__);
}
// Do we already own this recursive or error-check mutex?
pid_t tid = __get_thread()->tid; if ( tid != atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed) ) { return EPERM;
}
// If the counter is > 0, we can simply decrement it atomically. // Since other threads can mutate the lower state bits (and only the // lower state bits), use a compare_exchange loop to do it. if (!MUTEX_COUNTER_BITS_IS_ZERO(old_state)) { // We still own the mutex, so a release fence is not needed.
atomic_fetch_sub_explicit(&mutex->state, MUTEX_COUNTER_BITS_ONE, memory_order_relaxed); return0;
}
// The counter is 0, so we'are going to unlock the mutex by resetting its // state to unlocked, we need to perform a atomic_exchange inorder to read // the current state, which will be locked_contended if there may have waiters // to awake. // A release fence is required to make previous stores visible to next // lock owner threads.
atomic_store_explicit(&mutex->owner_tid, 0, memory_order_relaxed); const uint16_t unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED;
old_state = atomic_exchange_explicit(&mutex->state, unlocked, memory_order_release); if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(old_state)) {
__futex_wake_ex(&mutex->state, shared, 1);
}
return0;
}
int pthread_mutex_trylock(pthread_mutex_t* mutex_interface) {
pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface);
// Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) {
uint16_t shared = (old_state & MUTEX_SHARED_MASK); return NonPI::NormalMutexTryLock(mutex, shared);
} if (old_state == PI_MUTEX_STATE) { return PIMutexTryLock(mutex->ToPIMutex());
} if (__predict_false(IsMutexDestroyed(old_state))) { return HandleUsingDestroyedMutex(mutex_interface, __FUNCTION__);
}
// Do we already own this recursive or error-check mutex?
pid_t tid = __get_thread()->tid; if (tid == atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed)) { if (mtype == MUTEX_TYPE_BITS_ERRORCHECK) { return EBUSY;
} return NonPI::RecursiveIncrement(mutex, old_state);
}
// Same as pthread_mutex_lock, except that we don't want to wait, and // the only operation that can succeed is a single compare_exchange to acquire the // lock if it is released / not owned by anyone. No need for a complex loop. // If exchanged successfully, an acquire fence is required to make // all memory accesses made by other threads visible to the current CPU.
old_state = unlocked; if (__predict_true(atomic_compare_exchange_strong_explicit(&mutex->state, &old_state,
locked_uncontended,
memory_order_acquire,
memory_order_relaxed))) {
atomic_store_explicit(&mutex->owner_tid, tid, memory_order_relaxed); return0;
} return EBUSY;
}
#if !defined(__LP64__) // This exists only for backward binary compatibility on 32 bit platforms. // (This function never existed for LP64.) extern"C"int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex_interface, unsigned ms) {
timespec ts;
timespec_from_ms(ts, ms);
timespec abs_timeout;
absolute_timespec_from_timespec(abs_timeout, ts, CLOCK_MONOTONIC); int error = NonPI::MutexLockWithTimeout(__get_internal_mutex(mutex_interface), false,
&abs_timeout); if (error == ETIMEDOUT) {
error = EBUSY;
} return error;
} #endif
int pthread_mutex_destroy(pthread_mutex_t* mutex_interface) {
pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface);
uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed); if (__predict_false(IsMutexDestroyed(old_state))) { return HandleUsingDestroyedMutex(mutex_interface, __FUNCTION__);
} if (old_state == PI_MUTEX_STATE) { int result = PIMutexDestroy(mutex->ToPIMutex()); if (result == 0) {
mutex->FreePIMutex();
atomic_store(&mutex->state, 0xffff);
} return result;
} // Store 0xffff to make the mutex unusable. Although POSIX standard says it is undefined // behavior to destroy a locked mutex, we prefer not to change mutex->state in that situation. if (MUTEX_STATE_BITS_IS_UNLOCKED(old_state) &&
atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, 0xffff,
memory_order_relaxed, memory_order_relaxed)) { return0;
} return EBUSY;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-28)
¤
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.