monitor/mon/
thread.rs

1use std::{
2    collections::HashMap,
3    mem::MaybeUninit,
4    ptr::NonNull,
5    sync::{Arc, OnceLock},
6};
7
8use dynlink::{compartment::Compartment, tls::TlsRegion};
9use monitor_api::{RuntimeThreadControl, ThreadMgrStats, MONITOR_INSTANCE_ID};
10use twizzler_abi::{
11    object::NULLPAGE_SIZE,
12    syscall::{sys_spawn, sys_thread_exit, ThreadSyncSleep, UpcallTargetSpawnOption},
13    thread::{ExecutionState, ThreadRepr},
14    upcall::{UpcallFlags, UpcallInfo, UpcallMode, UpcallOptions, UpcallTarget},
15};
16use twizzler_rt_abi::{
17    error::{GenericError, TwzError},
18    object::{MapFlags, ObjID},
19};
20
21use super::{
22    get_monitor,
23    space::{MapHandle, MapInfo},
24};
25use crate::mon::space::Space;
26
27mod cleaner;
28pub(crate) use cleaner::ThreadCleaner;
29
30/// Stack size for the supervisor upcall stack.
31pub const SUPER_UPCALL_STACK_SIZE: usize = 8 * 1024 * 1024; // 8MB
32/// Default stack size for the user stack.
33pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; // 8MB
34/// Stack minimium alignment.
35pub const STACK_SIZE_MIN_ALIGN: usize = 0x1000; // 4K
36
37/// Manages all threads owned by the monitor. Typically, this is all threads.
38/// Threads are spawned here and tracked in the background by a [cleaner::ThreadCleaner]. The thread
39/// cleaner detects when a thread has exited and performs any final thread cleanup logic.
40pub struct ThreadMgr {
41    all: HashMap<ObjID, ManagedThread>,
42    cleaner: OnceLock<cleaner::ThreadCleaner>,
43    next_id: u32,
44    id_stack: Vec<u32>,
45}
46
47impl Default for ThreadMgr {
48    fn default() -> Self {
49        Self {
50            all: HashMap::default(),
51            cleaner: OnceLock::new(),
52            next_id: 1,
53            id_stack: Vec::new(),
54        }
55    }
56}
57
58struct IdDropper<'a> {
59    mgr: &'a mut ThreadMgr,
60    id: u32,
61}
62
63impl<'a> IdDropper<'a> {
64    pub fn freeze(self) -> u32 {
65        let id = self.id;
66        std::mem::forget(self);
67        id
68    }
69}
70
71impl<'a> Drop for IdDropper<'a> {
72    fn drop(&mut self) {
73        self.mgr.release_super_tid(self.id);
74    }
75}
76
77impl ThreadMgr {
78    pub(super) fn set_cleaner(&mut self, cleaner: cleaner::ThreadCleaner) {
79        self.cleaner.set(cleaner).ok().unwrap();
80    }
81
82    fn next_super_tid(&mut self) -> IdDropper<'_> {
83        let id = self.id_stack.pop().unwrap_or_else(|| {
84            let id = self.next_id;
85            self.next_id += 1;
86            id
87        });
88        IdDropper { mgr: self, id }
89    }
90
91    fn release_super_tid(&mut self, id: u32) {
92        self.id_stack.push(id);
93    }
94
95    fn do_remove(&mut self, thread: &ManagedThread) {
96        self.all.remove(&thread.id);
97        self.release_super_tid(thread.super_tid);
98        if let Some(cleaner) = self.cleaner.get() {
99            cleaner.untrack(thread.id);
100        }
101    }
102
103    pub fn stat(&self) -> ThreadMgrStats {
104        ThreadMgrStats {
105            nr_threads: self.all.len(),
106        }
107    }
108
109    unsafe fn spawn_thread(
110        start: usize,
111        super_stack_start: usize,
112        super_thread_pointer: usize,
113        arg: usize,
114        self_ctx: ObjID,
115    ) -> Result<ObjID, TwzError> {
116        let mut upcall_target = UpcallTarget::new(
117            None,
118            Some(twizzler_rt_abi::arch::__twz_rt_upcall_entry),
119            super_stack_start,
120            SUPER_UPCALL_STACK_SIZE,
121            super_thread_pointer,
122            MONITOR_INSTANCE_ID,
123            self_ctx,
124            [UpcallOptions {
125                flags: UpcallFlags::empty(),
126                mode: UpcallMode::CallSuper,
127            }; UpcallInfo::NR_UPCALLS],
128        );
129
130        let mb = &mut upcall_target.options[UpcallInfo::Mailbox(0).number()];
131        mb.mode = UpcallMode::CallSelf;
132
133        sys_spawn(twizzler_abi::syscall::ThreadSpawnArgs {
134            entry: start,
135            stack_base: super_stack_start,
136            stack_size: SUPER_UPCALL_STACK_SIZE,
137            tls: super_thread_pointer,
138            arg,
139            flags: twizzler_abi::syscall::ThreadSpawnFlags::empty(),
140            vm_context_handle: None,
141            upcall_target: UpcallTargetSpawnOption::SetTo(upcall_target),
142        })
143    }
144
145    fn do_spawn(
146        &mut self,
147        monitor_dynlink_comp: &mut Compartment,
148        start: unsafe extern "C" fn(usize) -> !,
149        arg: usize,
150        main_thread_comp: Option<ObjID>,
151        instance: ObjID,
152    ) -> Result<ManagedThread, TwzError> {
153        let super_tls = monitor_dynlink_comp
154            .build_tls_region(RuntimeThreadControl::default(), |layout| unsafe {
155                NonNull::new(std::alloc::alloc_zeroed(layout))
156            })
157            .map_err(|_| GenericError::Internal)?;
158        let super_tid = self.next_super_tid().freeze();
159        unsafe {
160            let tcb = super_tls.get_thread_control_block::<RuntimeThreadControl>();
161            (*tcb).runtime_data.set_id(super_tid);
162        }
163        let super_thread_pointer = super_tls.get_thread_pointer_value();
164        let super_stack = Box::new_zeroed_slice(SUPER_UPCALL_STACK_SIZE);
165        let id = unsafe {
166            Self::spawn_thread(
167                start as *const () as usize,
168                super_stack.as_ptr() as usize,
169                super_thread_pointer,
170                arg,
171                instance,
172            )?
173        };
174        let repr = Space::map(
175            &get_monitor().space,
176            MapInfo {
177                id,
178                flags: MapFlags::READ,
179            },
180        )
181        .unwrap();
182        Ok(Arc::new(ManagedThreadInner {
183            id,
184            super_tid,
185            repr: ManagedThreadRepr::new(repr),
186            _super_stack: super_stack,
187            _super_tls: super_tls,
188            main_thread_comp,
189        }))
190    }
191
192    /// Start a thread, running the provided Box'd closure. The thread will be running in
193    /// monitor-mode, and will have no connection to any compartment.
194    pub fn start_thread(
195        &mut self,
196        monitor_dynlink_comp: &mut Compartment,
197        main: Box<dyn FnOnce()>,
198        main_thread_comp: Option<ObjID>,
199        instance: ObjID,
200    ) -> Result<ManagedThread, TwzError> {
201        let main_addr = Box::into_raw(Box::new(main)) as usize;
202        unsafe extern "C" fn managed_thread_entry(main: usize) -> ! {
203            {
204                let main = Box::from_raw(main as *mut Box<dyn FnOnce()>);
205                main();
206            }
207
208            sys_thread_exit(0);
209        }
210
211        let mt = self.do_spawn(
212            monitor_dynlink_comp,
213            managed_thread_entry,
214            main_addr,
215            main_thread_comp,
216            instance,
217        );
218        if let Ok(ref mt) = mt {
219            if let Some(cleaner) = self.cleaner.get() {
220                cleaner.track(mt.clone());
221            }
222        }
223        mt
224    }
225}
226
227/// Internal managed thread data.
228pub struct ManagedThreadInner {
229    /// The ID of the thread.
230    pub id: ObjID,
231    pub super_tid: u32,
232    /// The thread repr.
233    pub(crate) repr: ManagedThreadRepr,
234    _super_stack: Box<[MaybeUninit<u8>]>,
235    _super_tls: TlsRegion,
236    pub main_thread_comp: Option<ObjID>,
237}
238
239impl ManagedThreadInner {
240    /// Check if this thread has exited.
241    pub fn has_exited(&self) -> bool {
242        self.repr.get_repr().get_state() == ExecutionState::Exited
243    }
244
245    /// Create a ThreadSyncSleep that will wait until the thread has exited.
246    pub fn waitable_until_exit(&self) -> ThreadSyncSleep {
247        self.repr.get_repr().waitable(ExecutionState::Exited)
248    }
249}
250
251// Safety: TlsRegion is not changed, and points to only globally- and permanently-allocated data.
252unsafe impl Send for ManagedThreadInner {}
253unsafe impl Sync for ManagedThreadInner {}
254
255impl core::fmt::Debug for ManagedThreadInner {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        write!(f, "ManagedThread({})", self.id)
258    }
259}
260
261impl Drop for ManagedThreadInner {
262    fn drop(&mut self) {
263        tracing::trace!("dropping ManagedThread {}", self.id);
264    }
265}
266
267/// A thread managed by the monitor.
268pub type ManagedThread = Arc<ManagedThreadInner>;
269
270/// An owned handle to a thread's repr object.
271pub(crate) struct ManagedThreadRepr {
272    handle: MapHandle,
273}
274
275impl ManagedThreadRepr {
276    fn new(handle: MapHandle) -> Self {
277        Self { handle }
278    }
279
280    /// Get the thread representation structure for the associated thread.
281    pub fn get_repr(&self) -> &ThreadRepr {
282        let addr = self.handle.addrs().start + NULLPAGE_SIZE;
283        unsafe { (addr as *const ThreadRepr).as_ref().unwrap() }
284    }
285}