blob: 45ede4dc880b2bf765329ee15c1d1e84c913f028 [file] [log] [blame]
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/mutex.h"
#include "pico/time.h"
static void mutex_init_internal(mutex_t *mtx, uint8_t recursion_state) {
lock_init(&mtx->core, next_striped_spin_lock_num());
mtx->owner = LOCK_INVALID_OWNER_ID;
mtx->recursion_state = recursion_state;
__mem_fence_release();
}
void mutex_init(mutex_t *mtx) {
mutex_init_internal(mtx, 0);
}
void recursive_mutex_init(mutex_t *mtx) {
mutex_init_internal(mtx, MAX_RECURSION_STATE);
}
void __time_critical_func(mutex_enter_blocking)(mutex_t *mtx) {
assert(mtx->core.spin_lock);
do {
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
lock_owner_id_t caller = lock_get_caller_owner_id();
if (mtx->owner == LOCK_INVALID_OWNER_ID) {
mtx->owner = caller;
if (mtx->recursion_state) {
assert(mtx->recursion_state == MAX_RECURSION_STATE);
mtx->recursion_state--;
}
} else if (mtx->owner == caller && mtx->recursion_state > 1) {
mtx->recursion_state--;
} else {
lock_internal_spin_unlock_with_wait(&mtx->core, save);
// spin lock already unlocked, so loop again
continue;
}
spin_unlock(mtx->core.spin_lock, save);
break;
} while (true);
}
bool __time_critical_func(mutex_try_enter)(mutex_t *mtx, uint32_t *owner_out) {
bool entered;
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
lock_owner_id_t caller = lock_get_caller_owner_id();
if (mtx->owner == LOCK_INVALID_OWNER_ID) {
mtx->owner = lock_get_caller_owner_id();
entered = true;
} else if (mtx->owner == caller && mtx->recursion_state > 1) {
mtx->recursion_state--;
entered = true;
} else {
if (owner_out) *owner_out = (uint32_t) mtx->owner;
entered = false;
}
spin_unlock(mtx->core.spin_lock, save);
return entered;
}
bool __time_critical_func(mutex_enter_timeout_ms)(mutex_t *mtx, uint32_t timeout_ms) {
return mutex_enter_block_until(mtx, make_timeout_time_ms(timeout_ms));
}
bool __time_critical_func(mutex_enter_timeout_us)(mutex_t *mtx, uint32_t timeout_us) {
return mutex_enter_block_until(mtx, make_timeout_time_us(timeout_us));
}
bool __time_critical_func(mutex_enter_block_until)(mutex_t *mtx, absolute_time_t until) {
assert(mtx->core.spin_lock);
do {
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
lock_owner_id_t caller = lock_get_caller_owner_id();
if (mtx->owner == LOCK_INVALID_OWNER_ID) {
mtx->owner = caller;
} else if (mtx->owner == caller && mtx->recursion_state > 1) {
mtx->recursion_state--;
} else {
if (lock_internal_spin_unlock_with_best_effort_wait_or_timeout(&mtx->core, save, until)) {
// timed out
return false;
} else {
// not timed out; spin lock already unlocked, so loop again
continue;
}
}
spin_unlock(mtx->core.spin_lock, save);
return true;
} while (true);
}
void __time_critical_func(mutex_exit)(mutex_t *mtx) {
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
assert(mtx->owner != LOCK_INVALID_OWNER_ID);
if (!mtx->recursion_state) {
mtx->owner = LOCK_INVALID_OWNER_ID;
lock_internal_spin_unlock_with_notify(&mtx->core, save);
} else {
mtx->recursion_state++;
assert(mtx->recursion_state);
if (mtx->recursion_state == MAX_RECURSION_STATE) {
mtx->owner = LOCK_INVALID_OWNER_ID;
lock_internal_spin_unlock_with_notify(&mtx->core, save);
} else {
spin_unlock(mtx->core.spin_lock, save);
}
}
}