blob: 4ed7285450b5f1beb55ae8255120208a29471fc9 [file] [log] [blame]
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/sem.h"
#include "pico/time.h"
void sem_init(semaphore_t *sem, int16_t initial_permits, int16_t max_permits) {
lock_init(&sem->core, next_striped_spin_lock_num());
sem->permits = initial_permits;
sem->max_permits = max_permits;
__mem_fence_release();
}
int __time_critical_func(sem_available)(semaphore_t *sem) {
return *(volatile typeof(sem->permits) *) &sem->permits;
}
void __time_critical_func(sem_acquire_blocking)(semaphore_t *sem) {
bool block = true;
do {
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
if (sem->permits > 0) {
sem->permits--;
__sev();
block = false;
}
spin_unlock(sem->core.spin_lock, save);
if (block) {
__wfe();
}
} while (block);
}
bool __time_critical_func(sem_acquire_timeout_ms)(semaphore_t *sem, uint32_t timeout_ms) {
bool block = true;
absolute_time_t target = nil_time;
do {
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
if (sem->permits > 0) {
sem->permits--;
__sev();
block = false;
}
spin_unlock(sem->core.spin_lock, save);
if (block) {
if (is_nil_time(target)) {
target = make_timeout_time_ms(timeout_ms);
}
if (best_effort_wfe_or_timeout(target)) {
return false;
}
}
} while (block);
return true;
}
// todo this should really have a blocking variant for when permits are maxed out
bool __time_critical_func(sem_release)(semaphore_t *sem) {
bool rc;
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
int32_t count = sem->permits;
if (count < sem->max_permits) {
sem->permits = count + 1;
__sev();
rc = true;
} else {
rc = false;
}
spin_unlock(sem->core.spin_lock, save);
return rc;
}
void __time_critical_func(sem_reset)(semaphore_t *sem, int16_t permits) {
assert(permits >= 0 && permits <= sem->max_permits);
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
if (permits > sem->permits) __sev();
sem->permits = permits;
spin_unlock(sem->core.spin_lock, save);
}