int pthread_mutex_unlock(pthread_mutex_t *mutex) { if (NACL_UNLIKELY(mutex->mutex_type != PTHREAD_MUTEX_FAST_NP)) { if ((PTHREAD_MUTEX_RECURSIVE_NP == mutex->mutex_type) && (0 != (--mutex->recursion_counter))) { /* * We assume that this thread owns the lock * (no verification for recursive locks), * so just decrement the counter, this thread is still the owner. */ return 0; } if ((PTHREAD_MUTEX_ERRORCHECK_NP == mutex->mutex_type) && (pthread_self() != mutex->owner_thread_id)) { /* Error - releasing a mutex that's free or owned by another thread. */ return EPERM; } /* Writing to owner_thread_id here must be done atomically. */ mutex->owner_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; mutex->recursion_counter = 0; } /* * Release the mutex. This atomic decrement executes the full * memory barrier that pthread_mutex_unlock() is required to * execute. */ int old_state = __sync_fetch_and_sub(&mutex->mutex_state, 1); if (NACL_UNLIKELY(old_state != LOCKED_WITHOUT_WAITERS)) { if (old_state == UNLOCKED) { /* * The mutex was not locked. mutex_state is now -1 and the * mutex is likely unusable, but that is the caller's fault for * using the mutex interface incorrectly. */ return EPERM; } /* * We decremented mutex_state from LOCKED_WITH_WAITERS to * LOCKED_WITHOUT_WAITERS. We must now release the mutex fully. * * No further memory barrier is required for the following * modification of mutex_state. The full memory barrier from the * atomic decrement acts as a release memory barrier for the * following modification. * * TODO(mseaborn): Change the following store to use an atomic * store builtin when this is available in all the NaCl * toolchains. For now, PNaCl converts the volatile store to an * atomic store. */ mutex->mutex_state = UNLOCKED; int woken; __nc_irt_futex.futex_wake(&mutex->mutex_state, 1, &woken); } return 0; }
static int mutex_lock_nonrecursive(pthread_mutex_t *mutex, int try_only, const struct timespec *abstime) { /* * Try to claim the mutex. This compare-and-swap executes the full * memory barrier that pthread_mutex_lock() is required to execute. */ int old_state = __sync_val_compare_and_swap(&mutex->mutex_state, UNLOCKED, LOCKED_WITHOUT_WAITERS); if (NACL_UNLIKELY(old_state != UNLOCKED)) { if (try_only) { return EBUSY; } if (abstime != NULL && (abstime->tv_nsec < 0 || 1000000000 <= abstime->tv_nsec)) { return EINVAL; } do { /* * If the state shows there are already waiters, or we can * update it to indicate that there are waiters, then wait. */ if (old_state == LOCKED_WITH_WAITERS || __sync_val_compare_and_swap(&mutex->mutex_state, LOCKED_WITHOUT_WAITERS, LOCKED_WITH_WAITERS) != UNLOCKED) { int rc = __libnacl_irt_futex.futex_wait_abs(&mutex->mutex_state, LOCKED_WITH_WAITERS, abstime); if (abstime != NULL && rc == ETIMEDOUT) return ETIMEDOUT; } /* * Try again to claim the mutex. On this try, we must set * mutex_state to LOCKED_WITH_WAITERS rather than * LOCKED_WITHOUT_WAITERS. We could have been woken up when * many threads are in the wait queue for the mutex. */ old_state = __sync_val_compare_and_swap(&mutex->mutex_state, UNLOCKED, LOCKED_WITH_WAITERS); } while (old_state != UNLOCKED); } return 0; }