use std::marker::PhantomData;
use std::sync::OnceLock;
use std::thread::{self, ThreadId};
static MAIN_THREAD_ID: OnceLock<ThreadId> = OnceLock::new();
pub fn designate_main_thread() {
let _ = MAIN_THREAD_ID.set(thread::current().id());
}
pub fn main_thread_id() -> Option<ThreadId> {
MAIN_THREAD_ID.get().copied()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MainThreadToken(PhantomData<*mut ()>);
impl MainThreadToken {
#[expect(
unsafe_code,
reason = "phantom data marker; !Send + !Sync prevents token leakage"
)]
pub unsafe fn new_unchecked() -> Self {
Self(PhantomData)
}
pub fn try_new() -> Option<Self> {
let designated = MAIN_THREAD_ID.get()?;
if *designated == thread::current().id() {
Some(Self(PhantomData))
} else {
None
}
}
}
pub use send_wrapper::SendWrapper;
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn worker_thread_never_obtains_token() {
let on_worker = thread::spawn(|| MainThreadToken::try_new().is_some())
.join()
.expect("worker thread");
assert!(!on_worker);
}
#[test]
fn try_new_returns_some_after_designation_on_same_thread() {
designate_main_thread();
match main_thread_id() {
Some(id) if id == thread::current().id() => {
assert!(MainThreadToken::try_new().is_some());
}
_ => {
assert!(MainThreadToken::try_new().is_none());
}
}
}
}