starnix_core/execution/
executor.rs1use crate::execution::loop_entry::enter_syscall_loop;
6use crate::task::{CurrentTask, DelayedReleaser, ExitStatus, TaskBuilder, ZirconThread};
7use anyhow::Error;
8use starnix_logging::{log_error, log_warn};
9use starnix_sync::{ExecutorVmarManagerLock, LockDepMutex};
10use starnix_uapi::errors::Errno;
11use starnix_uapi::{errno, error};
12use std::os::unix::thread::JoinHandleExt;
13use std::sync::Arc;
14use std::sync::mpsc::sync_channel;
15use thread_create_vmars::ThreadCreateVmars;
16
17struct ExecutorVmarManager(LockDepMutex<ThreadCreateVmars, ExecutorVmarManagerLock>);
21
22pub fn execute_task_with_prerun_result<F, R, G>(
23 task_builder: TaskBuilder,
24 pre_run: F,
25 task_complete: G,
26) -> Result<R, Errno>
27where
28 F: FnOnce(&mut CurrentTask) -> Result<R, Errno> + Send + Sync + 'static,
29 R: Send + Sync + 'static,
30 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
31{
32 let (sender, receiver) = sync_channel::<Result<R, Errno>>(1);
33 execute_task(
34 task_builder,
35 move |current_task| match pre_run(current_task) {
36 Err(errno) => {
37 let _ = sender.send(Err(errno.clone()));
38 Err(errno)
39 }
40 Ok(value) => sender.send(Ok(value)).map_err(|error| {
41 log_error!("Unable to send `pre_run` result: {error:?}");
42 errno!(EINVAL)
43 }),
44 },
45 task_complete,
46 )?;
47 receiver.recv().map_err(|e| {
48 log_error!("Unable to retrieve result from `pre_run`: {e:?}");
49 errno!(EINVAL)
50 })?
51}
52
53pub fn execute_task<F, G>(
54 task_builder: TaskBuilder,
55 pre_run: F,
56 task_complete: G,
57) -> Result<(), Errno>
58where
59 F: FnOnce(&mut CurrentTask) -> Result<(), Errno> + Send + Sync + 'static,
60 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
61{
62 let process_handle = task_builder.task.thread_group().process.raw_handle();
65
66 let kernel = task_builder.task.kernel();
67 let create_vmars =
68 kernel.expando.get_or_init(|| ExecutorVmarManager(ThreadCreateVmars::new().into()));
69 let mut create_vmars = create_vmars.0.lock();
70
71 let old_handles = unsafe {
78 thrd_set_zx_create_handles(thrd_zx_create_handles {
79 process: process_handle,
80 machine_stack_vmar: create_vmars.machine_stack.probe()?.raw_handle(),
81 security_stack_vmar: create_vmars.security_stack.probe()?.raw_handle(),
82 thread_block_vmar: create_vmars.thread_block.probe()?.raw_handle(),
83 })
84 };
85 scopeguard::defer! {
86 unsafe {
91 thrd_set_zx_create_handles(old_handles);
92 };
93 };
94
95 let ref_task = Arc::clone(&task_builder.task);
96 let running_state = ref_task.running_state().unwrap();
97
98 let (sender, receiver) = sync_channel::<TaskBuilder>(1);
101 let result = std::thread::Builder::new().name("user-thread".to_string()).spawn(move || {
102 let _rcu_registration = fuchsia_rcu::register_thread();
103
104 let mut current_task: CurrentTask = receiver
108 .recv()
109 .expect("caller should always send task builder before disconnecting")
110 .into();
111
112 std::mem::drop(receiver);
115
116 let pre_run_result = { pre_run(&mut current_task) };
117 if pre_run_result.is_err() {
118 if current_task.exit_status().is_none() {
121 log_error!("Pre run failed from {pre_run_result:?}. The task will not be run.");
122 }
123
124 std::mem::drop(task_complete);
127 } else {
128 let exit_status = enter_syscall_loop(&mut current_task);
129 current_task.write().set_exit_status(exit_status.clone());
130 task_complete(Ok(exit_status));
131 }
132
133 current_task.release(());
136
137 DelayedReleaser::finalize();
139 });
140 let join_handle = match result {
141 Ok(handle) => handle,
142 Err(e) => {
143 task_builder.release(());
144 match e.kind() {
145 std::io::ErrorKind::WouldBlock => return error!(EAGAIN),
146 other => panic!("unexpected error on thread spawn: {other}"),
147 }
148 }
149 };
150 std::mem::drop(create_vmars);
155
156 task_builder.task.write().set_spawned();
158
159 let pthread = join_handle.as_pthread_t();
165 #[allow(
166 clippy::undocumented_unsafe_blocks,
167 reason = "Force documented unsafe blocks in Starnix"
168 )]
169 let raw_thread_handle =
170 unsafe { zx::Unowned::<'_, zx::Thread>::from_raw_handle(thrd_get_zx_handle(pthread)) };
171 let thread = Arc::new(
172 raw_thread_handle
173 .duplicate_handle(zx::Rights::SAME_RIGHTS)
174 .expect("must have RIGHT_DUPLICATE on handle we created"),
175 );
176 running_state.thread.set(ZirconThread::new(thread)).expect("thread should only be set once");
177 if let Err(err) = ref_task.sync_scheduler_state_to_role() {
179 log_warn!(err:?; "Couldn't update freshly spawned thread's profile.");
180 }
181
182 ref_task.record_pid_koid_mapping();
184
185 sender
189 .send(task_builder)
190 .expect("receiver should not be disconnected because thread spawned successfully");
191
192 Ok(())
193}
194
195#[repr(C)]
196#[derive(Debug)]
197pub struct thrd_zx_create_handles {
198 pub process: zx::sys::zx_handle_t,
199 pub machine_stack_vmar: zx::sys::zx_handle_t,
200 pub security_stack_vmar: zx::sys::zx_handle_t,
201 pub thread_block_vmar: zx::sys::zx_handle_t,
202}
203unsafe extern "C" {
204 fn thrd_set_zx_create_handles(handles: thrd_zx_create_handles) -> thrd_zx_create_handles;
205
206 fn thrd_get_zx_handle(thread: u64) -> zx::sys::zx_handle_t;
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::ptrace::StopState;
215 use crate::signals::SignalInfo;
216 use crate::testing::*;
217 use starnix_uapi::signals::{SIGCONT, SIGSTOP};
218
219 #[::fuchsia::test]
220 async fn test_block_if_stopped_stop_and_continue() {
221 spawn_kernel_and_run(async |task| {
222 assert!(!task.block_if_stopped());
224
225 task.thread_group().set_stopped(
227 StopState::GroupStopping,
228 Some(SignalInfo::kernel(SIGSTOP)),
229 false,
230 );
231
232 let thread = std::thread::spawn({
233 let task = task.weak_task();
234 move || {
235 let task = task.upgrade().expect("task must be alive");
236 while !task.read().is_blocked() {
238 std::thread::sleep(std::time::Duration::from_millis(10));
239 }
240
241 task.thread_group().set_stopped(
243 StopState::Waking,
244 Some(SignalInfo::kernel(SIGCONT)),
245 false,
246 );
247 }
248 });
249
250 assert!(task.block_if_stopped());
252
253 thread.join().expect("joined");
255
256 assert!(!task.block_if_stopped());
258 })
259 .await;
260 }
261
262 #[::fuchsia::test]
263 async fn test_block_if_stopped_stop_and_exit() {
264 spawn_kernel_and_run(async |task| {
265 assert!(!task.block_if_stopped());
267
268 task.thread_group().set_stopped(
270 StopState::GroupStopping,
271 Some(SignalInfo::kernel(SIGSTOP)),
272 false,
273 );
274
275 let thread = std::thread::spawn({
276 let task = task.weak_task();
277 move || {
278 let task = task.upgrade().expect("task must be alive");
279 while !task.read().is_blocked() {
281 std::thread::sleep(std::time::Duration::from_millis(10));
282 }
283
284 task.thread_group().kill(ExitStatus::Exit(1), None);
286 }
287 });
288
289 assert!(task.block_if_stopped());
291
292 thread.join().expect("joined");
294
295 assert!(!task.block_if_stopped());
297 })
298 .await;
299 }
300}