/* * Lock a mutex of type NORMAL. * * As noted above, there are three states: * 0 (unlocked, no contention) * 1 (locked, no contention) * 2 (locked, contention) * * Non-recursive mutexes don't use the thread-id or counter fields, and the * "type" value is zero, so the only bits that will be set are the ones in * the lock state field. */ static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_internal_t* mutex, uint16_t shared, bool use_realtime_clock, const timespec* abs_timeout_or_null) { if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { return 0; } int result = check_timespec(abs_timeout_or_null, true); if (result != 0) { return result; } ScopedTrace trace("Contending for pthread mutex"); const uint16_t unlocked = shared | MUTEX_STATE_BITS_UNLOCKED; const uint16_t locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED; // 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; } } return 0; }
int pthread_mutex_lock(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); uint16_t mtype = (old_state & MUTEX_TYPE_MASK); uint16_t shared = (old_state & MUTEX_SHARED_MASK); // Avoid slowing down fast path of normal mutex lock operation. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { return 0; } } return __pthread_mutex_lock_with_timeout(mutex, NULL, 0); }
int pthread_mutex_trylock(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); uint16_t mtype = (old_state & MUTEX_TYPE_MASK); uint16_t shared = (old_state & MUTEX_SHARED_MASK); const uint16_t unlocked = mtype | shared | MUTEX_STATE_BITS_UNLOCKED; const uint16_t locked_uncontended = mtype | shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED; // Handle common case first. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { return __pthread_normal_mutex_trylock(mutex, shared); } // 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 __recursive_increment(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); return 0; } 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 == NULL) { 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); uint16_t shared = (old_state & MUTEX_SHARED_MASK); // Avoid slowing down fast path of normal mutex lock operation. if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) { if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) { return 0; } } return __pthread_mutex_lock_with_timeout(mutex, false, nullptr); }