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