use core::{
fmt, hash,
marker::PhantomData,
sync::atomic::{AtomicU32, Ordering},
};
use super::{
state, task, timeout, utils,
wait::{WaitPayload, WaitQueue},
BadIdError, GetEventGroupError, Id, Kernel, PollEventGroupError, Port, UpdateEventGroupError,
WaitEventGroupError, WaitEventGroupTimeoutError,
};
use crate::{time::Duration, utils::Init};
pub type EventGroupBits = u32;
pub type AtomicEventGroupBits = AtomicU32;
#[doc(include = "../common.md")]
#[repr(transparent)]
pub struct EventGroup<System>(Id, PhantomData<System>);
impl<System> Clone for EventGroup<System> {
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<System> Copy for EventGroup<System> {}
impl<System> PartialEq for EventGroup<System> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<System> Eq for EventGroup<System> {}
impl<System> hash::Hash for EventGroup<System> {
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
hash::Hash::hash(&self.0, state);
}
}
impl<System> fmt::Debug for EventGroup<System> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("EventGroup").field(&self.0).finish()
}
}
bitflags::bitflags! {
pub struct EventGroupWaitFlags: u8 {
const ALL = 1 << 0;
const CLEAR = 1 << 1;
}
}
impl<System> EventGroup<System> {
pub const unsafe fn from_id(id: Id) -> Self {
Self(id, PhantomData)
}
pub const fn id(self) -> Id {
self.0
}
}
impl<System: Kernel> EventGroup<System> {
fn event_group_cb(self) -> Result<&'static EventGroupCb<System>, BadIdError> {
System::get_event_group_cb(self.0.get() - 1).ok_or(BadIdError::BadId)
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
pub fn set(self, bits: EventGroupBits) -> Result<(), UpdateEventGroupError> {
let lock = utils::lock_cpu::<System>()?;
let event_group_cb = self.event_group_cb()?;
set(event_group_cb, lock, bits);
Ok(())
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
pub fn clear(self, bits: EventGroupBits) -> Result<(), UpdateEventGroupError> {
let mut lock = utils::lock_cpu::<System>()?;
let event_group_cb = self.event_group_cb()?;
event_group_cb.bits.replace_with(&mut *lock, |b| *b & !bits);
Ok(())
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
pub fn get(self) -> Result<EventGroupBits, GetEventGroupError> {
let lock = utils::lock_cpu::<System>()?;
let event_group_cb = self.event_group_cb()?;
Ok(event_group_cb.bits.get(&*lock))
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
pub fn wait(
self,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
) -> Result<EventGroupBits, WaitEventGroupError> {
let lock = utils::lock_cpu::<System>()?;
state::expect_waitable_context::<System>()?;
let event_group_cb = self.event_group_cb()?;
wait(event_group_cb, lock, bits, flags)
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
pub fn wait_timeout(
self,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
timeout: Duration,
) -> Result<EventGroupBits, WaitEventGroupTimeoutError> {
let time32 = timeout::time32_from_duration(timeout)?;
let lock = utils::lock_cpu::<System>()?;
state::expect_waitable_context::<System>()?;
let event_group_cb = self.event_group_cb()?;
wait_timeout(event_group_cb, lock, bits, flags, time32)
}
#[cfg_attr(not(feature = "inline_syscall"), inline(never))]
pub fn poll(
self,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
) -> Result<EventGroupBits, PollEventGroupError> {
let lock = utils::lock_cpu::<System>()?;
let event_group_cb = self.event_group_cb()?;
poll(event_group_cb, lock, bits, flags)
}
}
#[doc(hidden)]
pub struct EventGroupCb<System: Port, EventGroupBits: 'static = self::EventGroupBits> {
pub(super) bits: utils::CpuLockCell<System, EventGroupBits>,
pub(super) wait_queue: WaitQueue<System>,
}
impl<System: Port, EventGroupBits: Init + 'static> Init for EventGroupCb<System, EventGroupBits> {
const INIT: Self = Self {
bits: Init::INIT,
wait_queue: Init::INIT,
};
}
impl<System: Kernel, EventGroupBits: fmt::Debug + 'static> fmt::Debug
for EventGroupCb<System, EventGroupBits>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EventGroupCb")
.field("self", &(self as *const _))
.field("bits", &self.bits)
.field("wait_queue", &self.wait_queue)
.finish()
}
}
fn poll<System: Kernel>(
event_group_cb: &'static EventGroupCb<System>,
mut lock: utils::CpuLockGuard<System>,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
) -> Result<EventGroupBits, PollEventGroupError> {
if let Some(original_value) = poll_core(event_group_cb.bits.write(&mut *lock), bits, flags) {
Ok(original_value)
} else {
Err(PollEventGroupError::Timeout)
}
}
fn wait<System: Kernel>(
event_group_cb: &'static EventGroupCb<System>,
mut lock: utils::CpuLockGuard<System>,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
) -> Result<EventGroupBits, WaitEventGroupError> {
if let Some(original_value) = poll_core(event_group_cb.bits.write(&mut *lock), bits, flags) {
Ok(original_value)
} else {
let result = event_group_cb.wait_queue.wait(
lock.borrow_mut(),
WaitPayload::EventGroupBits {
bits,
flags,
orig_bits: Init::INIT,
},
)?;
if let WaitPayload::EventGroupBits { orig_bits, .. } = result {
Ok(orig_bits.load(Ordering::Relaxed))
} else {
unreachable!()
}
}
}
fn wait_timeout<System: Kernel>(
event_group_cb: &'static EventGroupCb<System>,
mut lock: utils::CpuLockGuard<System>,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
time32: timeout::Time32,
) -> Result<EventGroupBits, WaitEventGroupTimeoutError> {
if let Some(original_value) = poll_core(event_group_cb.bits.write(&mut *lock), bits, flags) {
Ok(original_value)
} else {
let result = event_group_cb.wait_queue.wait_timeout(
lock.borrow_mut(),
WaitPayload::EventGroupBits {
bits,
flags,
orig_bits: Init::INIT,
},
time32,
)?;
if let WaitPayload::EventGroupBits { orig_bits, .. } = result {
Ok(orig_bits.load(Ordering::Relaxed))
} else {
unreachable!()
}
}
}
fn poll_core(
event_group_bits: &mut EventGroupBits,
bits: EventGroupBits,
flags: EventGroupWaitFlags,
) -> Option<EventGroupBits> {
let success = if flags.contains(EventGroupWaitFlags::ALL) {
(*event_group_bits & bits) == bits
} else {
(*event_group_bits & bits) != 0
};
if success {
let original_value = *event_group_bits;
if flags.contains(EventGroupWaitFlags::CLEAR) {
*event_group_bits &= !bits;
}
Some(original_value)
} else {
None
}
}
fn set<System: Kernel>(
event_group_cb: &'static EventGroupCb<System>,
mut lock: utils::CpuLockGuard<System>,
added_bits: EventGroupBits,
) {
let mut event_group_bits = event_group_cb.bits.get(&*lock);
if (event_group_bits | added_bits) == event_group_bits {
return;
}
event_group_bits |= added_bits;
let mut woke_up_any = false;
event_group_cb
.wait_queue
.wake_up_all_conditional(lock.borrow_mut(), |wait_payload| match wait_payload {
WaitPayload::EventGroupBits {
bits,
flags,
orig_bits,
} => {
if let Some(orig) = poll_core(&mut event_group_bits, *bits, *flags) {
woke_up_any = true;
orig_bits.store(orig, Ordering::Relaxed);
true
} else {
false
}
}
_ => unreachable!(),
});
event_group_cb.bits.replace(&mut *lock, event_group_bits);
if woke_up_any {
task::unlock_cpu_and_check_preemption(lock);
}
}