use core::{
cell::{Cell, UnsafeCell},
future::Future,
ops,
pin::Pin,
task::{Context, Poll, Waker},
};
use heapless::LinearMap;
use ockam_core::compat::sync::Mutex as CriticalSection;
pub struct WakerSet {
inner: CriticalSection<Inner>,
}
impl WakerSet {
pub fn new() -> Self {
Self {
inner: CriticalSection::new(Inner::new()),
}
}
pub fn cancel(&self, key: usize) -> bool {
let mut guard = self.inner.lock().unwrap();
guard.cancel(key)
}
pub fn notify_any(&self) -> bool {
let mut guard = self.inner.lock().unwrap();
guard.notify_any()
}
#[allow(dead_code)]
pub fn notify_one(&self) -> bool {
let mut guard = self.inner.lock().unwrap();
guard.notify_one()
}
pub fn insert(&self, cx: &Context<'_>) -> usize {
let mut guard = self.inner.lock().unwrap();
guard.insert(cx)
}
pub fn remove(&self, key: usize) {
let mut guard = self.inner.lock().unwrap();
guard.remove(key)
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
enum Notify {
Any,
One,
}
struct Inner {
counter: usize,
entries: LinearMap<usize, Option<Waker>, 8>,
notifiable: usize,
}
impl Inner {
const fn new() -> Self {
Self {
counter: 0,
entries: LinearMap::new(),
notifiable: 0,
}
}
fn cancel(&mut self, key: usize) -> bool {
match self.entries.remove(&key) {
Some(_) => self.notifiable -= 1,
None => {
for (_, opt_waker) in self.entries.iter_mut() {
if let Some(w) = opt_waker.take() {
w.wake();
self.notifiable -= 1;
return true;
}
}
}
}
false
}
fn notify_any(&mut self) -> bool {
self.notify(Notify::Any)
}
fn notify_one(&mut self) -> bool {
self.notify(Notify::One)
}
fn notify(&mut self, n: Notify) -> bool {
let mut notified = false;
for (_, opt_waker) in self.entries.iter_mut() {
if let Some(w) = opt_waker.take() {
w.wake();
self.notifiable -= 1;
notified = true;
if n == Notify::One {
break;
}
}
if n == Notify::Any {
break;
}
}
notified
}
fn insert(&mut self, cx: &Context<'_>) -> usize {
let w = cx.waker().clone();
let key = self.counter;
self.entries.insert(key, Some(w)).expect("OOM");
self.counter += 1;
self.notifiable += 1;
key
}
fn remove(&mut self, key: usize) {
if self.entries.remove(&key).is_some() {
self.notifiable -= 1;
}
}
}
pub struct Mutex<T> {
locked: Cell<bool>,
value: UnsafeCell<T>,
wakers: WakerSet,
}
#[allow(unsafe_code)]
unsafe impl<T> Send for Mutex<T> {}
#[allow(unsafe_code)]
unsafe impl<T> Sync for Mutex<T> {}
impl<T> Mutex<T> {
pub fn new(t: T) -> Self {
Self {
locked: Cell::new(false),
value: UnsafeCell::new(t),
wakers: WakerSet::new(),
}
}
pub async fn lock(&self) -> MutexGuard<'_, T> {
struct Lock<'a, T> {
mutex: &'a Mutex<T>,
opt_key: Option<usize>,
}
impl<'a, T> Future for Lock<'a, T> {
type Output = MutexGuard<'a, T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(key) = self.opt_key.take() {
self.mutex.wakers.remove(key);
}
match self.mutex.try_lock() {
Some(guard) => Poll::Ready(guard),
None => {
self.opt_key = Some(self.mutex.wakers.insert(cx));
Poll::Pending
}
}
}
}
impl<T> Drop for Lock<'_, T> {
fn drop(&mut self) {
if let Some(key) = self.opt_key {
self.mutex.wakers.cancel(key);
}
}
}
Lock {
mutex: self,
opt_key: None,
}
.await
}
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
if !self.locked.get() {
self.locked.set(true);
Some(MutexGuard(self))
} else {
None
}
}
}
pub struct MutexGuard<'a, T>(&'a Mutex<T>);
impl<T> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
self.0.locked.set(false);
self.0.wakers.notify_any();
}
}
impl<T> ops::Deref for MutexGuard<'_, T> {
type Target = T;
#[allow(unsafe_code)]
fn deref(&self) -> &T {
unsafe { &*self.0.value.get() }
}
}
impl<T> ops::DerefMut for MutexGuard<'_, T> {
#[allow(unsafe_code)]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.value.get() }
}
}