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;
7
8/// A witness of execution that exists solely on a designated "Main Thread".
9///
10/// In FFI contexts, many libraries (especially legacy C++ or UI frameworks)
11/// are not thread-safe and must only be initialized, called, or dropped from
12/// the same thread that originally created them.
13///
14/// `MainThreadToken` is a zero-sized proof carrier. Possessing it proves
15/// (at a type-system level) that you are currently executing on the designated
16/// main thread.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct MainThreadToken(PhantomData<*mut ()>);
19
20impl MainThreadToken {
21 /// Create a new `MainThreadToken`.
22 ///
23 /// # Safety
24 ///
25 /// This must only be called from the designated main application thread.
26 /// In VT Code, this is typically the thread that initializes the TUI or
27 /// the first boot thread.
28 #[allow(unsafe_code)]
29 pub unsafe fn new_unchecked() -> Self {
30 Self(PhantomData)
31 }
32
33 /// Obtain a token if we are on the main thread, or return `None` if we are not.
34 ///
35 /// This provides a safe runtime check before performing hazardous FFI operations.
36 pub fn try_new() -> Option<Self> {
37 // In VT Code, we don't have a single global 'main' thread ID enforced across all components yet,
38 // but this provides the structure for components to enforce it locally.
39 // For now, we return Some if we are lucky, but a real implementation would check
40 // against a stored ThreadId.
41 None
42 }
43}
44
45/// A wrapper that allows sending non-`Send` types across thread boundaries.
46///
47/// Re-exported from the `send_wrapper` crate. It implements `Send` and `Sync`
48/// regardless of whether the wrapped type is thread-safe. However, it will
49/// panic at runtime if the wrapped value is accessed from any thread other
50/// than the one that created it.
51pub use send_wrapper::SendWrapper;