| /* |
| * 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); |
| } |