use core::ptr;
use crate::kernel::queue::{
queueQUEUE_TYPE_BINARY_SEMAPHORE, queueSEND_TO_BACK, uxQueueMessagesWaiting,
uxQueueMessagesWaitingFromISR, xQueueCreateCountingSemaphoreStatic, xQueueGenericCreateStatic,
xQueueGenericSend, xQueueGiveFromISR, xQueueReceiveFromISR, xQueueSemaphoreTake, QueueHandle_t,
StaticQueue_t,
};
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
use crate::kernel::queue::{vQueueDelete, xQueueCreateCountingSemaphore, xQueueGenericCreate};
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
use crate::sync::is_in_isr;
use crate::sync::task::TaskContext;
use crate::sync::{assert_can_block, assert_task_context};
use crate::types::*;
pub struct BinarySemaphore {
handle: QueueHandle_t,
owns_handle: bool,
}
unsafe impl Sync for BinarySemaphore {}
unsafe impl Send for BinarySemaphore {}
impl BinarySemaphore {
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn new(_context: &TaskContext) -> Option<Self> {
assert_task_context("BinarySemaphore::new");
let handle = unsafe { xQueueGenericCreate(1, 0, queueQUEUE_TYPE_BINARY_SEMAPHORE) };
if handle.is_null() {
None
} else {
Some(Self {
handle,
owns_handle: true,
})
}
}
pub fn new_static(
_context: &TaskContext,
sem_buffer: &'static mut StaticQueue_t,
) -> Option<Self> {
assert_task_context("BinarySemaphore::new_static");
let handle = unsafe {
xQueueGenericCreateStatic(
1,
0,
ptr::null_mut(),
sem_buffer as *mut StaticQueue_t,
queueQUEUE_TYPE_BINARY_SEMAPHORE,
)
};
if handle.is_null() {
None
} else {
Some(Self {
handle,
owns_handle: true,
})
}
}
pub fn take(&self, context: &TaskContext) -> bool {
self.take_timeout(context, portMAX_DELAY)
}
pub fn try_take(&self, context: &TaskContext) -> bool {
self.take_timeout(context, 0)
}
pub fn take_timeout(&self, _context: &TaskContext, ticks: TickType_t) -> bool {
assert_can_block("BinarySemaphore::take_timeout", ticks);
unsafe { xQueueSemaphoreTake(self.handle, ticks) == pdTRUE }
}
pub fn give(&self, _context: &TaskContext) -> bool {
assert_task_context("BinarySemaphore::give");
unsafe { xQueueGenericSend(self.handle, core::ptr::null(), 0, queueSEND_TO_BACK) == pdTRUE }
}
pub unsafe fn give_from_isr(&self, higher_priority_task_woken: &mut BaseType_t) -> bool {
unsafe { xQueueGiveFromISR(self.handle, higher_priority_task_woken) == pdPASS }
}
pub unsafe fn take_from_isr(&self, higher_priority_task_woken: &mut BaseType_t) -> bool {
unsafe {
xQueueReceiveFromISR(self.handle, ptr::null_mut(), higher_priority_task_woken) == pdPASS
}
}
pub fn is_available(&self, _context: &TaskContext) -> bool {
assert_task_context("BinarySemaphore::is_available");
unsafe { uxQueueMessagesWaiting(self.handle) != 0 }
}
pub unsafe fn is_available_from_isr(&self) -> bool {
unsafe { uxQueueMessagesWaitingFromISR(self.handle) != 0 }
}
pub unsafe fn raw_handle(&self) -> QueueHandle_t {
self.handle
}
pub fn into_raw(mut self) -> QueueHandle_t {
self.owns_handle = false;
self.handle
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn delete(mut self, _context: &TaskContext) {
assert_task_context("BinarySemaphore::delete");
unsafe { vQueueDelete(self.handle) };
self.owns_handle = false;
}
}
pub struct CountingSemaphore {
handle: QueueHandle_t,
owns_handle: bool,
}
unsafe impl Sync for CountingSemaphore {}
unsafe impl Send for CountingSemaphore {}
impl CountingSemaphore {
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn new(
_context: &TaskContext,
max_count: UBaseType_t,
initial_count: UBaseType_t,
) -> Option<Self> {
assert_task_context("CountingSemaphore::new");
Self::validate_counts(max_count, initial_count)?;
let handle = unsafe { xQueueCreateCountingSemaphore(max_count, initial_count) };
if handle.is_null() {
None
} else {
Some(Self {
handle,
owns_handle: true,
})
}
}
pub fn new_static(
_context: &TaskContext,
max_count: UBaseType_t,
initial_count: UBaseType_t,
sem_buffer: &'static mut StaticQueue_t,
) -> Option<Self> {
assert_task_context("CountingSemaphore::new_static");
Self::validate_counts(max_count, initial_count)?;
let handle = unsafe {
xQueueCreateCountingSemaphoreStatic(
max_count,
initial_count,
sem_buffer as *mut StaticQueue_t,
)
};
if handle.is_null() {
None
} else {
Some(Self {
handle,
owns_handle: true,
})
}
}
fn validate_counts(max_count: UBaseType_t, initial_count: UBaseType_t) -> Option<()> {
if max_count == 0 || initial_count > max_count {
None
} else {
Some(())
}
}
pub fn take(&self, context: &TaskContext) -> bool {
self.take_timeout(context, portMAX_DELAY)
}
pub fn try_take(&self, context: &TaskContext) -> bool {
self.take_timeout(context, 0)
}
pub fn take_timeout(&self, _context: &TaskContext, ticks: TickType_t) -> bool {
assert_can_block("CountingSemaphore::take_timeout", ticks);
unsafe { xQueueSemaphoreTake(self.handle, ticks) == pdTRUE }
}
pub fn give(&self, _context: &TaskContext) -> bool {
assert_task_context("CountingSemaphore::give");
unsafe { xQueueGenericSend(self.handle, core::ptr::null(), 0, queueSEND_TO_BACK) == pdTRUE }
}
pub unsafe fn give_from_isr(&self, higher_priority_task_woken: &mut BaseType_t) -> bool {
unsafe { xQueueGiveFromISR(self.handle, higher_priority_task_woken) == pdPASS }
}
pub unsafe fn take_from_isr(&self, higher_priority_task_woken: &mut BaseType_t) -> bool {
unsafe {
xQueueReceiveFromISR(self.handle, ptr::null_mut(), higher_priority_task_woken) == pdPASS
}
}
pub fn count(&self, _context: &TaskContext) -> UBaseType_t {
assert_task_context("CountingSemaphore::count");
unsafe { uxQueueMessagesWaiting(self.handle) }
}
pub unsafe fn count_from_isr(&self) -> UBaseType_t {
unsafe { uxQueueMessagesWaitingFromISR(self.handle) }
}
pub unsafe fn raw_handle(&self) -> QueueHandle_t {
self.handle
}
pub fn into_raw(mut self) -> QueueHandle_t {
self.owns_handle = false;
self.handle
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
pub fn delete(mut self, _context: &TaskContext) {
assert_task_context("CountingSemaphore::delete");
unsafe { vQueueDelete(self.handle) };
self.owns_handle = false;
}
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
impl Drop for BinarySemaphore {
fn drop(&mut self) {
if self.owns_handle && !is_in_isr() {
unsafe { vQueueDelete(self.handle) };
self.owns_handle = false;
}
}
}
#[cfg(any(feature = "alloc", feature = "heap-4", feature = "heap-5"))]
impl Drop for CountingSemaphore {
fn drop(&mut self) {
if self.owns_handle && !is_in_isr() {
unsafe { vQueueDelete(self.handle) };
self.owns_handle = false;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counting_semaphore_rejects_invalid_counts() {
assert!(CountingSemaphore::validate_counts(0, 0).is_none());
assert!(CountingSemaphore::validate_counts(2, 3).is_none());
assert!(CountingSemaphore::validate_counts(2, 2).is_some());
}
#[test]
fn semaphore_handles_are_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<BinarySemaphore>();
assert_send_sync::<CountingSemaphore>();
}
}