shadow_rs/host/
thread.rs

1//! An emulated Linux thread.
2
3use std::cell::{Cell, RefCell};
4use std::ops::{Deref, DerefMut};
5
6use linux_api::errno::Errno;
7use linux_api::fcntl::DescriptorFlags;
8use linux_api::mman::{MapFlags, ProtFlags};
9use linux_api::posix_types::Pid;
10use linux_api::signal::stack_t;
11use shadow_shim_helper_rs::HostId;
12use shadow_shim_helper_rs::explicit_drop::ExplicitDrop;
13use shadow_shim_helper_rs::rootedcell::rc::RootedRc;
14use shadow_shim_helper_rs::rootedcell::refcell::RootedRefCell;
15use shadow_shim_helper_rs::shim_shmem::{HostShmemProtected, ThreadShmem};
16use shadow_shim_helper_rs::syscall_types::{ForeignPtr, SyscallReg};
17use shadow_shim_helper_rs::util::SendPointer;
18use shadow_shmem::allocator::{ShMemBlock, shmalloc};
19
20use super::context::ProcessContext;
21use super::descriptor::descriptor_table::{DescriptorHandle, DescriptorTable};
22use super::host::Host;
23use super::managed_thread::{self, ManagedThread};
24use super::process::{Process, ProcessId};
25use crate::cshadow as c;
26use crate::host::syscall::condition::{SyscallConditionRef, SyscallConditionRefMut};
27use crate::host::syscall::handler::SyscallHandler;
28use crate::utility::callback_queue::CallbackQueue;
29use crate::utility::{IsSend, ObjectCounter, syscall};
30
31/// The thread's state after having been allowed to execute some code.
32#[derive(Debug)]
33#[must_use]
34pub enum ResumeResult {
35    /// Blocked on a syscall.
36    Blocked,
37    /// The thread has exited with the given code.
38    ExitedThread(i32),
39    /// The process has exited.
40    ExitedProcess,
41}
42
43/// A virtual Thread in Shadow. Currently a thin wrapper around the C Thread,
44/// which this object owns, and frees on Drop.
45pub struct Thread {
46    id: ThreadId,
47    host_id: HostId,
48    process_id: ProcessId,
49    // If non-NULL, this address should be cleared and futex-awoken on thread exit.
50    // See set_tid_address(2).
51    tid_address: Cell<ForeignPtr<libc::pid_t>>,
52    shim_shared_memory: ShMemBlock<'static, ThreadShmem>,
53    syscallhandler: RootedRefCell<SyscallHandler>,
54    /// Descriptor table; potentially shared with other threads and processes.
55    // TODO: Consider using an Arc instead of RootedRc, particularly if this
56    // continues to be the only RootedRc member. Cloning this object currently
57    // only done when creating a child process or thread, and if we don't have
58    // any RootedRc members we could get rid of the requirement to explicitly
59    // drop Thread.
60    desc_table: Option<RootedRc<RootedRefCell<DescriptorTable>>>,
61    // TODO: convert to SyscallCondition (Rust wrapper for c::SysCallCondition).
62    // Non-trivial because SyscallCondition is currently not `Send`.
63    cond: Cell<SendPointer<c::SysCallCondition>>,
64    /// The native, managed thread
65    mthread: RefCell<ManagedThread>,
66    _counter: ObjectCounter,
67}
68
69impl IsSend for Thread {}
70
71impl Thread {
72    /// Minimal wrapper around the native managed thread.
73    pub fn mthread(&self) -> impl Deref<Target = ManagedThread> + '_ {
74        self.mthread.borrow()
75    }
76
77    /// Update this thread to be the new thread group leader as part of an
78    /// `execve` or `execveat` syscall.  Replaces the managed thread with
79    /// `mthread` and updates the thread ID.
80    pub fn update_for_exec(&mut self, host: &Host, mthread: ManagedThread, new_tid: ThreadId) {
81        self.mthread.replace(mthread).handle_process_exit();
82        self.tid_address.set(ForeignPtr::null());
83
84        // Update shmem
85        {
86            // We potentially need to update the thread-id. It doesn't currently
87            // have interior mutability, and since mutating it is rare, it seems
88            // nicer to get a mutable copy of the current  shared memory, update
89            // it, and alloc a new block, vs. adding another layer of interior
90            // mutability at all the other points we access it.
91
92            let host_shmem_prot = host.shim_shmem_lock_borrow().unwrap();
93
94            let mut thread_shmem =
95                ThreadShmem::clone(&self.shim_shared_memory, &host_shmem_prot.root);
96
97            // thread id is updated to make this the new thread group leader.
98            thread_shmem.tid = new_tid.into();
99
100            // sigaltstack is reset to disabled.
101            unsafe {
102                *thread_shmem
103                    .protected
104                    .borrow_mut(&host_shmem_prot.root)
105                    .sigaltstack_mut() = stack_t::new(
106                    std::ptr::null_mut(),
107                    linux_api::signal::SigAltStackFlags::SS_DISABLE,
108                    0,
109                )
110            };
111
112            self.shim_shared_memory = shmalloc(thread_shmem);
113        }
114
115        self.syscallhandler = RootedRefCell::new(
116            host.root(),
117            SyscallHandler::new(
118                host.id(),
119                self.process_id,
120                new_tid,
121                host.params.use_syscall_counters,
122            ),
123        );
124
125        // Update descriptor table
126        {
127            // Descriptor table is unshared
128            let desc_table_rc = self.desc_table.take().unwrap();
129            let mut desc_table = DescriptorTable::clone(&desc_table_rc.borrow(host.root()));
130            desc_table_rc.explicit_drop_recursive(host.root(), host);
131
132            // Any descriptors with CLOEXEC are closed.
133            let to_close: Vec<DescriptorHandle> = desc_table
134                .iter()
135                .filter_map(|(handle, descriptor)| {
136                    if descriptor.flags().contains(DescriptorFlags::FD_CLOEXEC) {
137                        Some(*handle)
138                    } else {
139                        None
140                    }
141                })
142                .collect();
143
144            CallbackQueue::queue_and_run_with_legacy(|q| {
145                for handle in to_close {
146                    log::trace!("Unregistering FD_CLOEXEC descriptor {handle:?}");
147                    if let Some(Err(e)) = desc_table
148                        .deregister_descriptor(handle)
149                        .unwrap()
150                        .close(host, q)
151                    {
152                        log::debug!("Error closing {handle:?}: {e:?}");
153                    };
154                }
155            });
156
157            self.desc_table = Some(RootedRc::new(
158                host.root(),
159                RootedRefCell::new(host.root(), desc_table),
160            ));
161        }
162
163        if let Some(c) = unsafe { self.cond.get_mut().ptr().as_mut() } {
164            unsafe { c::syscallcondition_cancel(c) };
165            unsafe { c::syscallcondition_unref(c) };
166        }
167        self.cond = Cell::new(unsafe { SendPointer::new(std::ptr::null_mut()) });
168
169        self.id = new_tid;
170    }
171
172    /// Have the plugin thread natively execute the given syscall.
173    fn native_syscall_raw(
174        &self,
175        ctx: &ProcessContext,
176        n: i64,
177        args: &[SyscallReg],
178    ) -> libc::c_long {
179        self.mthread
180            .borrow()
181            .native_syscall(&ctx.with_thread(self), n, args)
182            .into()
183    }
184
185    /// Have the plugin thread natively execute the given syscall.
186    fn native_syscall(
187        &self,
188        ctx: &ProcessContext,
189        n: i64,
190        args: &[SyscallReg],
191    ) -> Result<SyscallReg, Errno> {
192        syscall::raw_return_value_to_result(self.native_syscall_raw(ctx, n, args))
193    }
194
195    pub fn process_id(&self) -> ProcessId {
196        self.process_id
197    }
198
199    pub fn host_id(&self) -> HostId {
200        self.host_id
201    }
202
203    pub fn native_pid(&self) -> Pid {
204        self.mthread.borrow().native_pid()
205    }
206
207    pub fn native_tid(&self) -> Pid {
208        self.mthread.borrow().native_tid()
209    }
210
211    pub fn id(&self) -> ThreadId {
212        self.id
213    }
214
215    /// Returns whether the given thread is its thread group (aka process) leader.
216    /// Typically this is true for the first thread created in a process.
217    pub fn is_leader(&self) -> bool {
218        self.id == self.process_id.into()
219    }
220
221    pub fn syscall_condition(&self) -> Option<SyscallConditionRef> {
222        // We check the for null explicitly here instead of using `as_mut` to
223        // construct and match an `Option<&mut c::SysCallCondition>`, since it's
224        // difficult to ensure we're not breaking any Rust aliasing rules when
225        // constructing a mutable reference.
226        let c = self.cond.get().ptr();
227        if c.is_null() {
228            None
229        } else {
230            Some(unsafe { SyscallConditionRef::borrow_from_c(c) })
231        }
232    }
233
234    pub fn syscall_condition_mut(&self) -> Option<SyscallConditionRefMut> {
235        // We can't safely use `as_mut` here, since that would construct a mutable reference,
236        // and we can't prove no other reference exists.
237        let c = self.cond.get().ptr();
238        if c.is_null() {
239            None
240        } else {
241            Some(unsafe { SyscallConditionRefMut::borrow_from_c(c) })
242        }
243    }
244
245    pub fn cleanup_syscall_condition(&self) {
246        if let Some(c) = unsafe {
247            self.cond
248                .replace(SendPointer::new(std::ptr::null_mut()))
249                .ptr()
250                .as_mut()
251        } {
252            unsafe { c::syscallcondition_cancel(c) };
253            unsafe { c::syscallcondition_unref(c) };
254        }
255    }
256
257    pub fn descriptor_table(&self) -> &RootedRc<RootedRefCell<DescriptorTable>> {
258        self.desc_table.as_ref().unwrap()
259    }
260
261    #[track_caller]
262    pub fn descriptor_table_borrow<'a>(
263        &'a self,
264        host: &'a Host,
265    ) -> impl Deref<Target = DescriptorTable> + 'a {
266        self.desc_table.as_ref().unwrap().borrow(host.root())
267    }
268
269    #[track_caller]
270    pub fn descriptor_table_borrow_mut<'a>(
271        &'a self,
272        host: &'a Host,
273    ) -> impl DerefMut<Target = DescriptorTable> + 'a {
274        self.desc_table.as_ref().unwrap().borrow_mut(host.root())
275    }
276
277    /// Natively execute munmap(2) on the given thread.
278    pub fn native_munmap(
279        &self,
280        ctx: &ProcessContext,
281        ptr: ForeignPtr<u8>,
282        size: usize,
283    ) -> Result<(), Errno> {
284        self.native_syscall(ctx, libc::SYS_munmap, &[ptr.into(), size.into()])?;
285        Ok(())
286    }
287
288    /// Natively execute mmap(2) on the given thread.
289    pub fn native_mmap(
290        &self,
291        ctx: &ProcessContext,
292        addr: ForeignPtr<u8>,
293        len: usize,
294        prot: ProtFlags,
295        flags: MapFlags,
296        fd: i32,
297        offset: i64,
298    ) -> Result<ForeignPtr<u8>, Errno> {
299        Ok(self
300            .native_syscall(
301                ctx,
302                libc::SYS_mmap,
303                &[
304                    SyscallReg::from(addr),
305                    SyscallReg::from(len),
306                    SyscallReg::from(prot.bits()),
307                    SyscallReg::from(flags.bits()),
308                    SyscallReg::from(fd),
309                    SyscallReg::from(offset),
310                ],
311            )?
312            .into())
313    }
314
315    /// Natively execute mremap(2) on the given thread.
316    pub fn native_mremap(
317        &self,
318        ctx: &ProcessContext,
319        old_addr: ForeignPtr<u8>,
320        old_len: usize,
321        new_len: usize,
322        flags: i32,
323        new_addr: ForeignPtr<u8>,
324    ) -> Result<ForeignPtr<u8>, Errno> {
325        Ok(self
326            .native_syscall(
327                ctx,
328                libc::SYS_mremap,
329                &[
330                    SyscallReg::from(old_addr),
331                    SyscallReg::from(old_len),
332                    SyscallReg::from(new_len),
333                    SyscallReg::from(flags),
334                    SyscallReg::from(new_addr),
335                ],
336            )?
337            .into())
338    }
339
340    /// Natively execute mmap(2) on the given thread.
341    pub fn native_mprotect(
342        &self,
343        ctx: &ProcessContext,
344        addr: ForeignPtr<u8>,
345        len: usize,
346        prot: ProtFlags,
347    ) -> Result<(), Errno> {
348        self.native_syscall(
349            ctx,
350            libc::SYS_mprotect,
351            &[
352                SyscallReg::from(addr),
353                SyscallReg::from(len),
354                SyscallReg::from(prot.bits()),
355            ],
356        )?;
357        Ok(())
358    }
359
360    /// Natively execute open(2) on the given thread.
361    pub fn native_open(
362        &self,
363        ctx: &ProcessContext,
364        pathname: ForeignPtr<u8>,
365        flags: i32,
366        mode: i32,
367    ) -> Result<i32, Errno> {
368        let res = self.native_syscall(
369            ctx,
370            libc::SYS_open,
371            &[
372                SyscallReg::from(pathname),
373                SyscallReg::from(flags),
374                SyscallReg::from(mode),
375            ],
376        );
377        Ok(i32::from(res?))
378    }
379
380    /// Natively execute close(2) on the given thread.
381    pub fn native_close(&self, ctx: &ProcessContext, fd: i32) -> Result<(), Errno> {
382        self.native_syscall(ctx, libc::SYS_close, &[SyscallReg::from(fd)])?;
383        Ok(())
384    }
385
386    /// Natively execute brk(2) on the given thread.
387    pub fn native_brk(
388        &self,
389        ctx: &ProcessContext,
390        addr: ForeignPtr<u8>,
391    ) -> Result<ForeignPtr<u8>, Errno> {
392        let res = self.native_syscall(ctx, libc::SYS_brk, &[SyscallReg::from(addr)])?;
393        Ok(ForeignPtr::from(res))
394    }
395
396    /// Natively execute a chdir(2) syscall on the given thread.
397    pub fn native_chdir(
398        &self,
399        ctx: &ProcessContext,
400        pathname: ForeignPtr<std::ffi::c_char>,
401    ) -> Result<i32, Errno> {
402        let res = self.native_syscall(ctx, libc::SYS_chdir, &[SyscallReg::from(pathname)]);
403        Ok(i32::from(res?))
404    }
405
406    /// Allocates some space in the plugin's memory. Use `get_writeable_ptr` to write to it, and
407    /// `flush` to ensure that the write is flushed to the plugin's memory.
408    pub fn malloc_foreign_ptr(
409        &self,
410        ctx: &ProcessContext,
411        size: usize,
412    ) -> Result<ForeignPtr<u8>, Errno> {
413        // SAFETY: No pointer specified; can't pass a bad one.
414        self.native_mmap(
415            ctx,
416            ForeignPtr::null(),
417            size,
418            ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
419            MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
420            -1,
421            0,
422        )
423    }
424
425    /// Frees a pointer previously returned by `malloc_foreign_ptr`
426    pub fn free_foreign_ptr(
427        &self,
428        ctx: &ProcessContext,
429        ptr: ForeignPtr<u8>,
430        size: usize,
431    ) -> Result<(), Errno> {
432        self.native_munmap(ctx, ptr, size)?;
433        Ok(())
434    }
435
436    /// Create a new `Thread`, wrapping `mthread`. Intended for use by
437    /// syscall handlers such as `clone`.
438    pub fn wrap_mthread(
439        host: &Host,
440        mthread: ManagedThread,
441        desc_table: RootedRc<RootedRefCell<DescriptorTable>>,
442        pid: ProcessId,
443        tid: ThreadId,
444    ) -> Result<Thread, Errno> {
445        let child = Self {
446            mthread: RefCell::new(mthread),
447            syscallhandler: RootedRefCell::new(
448                host.root(),
449                SyscallHandler::new(host.id(), pid, tid, host.params.use_syscall_counters),
450            ),
451            cond: Cell::new(unsafe { SendPointer::new(std::ptr::null_mut()) }),
452            id: tid,
453            host_id: host.id(),
454            process_id: pid,
455            tid_address: Cell::new(ForeignPtr::null()),
456            shim_shared_memory: shmalloc(ThreadShmem::new(
457                &host.shim_shmem_lock_borrow().unwrap(),
458                tid.into(),
459            )),
460            desc_table: Some(desc_table),
461            _counter: ObjectCounter::new("Thread"),
462        };
463        Ok(child)
464    }
465
466    /// Shared memory for this thread.
467    pub fn shmem(&self) -> &ShMemBlock<ThreadShmem> {
468        &self.shim_shared_memory
469    }
470
471    pub fn resume(&self, ctx: &ProcessContext) -> ResumeResult {
472        // Ensure the condition isn't triggered again, but don't clear it yet.
473        // Syscall handler can still access.
474        if let Some(c) = unsafe { self.cond.get().ptr().as_mut() } {
475            unsafe { c::syscallcondition_cancel(c) };
476        }
477
478        let mut syscall_handler = self.syscallhandler.borrow_mut(ctx.host.root());
479
480        let res = self
481            .mthread
482            .borrow()
483            .resume(&ctx.with_thread(self), &mut syscall_handler);
484
485        // Now we're done with old condition.
486        if let Some(c) = unsafe {
487            self.cond
488                .replace(SendPointer::new(std::ptr::null_mut()))
489                .ptr()
490                .as_mut()
491        } {
492            unsafe { c::syscallcondition_unref(c) };
493        }
494
495        match res {
496            managed_thread::ResumeResult::Blocked(cond) => {
497                // Wait on new condition.
498                let cond = cond.into_inner();
499                self.cond.set(unsafe { SendPointer::new(cond) });
500                if let Some(cond) = unsafe { cond.as_mut() } {
501                    unsafe { c::syscallcondition_waitNonblock(cond, ctx.host, ctx.process, self) }
502                }
503                ResumeResult::Blocked
504            }
505            managed_thread::ResumeResult::ExitedThread(c) => ResumeResult::ExitedThread(c),
506            managed_thread::ResumeResult::ExitedProcess => ResumeResult::ExitedProcess,
507        }
508    }
509
510    pub fn handle_process_exit(&self) {
511        self.cleanup_syscall_condition();
512        self.mthread.borrow().handle_process_exit();
513    }
514
515    pub fn return_code(&self) -> Option<i32> {
516        self.mthread.borrow().return_code()
517    }
518
519    pub fn is_running(&self) -> bool {
520        self.mthread.borrow().is_running()
521    }
522
523    pub fn get_tid_address(&self) -> ForeignPtr<libc::pid_t> {
524        self.tid_address.get()
525    }
526
527    /// Sets the `clear_child_tid` attribute as for `set_tid_address(2)`. The thread will perform a
528    /// futex-wake operation on the given address on termination.
529    pub fn set_tid_address(&self, ptr: ForeignPtr<libc::pid_t>) {
530        self.tid_address.set(ptr)
531    }
532
533    pub fn unblocked_signal_pending(
534        &self,
535        process: &Process,
536        host_shmem: &HostShmemProtected,
537    ) -> bool {
538        debug_assert_eq!(process.id(), self.process_id);
539
540        let thread_shmem_protected = self.shmem().protected.borrow(&host_shmem.root);
541
542        let unblocked_signals = !thread_shmem_protected.blocked_signals;
543        let pending_signals = self
544            .shmem()
545            .protected
546            .borrow(&host_shmem.root)
547            .pending_signals
548            | process
549                .shmem()
550                .protected
551                .borrow(&host_shmem.root)
552                .pending_signals;
553
554        !(pending_signals & unblocked_signals).is_empty()
555    }
556}
557
558impl Drop for Thread {
559    fn drop(&mut self) {
560        if let Some(c) = unsafe { self.cond.get_mut().ptr().as_mut() } {
561            unsafe { c::syscallcondition_cancel(c) };
562            unsafe { c::syscallcondition_unref(c) };
563        }
564    }
565}
566
567impl ExplicitDrop for Thread {
568    type ExplicitDropParam = Host;
569    type ExplicitDropResult = ();
570
571    fn explicit_drop(mut self, host: &Host) {
572        if let Some(table) = self.desc_table.take() {
573            table.explicit_drop_recursive(host.root(), host);
574        }
575    }
576}
577
578#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd)]
579pub struct ThreadId(u32);
580
581impl TryFrom<libc::pid_t> for ThreadId {
582    type Error = <u32 as TryFrom<libc::pid_t>>::Error;
583
584    fn try_from(value: libc::pid_t) -> Result<Self, Self::Error> {
585        Ok(Self(u32::try_from(value)?))
586    }
587}
588
589impl From<ProcessId> for ThreadId {
590    fn from(value: ProcessId) -> Self {
591        // A process ID is also a valid thread ID
592        ThreadId(value.into())
593    }
594}
595
596impl From<ThreadId> for libc::pid_t {
597    fn from(val: ThreadId) -> Self {
598        val.0.try_into().unwrap()
599    }
600}
601
602impl std::fmt::Display for ThreadId {
603    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
604        write!(f, "{}", self.0)
605    }
606}
607
608mod export {
609    use shadow_shim_helper_rs::shim_shmem::export::{ShimShmemHostLock, ShimShmemThread};
610    use shadow_shim_helper_rs::syscall_types::UntypedForeignPtr;
611
612    use super::*;
613    use crate::core::worker::Worker;
614    use crate::host::descriptor::socket::Socket;
615    use crate::host::descriptor::socket::inet::InetSocket;
616    use crate::host::descriptor::{CompatFile, Descriptor, File};
617
618    /// Make the requested syscall from within the plugin.
619    ///
620    /// Does *not* flush or invalidate MemoryManager pointers, such as those
621    /// obtained through `process_getReadablePtr` etc.
622    ///
623    /// Arguments are treated opaquely. e.g. no pointer-marshalling is done.
624    ///
625    /// The return value is the value returned by the syscall *instruction*.
626    /// You can map to a corresponding errno value with syscall_rawReturnValueToErrno.
627    //
628    // Rust doesn't support declaring a function with varargs (...), but this
629    // declaration is ABI compatible with a caller who sees this function declared
630    // with arguments `Thread* thread, long n, ...`. We manually generate that declartion
631    // in our bindings.
632    #[unsafe(no_mangle)]
633    unsafe extern "C-unwind" fn thread_nativeSyscall(
634        thread: *const Thread,
635        n: libc::c_long,
636        arg1: SyscallReg,
637        arg2: SyscallReg,
638        arg3: SyscallReg,
639        arg4: SyscallReg,
640        arg5: SyscallReg,
641        arg6: SyscallReg,
642    ) -> libc::c_long {
643        let thread = unsafe { thread.as_ref().unwrap() };
644        Worker::with_active_host(|host| {
645            Worker::with_active_process(|process| {
646                thread.native_syscall_raw(
647                    &ProcessContext::new(host, process),
648                    n,
649                    &[arg1, arg2, arg3, arg4, arg5, arg6],
650                )
651            })
652            .unwrap()
653        })
654        .unwrap()
655    }
656
657    #[unsafe(no_mangle)]
658    pub unsafe extern "C-unwind" fn thread_getID(thread: *const Thread) -> libc::pid_t {
659        let thread = unsafe { thread.as_ref().unwrap() };
660        thread.id().into()
661    }
662
663    /// Gets the `clear_child_tid` attribute, as set by `thread_setTidAddress`.
664    #[unsafe(no_mangle)]
665    pub unsafe extern "C-unwind" fn thread_getTidAddress(
666        thread: *const Thread,
667    ) -> UntypedForeignPtr {
668        let thread = unsafe { thread.as_ref().unwrap() };
669        thread.get_tid_address().cast::<()>()
670    }
671
672    /// Returns a typed pointer to memory shared with the shim (which is backed by
673    /// the block returned by thread_getShMBlock).
674    #[unsafe(no_mangle)]
675    pub unsafe extern "C-unwind" fn thread_sharedMem(
676        thread: *const Thread,
677    ) -> *const ShimShmemThread {
678        let thread = unsafe { thread.as_ref().unwrap() };
679        &*thread.shim_shared_memory
680    }
681
682    #[unsafe(no_mangle)]
683    pub unsafe extern "C-unwind" fn thread_getProcess(thread: *const Thread) -> *const Process {
684        let thread = unsafe { thread.as_ref().unwrap() };
685        Worker::with_active_host(|host| {
686            let process = host.process_borrow(thread.process_id).unwrap();
687            let p: &Process = &process.borrow(host.root());
688            std::ptr::from_ref(p)
689        })
690        .unwrap()
691    }
692
693    #[unsafe(no_mangle)]
694    pub unsafe extern "C-unwind" fn thread_getHost(thread: *const Thread) -> *const Host {
695        let thread = unsafe { thread.as_ref().unwrap() };
696        Worker::with_active_host(|host| {
697            assert_eq!(host.id(), thread.host_id());
698            std::ptr::from_ref(host)
699        })
700        .unwrap()
701    }
702
703    #[unsafe(no_mangle)]
704    pub unsafe extern "C-unwind" fn thread_clearSysCallCondition(thread: *const Thread) {
705        let thread = unsafe { thread.as_ref().unwrap() };
706        thread.cleanup_syscall_condition();
707    }
708
709    /// Returns true iff there is an unblocked, unignored signal pending for this
710    /// thread (or its process).
711    #[unsafe(no_mangle)]
712    pub unsafe extern "C-unwind" fn thread_unblockedSignalPending(
713        thread: *const Thread,
714        host_lock: *const ShimShmemHostLock,
715    ) -> bool {
716        let thread = unsafe { thread.as_ref().unwrap() };
717        let host_lock = unsafe { host_lock.as_ref().unwrap() };
718
719        Worker::with_active_host(|host| {
720            let process = host.process_borrow(thread.process_id()).unwrap();
721            let process = process.borrow(host.root());
722            thread.unblocked_signal_pending(&process, host_lock)
723        })
724        .unwrap()
725    }
726
727    /// Register a `Descriptor`. This takes ownership of the descriptor and you must not access it
728    /// after.
729    #[unsafe(no_mangle)]
730    pub extern "C-unwind" fn thread_registerDescriptor(
731        thread: *const Thread,
732        desc: *mut Descriptor,
733    ) -> libc::c_int {
734        let thread = unsafe { thread.as_ref().unwrap() };
735        let desc = Descriptor::from_raw(desc).unwrap();
736
737        Worker::with_active_host(|host| {
738            thread
739                .descriptor_table_borrow_mut(host)
740                .register_descriptor(*desc)
741                .unwrap()
742                .into()
743        })
744        .unwrap()
745    }
746
747    /// Get a temporary reference to a descriptor.
748    #[unsafe(no_mangle)]
749    pub extern "C-unwind" fn thread_getRegisteredDescriptor(
750        thread: *const Thread,
751        handle: libc::c_int,
752    ) -> *const Descriptor {
753        let thread = unsafe { thread.as_ref().unwrap() };
754
755        let handle = match handle.try_into() {
756            Ok(i) => i,
757            Err(_) => {
758                log::debug!("Attempted to get a descriptor with handle {}", handle);
759                return std::ptr::null();
760            }
761        };
762
763        Worker::with_active_host(
764            |host| match thread.descriptor_table_borrow(host).get(handle) {
765                Some(d) => std::ptr::from_ref(d),
766                None => std::ptr::null(),
767            },
768        )
769        .unwrap()
770    }
771
772    /// Get a temporary mutable reference to a descriptor.
773    #[unsafe(no_mangle)]
774    pub extern "C-unwind" fn thread_getRegisteredDescriptorMut(
775        thread: *const Thread,
776        handle: libc::c_int,
777    ) -> *mut Descriptor {
778        let thread = unsafe { thread.as_ref().unwrap() };
779
780        let handle = match handle.try_into() {
781            Ok(i) => i,
782            Err(_) => {
783                log::debug!("Attempted to get a descriptor with handle {}", handle);
784                return std::ptr::null_mut();
785            }
786        };
787
788        Worker::with_active_host(|host| {
789            match thread.descriptor_table_borrow_mut(host).get_mut(handle) {
790                Some(d) => d as *mut Descriptor,
791                None => std::ptr::null_mut(),
792            }
793        })
794        .unwrap()
795    }
796
797    /// Get a temporary reference to a legacy file.
798    #[unsafe(no_mangle)]
799    pub unsafe extern "C-unwind" fn thread_getRegisteredLegacyFile(
800        thread: *const Thread,
801        handle: libc::c_int,
802    ) -> *mut c::LegacyFile {
803        let thread = unsafe { thread.as_ref().unwrap() };
804
805        let handle = match handle.try_into() {
806            Ok(i) => i,
807            Err(_) => {
808                log::debug!("Attempted to get a descriptor with handle {}", handle);
809                return std::ptr::null_mut();
810            }
811        };
812
813        Worker::with_active_host(|host| {
814        match thread.descriptor_table_borrow(host).get(handle).map(|x| x.file()) {
815            Some(CompatFile::Legacy(file)) => file.ptr(),
816            Some(CompatFile::New(file)) => {
817                // we have a special case for the legacy C TCP objects
818                if let File::Socket(Socket::Inet(InetSocket::LegacyTcp(tcp))) = file.inner_file() {
819                    tcp.borrow().as_legacy_file()
820                } else {
821                    log::warn!(
822                        "A descriptor exists for fd={}, but it is not a legacy file. Returning NULL.",
823                        handle
824                    );
825                    std::ptr::null_mut()
826                }
827            }
828            None => std::ptr::null_mut(),
829        }
830        }).unwrap()
831    }
832}