Skip to main content

zx/
thread.rs

1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon threads.
6
7use crate::{
8    ExceptionReport, MonotonicDuration, NullableHandle, ObjectQuery, Profile, Status, Task, Topic,
9    ok, sys,
10};
11use bitflags::bitflags;
12
13bitflags! {
14    /// Options that may be used with `Thread::raise_exception`
15    #[repr(transparent)]
16    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17    pub struct RaiseExceptionOptions: u32 {
18        const TARGET_JOB_DEBUGGER = sys::ZX_EXCEPTION_TARGET_JOB_DEBUGGER;
19    }
20}
21
22/// An object representing a Zircon thread.
23///
24/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
25#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
26#[repr(transparent)]
27pub struct Thread(NullableHandle);
28impl_handle_based!(Thread);
29
30struct ThreadExceptionReport;
31unsafe impl ObjectQuery for ThreadExceptionReport {
32    const TOPIC: Topic = Topic::THREAD_EXCEPTION_REPORT;
33    type InfoTy = sys::zx_exception_report_t;
34}
35
36impl Thread {
37    /// Cause the thread to begin execution.
38    ///
39    /// Wraps the
40    /// [zx_thread_start](https://fuchsia.dev/fuchsia-src/reference/syscalls/thread_start.md)
41    /// syscall.
42    pub fn start(
43        &self,
44        thread_entry: usize,
45        stack: usize,
46        arg1: usize,
47        arg2: usize,
48    ) -> Result<(), Status> {
49        let thread_raw = self.raw_handle();
50        let status = unsafe { sys::zx_thread_start(thread_raw, thread_entry, stack, arg1, arg2) };
51        ok(status)
52    }
53
54    /// Apply a scheduling profile to a thread.
55    ///
56    /// Wraps the
57    /// [zx_object_set_profile](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_set_profile) syscall.
58    pub fn set_profile(&self, profile: &Profile, options: u32) -> Result<(), Status> {
59        let thread_raw = self.raw_handle();
60        let profile_raw = profile.raw_handle();
61        let status = unsafe { sys::zx_object_set_profile(thread_raw, profile_raw, options) };
62        ok(status)
63    }
64
65    /// Terminate the current running thread.
66    ///
67    /// # Safety
68    ///
69    /// Extreme caution should be used-- this is basically always UB in Rust.
70    /// There's almost no "normal" program code where this is okay to call.
71    /// Users should take care that no references could possibly exist to
72    /// stack variables on this thread, and that any destructors, closure
73    /// suffixes, or other "after this thing runs" code is waiting to run
74    /// in order for safety.
75    pub unsafe fn exit() {
76        unsafe { sys::zx_thread_exit() }
77    }
78
79    /// Wraps the
80    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
81    /// syscall for the ZX_INFO_THREAD_EXCEPTION_REPORT topic.
82    pub fn exception_report(&self) -> Result<ExceptionReport, Status> {
83        let raw = self.0.get_info_single::<ThreadExceptionReport>()?;
84
85        // SAFETY: this value was provided by the kernel, the union is valid.
86        Ok(unsafe { ExceptionReport::from_raw(raw) })
87    }
88
89    /// Wraps the
90    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
91    /// syscall for the ZX_INFO_THREAD topic.
92    pub fn info(&self) -> Result<ThreadInfo, Status> {
93        Ok(ThreadInfo::from_raw(self.0.get_info_single::<ThreadInfoQuery>()?))
94    }
95
96    /// Wraps the
97    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
98    /// syscall for the ZX_INFO_THREAD_STATS topic.
99    pub fn stats(&self) -> Result<ThreadStats, Status> {
100        Ok(ThreadStats::from_raw(self.0.get_info_single::<ThreadStatsQuery>()?))
101    }
102
103    pub fn read_state_general_regs(&self) -> Result<sys::zx_thread_state_general_regs_t, Status> {
104        let mut state = sys::zx_thread_state_general_regs_t::default();
105        let thread_raw = self.raw_handle();
106        let status = unsafe {
107            sys::zx_thread_read_state(
108                thread_raw,
109                sys::ZX_THREAD_STATE_GENERAL_REGS,
110                std::ptr::from_mut(&mut state).cast::<u8>(),
111                std::mem::size_of_val(&state),
112            )
113        };
114        ok(status).map(|_| state)
115    }
116
117    pub fn write_state_general_regs(
118        &self,
119        state: sys::zx_thread_state_general_regs_t,
120    ) -> Result<(), Status> {
121        let thread_raw = self.raw_handle();
122        let status = unsafe {
123            sys::zx_thread_write_state(
124                thread_raw,
125                sys::ZX_THREAD_STATE_GENERAL_REGS,
126                std::ptr::from_ref(&state).cast::<u8>(),
127                std::mem::size_of_val(&state),
128            )
129        };
130        ok(status)
131    }
132
133    /// Wraps the `zx_thread_raise_exception` syscall.
134    ///
135    /// See https://fuchsia.dev/reference/syscalls/thread_raise_exception?hl=en for details.
136    pub fn raise_user_exception(
137        options: RaiseExceptionOptions,
138        synth_code: u32,
139        synth_data: u32,
140    ) -> Result<(), Status> {
141        let arch = unsafe { std::mem::zeroed::<sys::zx_exception_header_arch_t>() };
142        let context = sys::zx_exception_context_t { arch, synth_code, synth_data };
143
144        // SAFETY: basic FFI call. `&context` is a valid pointer.
145        ok(unsafe { sys::zx_thread_raise_exception(options.bits(), sys::ZX_EXCP_USER, &context) })
146    }
147}
148
149impl Task for Thread {}
150
151struct ThreadStatsQuery;
152unsafe impl ObjectQuery for ThreadStatsQuery {
153    const TOPIC: Topic = Topic::THREAD_STATS;
154    type InfoTy = sys::zx_info_thread_stats_t;
155}
156
157#[cfg(target_arch = "x86_64")]
158unsafe_handle_properties!(object: Thread,
159    props: [
160        {query_ty: REGISTER_GS, tag: RegisterGsTag, prop_ty: usize, set: set_register_gs},
161        {query_ty: REGISTER_FS, tag: RegisterFsTag, prop_ty: usize, set: set_register_fs},
162    ]
163);
164
165#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
166pub struct ThreadStats {
167    pub total_runtime: MonotonicDuration,
168    pub last_scheduled_cpu: u32,
169}
170
171impl ThreadStats {
172    fn from_raw(
173        sys::zx_info_thread_stats_t { total_runtime, last_scheduled_cpu }: sys::zx_info_thread_stats_t,
174    ) -> Self {
175        Self { total_runtime: MonotonicDuration::from_nanos(total_runtime), last_scheduled_cpu }
176    }
177}
178
179struct ThreadInfoQuery;
180unsafe impl ObjectQuery for ThreadInfoQuery {
181    const TOPIC: Topic = Topic::THREAD;
182    type InfoTy = sys::zx_info_thread_t;
183}
184
185#[derive(Debug, Copy, Clone, Eq, PartialEq)]
186pub struct ThreadInfo {
187    pub state: ThreadState,
188    pub cpu_affinity_mask: sys::zx_cpu_set_t,
189}
190
191impl ThreadInfo {
192    fn from_raw(raw: sys::zx_info_thread_t) -> Self {
193        Self {
194            state: ThreadState::from_raw(raw.state, raw.wait_exception_channel_type),
195            cpu_affinity_mask: raw.cpu_affinity_mask,
196        }
197    }
198}
199
200#[derive(Debug, Copy, Clone, Eq, PartialEq)]
201pub enum ThreadState {
202    New,
203    Running,
204    Suspended,
205    Blocked(ThreadBlockType),
206    Dying,
207    Dead,
208    Unknown(u32),
209}
210
211impl ThreadState {
212    fn from_raw(raw_state: u32, raw_wait_exception_channel_type: u32) -> Self {
213        match raw_state {
214            sys::ZX_THREAD_STATE_NEW => Self::New,
215            sys::ZX_THREAD_STATE_RUNNING => Self::Running,
216            sys::ZX_THREAD_STATE_SUSPENDED => Self::Suspended,
217            sys::ZX_THREAD_STATE_BLOCKED => Self::Blocked(ThreadBlockType::Unknown),
218            sys::ZX_THREAD_STATE_BLOCKED_EXCEPTION => Self::Blocked(ThreadBlockType::Exception(
219                ExceptionChannelType::from_raw(raw_wait_exception_channel_type),
220            )),
221            sys::ZX_THREAD_STATE_BLOCKED_SLEEPING => Self::Blocked(ThreadBlockType::Sleeping),
222            sys::ZX_THREAD_STATE_BLOCKED_FUTEX => Self::Blocked(ThreadBlockType::Futex),
223            sys::ZX_THREAD_STATE_BLOCKED_PORT => Self::Blocked(ThreadBlockType::Port),
224            sys::ZX_THREAD_STATE_BLOCKED_CHANNEL => Self::Blocked(ThreadBlockType::Channel),
225            sys::ZX_THREAD_STATE_BLOCKED_WAIT_ONE => Self::Blocked(ThreadBlockType::WaitOne),
226            sys::ZX_THREAD_STATE_BLOCKED_WAIT_MANY => Self::Blocked(ThreadBlockType::WaitMany),
227            sys::ZX_THREAD_STATE_BLOCKED_INTERRUPT => Self::Blocked(ThreadBlockType::Interrupt),
228            sys::ZX_THREAD_STATE_BLOCKED_PAGER => Self::Blocked(ThreadBlockType::Pager),
229            sys::ZX_THREAD_STATE_DYING => Self::Dying,
230            sys::ZX_THREAD_STATE_DEAD => Self::Dead,
231            _ => Self::Unknown(raw_state),
232        }
233    }
234}
235
236#[derive(Debug, Copy, Clone, Eq, PartialEq)]
237pub enum ThreadBlockType {
238    Exception(ExceptionChannelType),
239    Sleeping,
240    Futex,
241    Port,
242    Channel,
243    WaitOne,
244    WaitMany,
245    Interrupt,
246    Pager,
247    Unknown,
248}
249
250#[derive(Debug, Copy, Clone, Eq, PartialEq)]
251pub enum ExceptionChannelType {
252    None,
253    Debugger,
254    Thread,
255    Process,
256    Job,
257    JobDebugger,
258    Unknown(u32),
259}
260
261impl ExceptionChannelType {
262    fn from_raw(raw_wait_exception_channel_type: u32) -> Self {
263        match raw_wait_exception_channel_type {
264            sys::ZX_EXCEPTION_CHANNEL_TYPE_NONE => Self::None,
265            sys::ZX_EXCEPTION_CHANNEL_TYPE_DEBUGGER => Self::Debugger,
266            sys::ZX_EXCEPTION_CHANNEL_TYPE_THREAD => Self::Thread,
267            sys::ZX_EXCEPTION_CHANNEL_TYPE_PROCESS => Self::Process,
268            sys::ZX_EXCEPTION_CHANNEL_TYPE_JOB => Self::Job,
269            sys::ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER => Self::JobDebugger,
270            _ => Self::Unknown(raw_wait_exception_channel_type),
271        }
272    }
273}