vtcode_commons/thread_safety.rs
1//! # Thread Safety Primitives
2//!
3//! Based on "Formal methods for the unsafe side of the Force" (Antithesis, 2026).
4//! Provides rigorously defined primitives for bridging FFI and multi-threaded boundaries.
5
6use std::marker::PhantomData;
7use std::sync::OnceLock;
8use std::thread::{self, ThreadId};
9
10/// Stores the `ThreadId` designated as the application's main thread.
11///
12/// Populated exactly once by [`designate_main_thread`]; subsequent calls are no-ops
13/// so that callers can re-assert designation from defensive initialization paths
14/// without panicking.
15static MAIN_THREAD_ID: OnceLock<ThreadId> = OnceLock::new();
16
17/// Designate the calling thread as the application's main thread.
18///
19/// Should be invoked once, early in `main`, before spawning any worker threads
20/// that may try to obtain a [`MainThreadToken`]. Subsequent calls have no effect.
21pub fn designate_main_thread() {
22 let _ = MAIN_THREAD_ID.set(thread::current().id());
23}
24
25/// Returns the `ThreadId` previously designated as the main thread, if any.
26pub fn main_thread_id() -> Option<ThreadId> {
27 MAIN_THREAD_ID.get().copied()
28}
29
30/// A witness of execution that exists solely on a designated "Main Thread".
31///
32/// In FFI contexts, many libraries (especially legacy C++ or UI frameworks)
33/// are not thread-safe and must only be initialized, called, or dropped from
34/// the same thread that originally created them.
35///
36/// `MainThreadToken` is a zero-sized proof carrier. Possessing it proves
37/// (at a type-system level) that the holder previously executed on the
38/// designated main thread. The `PhantomData<*mut ()>` makes the token
39/// `!Send + !Sync`, so a token obtained on the main thread cannot leak to
40/// another thread through ordinary safe code.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct MainThreadToken(PhantomData<*mut ()>);
43
44impl MainThreadToken {
45 /// Create a new `MainThreadToken` without verifying the current thread.
46 ///
47 /// # Safety
48 ///
49 /// The caller must guarantee that:
50 /// 1. They are executing on the thread that was (or will be) passed to
51 /// [`designate_main_thread`], and
52 /// 2. The resulting token will not be transmitted to another thread
53 /// through `unsafe` channels (the type is `!Send + !Sync`, which
54 /// prevents safe channels from doing so).
55 #[expect(
56 unsafe_code,
57 reason = "phantom data marker; !Send + !Sync prevents token leakage"
58 )]
59 pub unsafe fn new_unchecked() -> Self {
60 Self(PhantomData)
61 }
62
63 /// Obtain a token if the current thread matches the one previously passed
64 /// to [`designate_main_thread`].
65 ///
66 /// Returns `None` if [`designate_main_thread`] has never been called, or
67 /// if the current thread is not the designated main thread.
68 pub fn try_new() -> Option<Self> {
69 let designated = MAIN_THREAD_ID.get()?;
70 if *designated == thread::current().id() {
71 Some(Self(PhantomData))
72 } else {
73 None
74 }
75 }
76}
77
78/// A wrapper that allows sending non-`Send` types across thread boundaries.
79///
80/// Re-exported from the `send_wrapper` crate. It implements `Send` and `Sync`
81/// regardless of whether the wrapped type is thread-safe. However, it will
82/// panic at runtime if the wrapped value is accessed from any thread other
83/// than the one that created it.
84pub use send_wrapper::SendWrapper;
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use std::thread;
90
91 #[test]
92 fn worker_thread_never_obtains_token() {
93 // A spawned worker thread is never the designated main thread, even if
94 // some other test in this process has called `designate_main_thread`
95 // on a different thread. The token type is `!Send`, so we materialize
96 // it inside the worker and return only its presence as a `bool`.
97 let on_worker = thread::spawn(|| MainThreadToken::try_new().is_some())
98 .join()
99 .expect("worker thread");
100 assert!(!on_worker);
101 }
102
103 #[test]
104 fn try_new_returns_some_after_designation_on_same_thread() {
105 designate_main_thread();
106 // If this test happens to run on the same thread that another test
107 // designated, we still get a token; if a different thread was
108 // designated first, `try_new` correctly returns `None`.
109 match main_thread_id() {
110 Some(id) if id == thread::current().id() => {
111 assert!(MainThreadToken::try_new().is_some());
112 }
113 _ => {
114 assert!(MainThreadToken::try_new().is_none());
115 }
116 }
117 }
118}