kithara-platform 0.0.1-alpha5

Cross-platform primitives (sync, time, thread) for native and wasm32.
Documentation
use core::{fmt, num::NonZeroUsize};

use ::loom::sync::{
    Arc as LoomArc,
    atomic::{AtomicBool, Ordering},
};

use crate::common::{thread_id::thread_id_hash, time::Duration};

pub(crate) type Thread = ::loom::thread::Thread;

#[derive(derive_more::Debug)]
pub struct JoinHandle<T> {
    #[debug(skip)]
    inner: ::loom::thread::JoinHandle<T>,
    #[debug(skip)]
    finished: LoomArc<AtomicBool>,
}

impl<T> JoinHandle<T> {
    #[must_use]
    pub fn is_finished(&self) -> bool {
        self.finished.load(Ordering::Acquire)
    }

    delegate::delegate! {
        to self.inner {
            pub fn join(self) -> std::thread::Result<T>;
            #[must_use]
            pub fn thread(&self) -> &Thread;
        }
    }
}

#[inline]
pub(crate) fn yield_now() {
    ::loom::thread::yield_now();
}

#[inline]
#[must_use]
pub(crate) fn is_worker_thread() -> bool {
    false
}

#[inline]
#[must_use]
pub(crate) fn is_main_thread() -> bool {
    true
}

#[inline]
pub(crate) fn assert_main_thread(_label: &str) {}

#[inline]
pub(crate) fn assert_not_main_thread(_label: &str) {}

#[inline]
#[must_use]
pub(crate) fn current() -> Thread {
    ::loom::thread::current()
}

pub(crate) fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,
{
    let (finished, tracked) = tracked(f);
    JoinHandle {
        finished,
        inner: ::loom::thread::spawn(tracked),
    }
}

pub(crate) fn spawn_named_uncounted<F, T, N: Into<String>>(name: N, f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,
{
    let (finished, tracked) = tracked(f);
    match ::loom::thread::Builder::new()
        .name(name.into())
        .spawn(tracked)
    {
        Ok(inner) => JoinHandle { inner, finished },
        Err(error) => panic!("failed to spawn named loom thread: {error}"),
    }
}

fn tracked<F, T>(f: F) -> (LoomArc<AtomicBool>, impl FnOnce() -> T)
where
    F: FnOnce() -> T,
{
    let finished = LoomArc::new(AtomicBool::new(false));
    let worker_finished = LoomArc::clone(&finished);
    let run = move || {
        let _finished = Finished(worker_finished);
        f()
    };
    (finished, run)
}

struct Finished(LoomArc<AtomicBool>);

impl Drop for Finished {
    fn drop(&mut self) {
        self.0.store(true, Ordering::Release);
    }
}

#[track_caller]
pub(crate) fn sleep(duration: Duration) {
    panic!("thread::sleep({duration:?}) requires the flash backend when loom is enabled");
}

#[inline]
#[track_caller]
pub(crate) fn park() {
    crate::no_block::forbid("thread::park");
    ::loom::thread::park();
}

#[track_caller]
pub(crate) fn park_timeout(duration: Duration) {
    panic!("thread::park_timeout({duration:?}) requires flash when loom is enabled");
}

#[inline]
pub(crate) fn unpark(thread: &Thread) {
    thread.unpark();
}

#[inline]
#[must_use]
pub(crate) fn current_thread_id() -> u64 {
    thread_id_hash(current().id())
}

#[inline]
#[must_use]
pub(crate) fn available_parallelism() -> Option<NonZeroUsize> {
    NonZeroUsize::new(1)
}