use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::signal::Signal;
use crate::critical::CriticalSlots;
use crate::free_slots::FreeSlots;
use crate::object::{ObjectGuard, ObjectPool};
pub struct AsyncObjectPool<T, const N: usize, B = CriticalSlots<N>> {
inner: ObjectPool<T, N, B>,
signal: Signal<CriticalSectionRawMutex, ()>,
}
unsafe impl<T, const N: usize, B: FreeSlots<N>> Sync for AsyncObjectPool<T, N, B> {}
pub struct StaticAsyncObjectPool<T, const N: usize, B: FreeSlots<N> = CriticalSlots<N>> {
data: UnsafeCell<MaybeUninit<AsyncObjectPool<T, N, B>>>,
}
unsafe impl<T, const N: usize, B: FreeSlots<N>> Sync for StaticAsyncObjectPool<T, N, B> {}
impl<T: Clone, const N: usize, B: FreeSlots<N>> StaticAsyncObjectPool<T, N, B> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
data: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub fn init_pool(&'static self, init: T) -> &'static AsyncObjectPool<T, N, B> {
assert!(N > 0, "Pool capacity must be greater than 0");
unsafe {
let ptr: *mut AsyncObjectPool<T, N, B> =
self.data.get().cast::<AsyncObjectPool<T, N, B>>();
ptr.write(AsyncObjectPool {
inner: ObjectPool {
slots: UnsafeCell::new(MaybeUninit::uninit()),
free: B::new(),
},
signal: Signal::new(),
});
(*ptr).inner.free.fill(N);
for i in 0..N {
(*ptr).inner.slot_mut_ptr(i).write(init.clone());
}
&*ptr
}
}
}
impl<T, const N: usize, B: FreeSlots<N>> AsyncObjectPool<T, N, B> {
pub async fn request(&'static self) -> AsyncObjectGuard<T, N, B> {
loop {
match self.inner.request() {
Some(guard) => {
return AsyncObjectGuard {
guard: Some(guard),
signal: &self.signal,
};
}
None => self.signal.wait().await,
}
}
}
}
#[must_use]
pub struct AsyncObjectGuard<T: 'static, const N: usize, B: FreeSlots<N> + 'static> {
guard: Option<ObjectGuard<T, N, B>>,
signal: &'static Signal<CriticalSectionRawMutex, ()>,
}
impl<T, const N: usize, B: FreeSlots<N>> Deref for AsyncObjectGuard<T, N, B> {
type Target = T;
fn deref(&self) -> &T {
self.guard.as_ref().unwrap()
}
}
impl<T, const N: usize, B: FreeSlots<N>> DerefMut for AsyncObjectGuard<T, N, B> {
fn deref_mut(&mut self) -> &mut T {
self.guard.as_mut().unwrap()
}
}
impl<T, const N: usize, B: FreeSlots<N>> Drop for AsyncObjectGuard<T, N, B> {
fn drop(&mut self) {
drop(self.guard.take());
self.signal.signal(());
}
}
unsafe impl<T: Send, const N: usize, B: FreeSlots<N>> Send for AsyncObjectGuard<T, N, B> {}