use core::{fmt, ops};
use tokenlock::UnsyncTokenLock;
use crate::{
error::BadContextError,
utils::{intrusive_list::CellLike, Init},
PortThreading,
};
pub(super) struct CpuLockTag<Traits>(Traits);
pub(super) type CpuLockToken<Traits> = tokenlock::UnsyncSingletonToken<CpuLockTag<Traits>>;
pub(super) type CpuLockKeyhole<Traits> = tokenlock::SingletonTokenId<CpuLockTag<Traits>>;
pub(super) struct CpuLockCell<Traits, T: ?Sized>(UnsyncTokenLock<T, CpuLockKeyhole<Traits>>);
impl<Traits, T> CpuLockCell<Traits, T> {
#[allow(dead_code)]
pub(super) const fn new(x: T) -> Self {
Self(UnsyncTokenLock::new(CpuLockKeyhole::INIT, x))
}
}
impl<Traits: PortThreading, T: ?Sized> CpuLockCell<Traits, T> {
pub(super) fn get_and_debug_fmt(&self) -> impl fmt::Debug + '_
where
T: Clone + fmt::Debug,
{
self.debug_fmt_with(|x, f| x.fmt(f))
}
pub(super) fn debug_fmt_with<'a, F: 'a + Fn(T, &mut fmt::Formatter) -> fmt::Result>(
&'a self,
f: F,
) -> impl fmt::Debug + 'a
where
T: Clone,
{
struct DebugFmtWith<'a, Traits, T: ?Sized, F> {
cell: &'a CpuLockCell<Traits, T>,
f: F,
}
impl<Traits: PortThreading, T: Clone, F: Fn(T, &mut fmt::Formatter) -> fmt::Result>
fmt::Debug for DebugFmtWith<'_, Traits, T, F>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Ok(lock) = lock_cpu() {
let inner = self.cell.0.read(&*lock).clone();
drop(lock);
f.write_str("CpuLockCell(")?;
(self.f)(inner, f)?;
f.write_str(")")
} else {
f.write_str("CpuLockCell(< locked >)")
}
}
}
DebugFmtWith { cell: self, f }
}
pub(super) fn debug_fmt_with_ref<'a, F: 'a + Fn(&T, &mut fmt::Formatter) -> fmt::Result>(
&'a self,
f: F,
) -> impl fmt::Debug + 'a {
struct DebugFmtWithRef<'a, Traits, T: ?Sized, F> {
cell: &'a CpuLockCell<Traits, T>,
f: F,
}
impl<Traits: PortThreading, T: ?Sized, F: Fn(&T, &mut fmt::Formatter) -> fmt::Result>
fmt::Debug for DebugFmtWithRef<'_, Traits, T, F>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Ok(lock) = lock_cpu() {
f.write_str("CpuLockCell(")?;
(self.f)(self.cell.0.read(&*lock), f)?;
f.write_str(")")
} else {
f.write_str("CpuLockCell(< locked >)")
}
}
}
DebugFmtWithRef { cell: self, f }
}
}
impl<Traits: PortThreading, T: fmt::Debug> fmt::Debug for CpuLockCell<Traits, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.debug_fmt_with_ref(|x, f| x.fmt(f)).fmt(f)
}
}
impl<Traits, T: Init> Init for CpuLockCell<Traits, T> {
const INIT: Self = Self(Init::INIT);
}
impl<Traits, T> ops::Deref for CpuLockCell<Traits, T> {
type Target = UnsyncTokenLock<T, CpuLockKeyhole<Traits>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<Traits, T> ops::DerefMut for CpuLockCell<Traits, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a, Element: Clone, Traits: PortThreading> CellLike<&'a mut CpuLockGuard<Traits>>
for CpuLockCell<Traits, Element>
{
type Target = Element;
fn get(&self, key: &&'a mut CpuLockGuard<Traits>) -> Self::Target {
(**self).get(&***key)
}
fn set(&self, key: &mut &'a mut CpuLockGuard<Traits>, value: Self::Target) {
CellLike::set(&**self, &mut &mut ***key, value);
}
fn modify<T>(
&self,
key: &mut &'a mut CpuLockGuard<Traits>,
f: impl FnOnce(&mut Self::Target) -> T,
) -> T {
CellLike::modify(&**self, &mut &mut ***key, f)
}
}
impl<'a, Element: Clone, Traits: PortThreading> CellLike<CpuLockTokenRefMut<'a, Traits>>
for CpuLockCell<Traits, Element>
{
type Target = Element;
fn get(&self, key: &CpuLockTokenRefMut<'a, Traits>) -> Self::Target {
(**self).get(&**key)
}
fn set(&self, key: &mut CpuLockTokenRefMut<'a, Traits>, value: Self::Target) {
CellLike::set(&**self, &mut &mut **key, value);
}
fn modify<T>(
&self,
key: &mut CpuLockTokenRefMut<'a, Traits>,
f: impl FnOnce(&mut Self::Target) -> T,
) -> T {
CellLike::modify(&**self, &mut &mut **key, f)
}
}
pub(super) fn lock_cpu<Traits: PortThreading>() -> Result<CpuLockGuard<Traits>, BadContextError> {
if unsafe { Traits::try_enter_cpu_lock() } {
Ok(unsafe { assume_cpu_lock() })
} else {
Err(BadContextError::BadContext)
}
}
pub(super) unsafe fn assume_cpu_lock<Traits: PortThreading>() -> CpuLockGuard<Traits> {
debug_assert!(Traits::is_cpu_lock_active());
CpuLockGuard {
token: unsafe { CpuLockToken::new_unchecked() },
}
}
pub(super) struct CpuLockGuard<Traits: PortThreading> {
token: CpuLockToken<Traits>,
}
impl<Traits: PortThreading> CpuLockGuard<Traits> {
pub(super) fn borrow_mut(&mut self) -> CpuLockTokenRefMut<'_, Traits> {
self.token.borrow_mut()
}
}
impl<Traits: PortThreading> Drop for CpuLockGuard<Traits> {
fn drop(&mut self) {
unsafe {
Traits::leave_cpu_lock();
}
}
}
impl<Traits: PortThreading> ops::Deref for CpuLockGuard<Traits> {
type Target = CpuLockToken<Traits>;
fn deref(&self) -> &Self::Target {
&self.token
}
}
impl<Traits: PortThreading> ops::DerefMut for CpuLockGuard<Traits> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.token
}
}
pub(super) type CpuLockTokenRefMut<'a, Traits> =
tokenlock::UnsyncSingletonTokenRefMut<'a, CpuLockTag<Traits>>;
pub(super) type CpuLockTokenRef<'a, Traits> =
tokenlock::UnsyncSingletonTokenRef<'a, CpuLockTag<Traits>>;