starnix_core/execution/
executor.rs1use crate::execution::loop_entry::enter_syscall_loop;
6use crate::ptrace::{PtraceCoreState, ptrace_attach_from_state};
7use crate::task::{CurrentTask, DelayedReleaser, ExitStatus, TaskBuilder};
8use anyhow::Error;
9use starnix_logging::{log_error, log_warn};
10use starnix_sync::{LockBefore, Locked, TaskRelease, Unlocked};
11use starnix_types::ownership::WeakRef;
12use starnix_uapi::errors::Errno;
13use starnix_uapi::{errno, error};
14use std::os::unix::thread::JoinHandleExt;
15use std::sync::Arc;
16use std::sync::mpsc::sync_channel;
17
18pub fn execute_task_with_prerun_result<L, F, R, G>(
19 locked: &mut Locked<L>,
20 task_builder: TaskBuilder,
21 pre_run: F,
22 task_complete: G,
23 ptrace_state: Option<PtraceCoreState>,
24) -> Result<R, Errno>
25where
26 L: LockBefore<TaskRelease>,
27 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> Result<R, Errno> + Send + Sync + 'static,
28 R: Send + Sync + 'static,
29 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
30{
31 let (sender, receiver) = sync_channel::<Result<R, Errno>>(1);
32 execute_task(
33 locked,
34 task_builder,
35 move |current_task, locked| match pre_run(current_task, locked) {
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 ptrace_state,
47 )?;
48 receiver.recv().map_err(|e| {
49 log_error!("Unable to retrieve result from `pre_run`: {e:?}");
50 errno!(EINVAL)
51 })?
52}
53
54pub fn execute_task<L, F, G>(
55 locked: &mut Locked<L>,
56 task_builder: TaskBuilder,
57 pre_run: F,
58 task_complete: G,
59 ptrace_state: Option<PtraceCoreState>,
60) -> Result<(), Errno>
61where
62 L: LockBefore<TaskRelease>,
63 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> Result<(), Errno> + Send + Sync + 'static,
64 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
65{
66 let process_handle = task_builder.task.thread_group().process.raw_handle();
69 let old_process_handle = unsafe { thrd_set_zx_process(process_handle) };
72 scopeguard::defer! {
73 unsafe {
76 thrd_set_zx_process(old_process_handle);
77 };
78 };
79
80 let weak_task = WeakRef::from(&task_builder.task);
81 let ref_task = weak_task.upgrade().unwrap();
82 if let Some(ptrace_state) = ptrace_state {
83 let _ = ptrace_attach_from_state(
84 locked.cast_locked::<TaskRelease>(),
85 &task_builder.task,
86 ptrace_state,
87 );
88 }
89
90 let live_task = ref_task.live().unwrap();
92 let mut task_thread_guard = live_task.thread.write();
93
94 let (sender, receiver) = sync_channel::<TaskBuilder>(1);
97 let result = std::thread::Builder::new().name("user-thread".to_string()).spawn(move || {
98 #[allow(
100 clippy::undocumented_unsafe_blocks,
101 reason = "Force documented unsafe blocks in Starnix"
102 )]
103 let locked = unsafe { Unlocked::new() };
104
105 let mut current_task: CurrentTask = receiver
109 .recv()
110 .expect("caller should always send task builder before disconnecting")
111 .into();
112
113 std::mem::drop(receiver);
116
117 let pre_run_result = { pre_run(locked, &mut current_task) };
118 if pre_run_result.is_err() {
119 if current_task.exit_status().is_none() {
122 log_error!("Pre run failed from {pre_run_result:?}. The task will not be run.");
123 }
124
125 std::mem::drop(task_complete);
128 } else {
129 let exit_status = enter_syscall_loop(locked, &mut current_task);
130 current_task.write().set_exit_status(exit_status.clone());
131 task_complete(Ok(exit_status));
132 }
133
134 current_task.release(locked);
137
138 DelayedReleaser::finalize();
140 });
141 let join_handle = match result {
142 Ok(handle) => handle,
143 Err(e) => {
144 task_builder.release(locked);
145 match e.kind() {
146 std::io::ErrorKind::WouldBlock => return error!(EAGAIN),
147 other => panic!("unexpected error on thread spawn: {other}"),
148 }
149 }
150 };
151
152 let pthread = join_handle.as_pthread_t();
158 #[allow(
159 clippy::undocumented_unsafe_blocks,
160 reason = "Force documented unsafe blocks in Starnix"
161 )]
162 let raw_thread_handle =
163 unsafe { zx::Unowned::<'_, zx::Thread>::from_raw_handle(thrd_get_zx_handle(pthread)) };
164 *task_thread_guard = Some(Arc::new(
165 raw_thread_handle
166 .duplicate(zx::Rights::SAME_RIGHTS)
167 .expect("must have RIGHT_DUPLICATE on handle we created"),
168 ));
169 drop(task_thread_guard);
171 if let Err(err) = ref_task.sync_scheduler_state_to_role() {
172 log_warn!(err:?; "Couldn't update freshly spawned thread's profile.");
173 }
174
175 ref_task.record_pid_koid_mapping();
177
178 sender
182 .send(task_builder)
183 .expect("receiver should not be disconnected because thread spawned successfully");
184
185 Ok(())
186}
187
188unsafe extern "C" {
189 fn thrd_set_zx_process(handle: zx::sys::zx_handle_t) -> zx::sys::zx_handle_t;
191
192 fn thrd_get_zx_handle(thread: u64) -> zx::sys::zx_handle_t;
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use crate::ptrace::StopState;
201 use crate::signals::SignalInfo;
202 use crate::testing::*;
203 use starnix_uapi::signals::{SIGCONT, SIGSTOP};
204
205 #[::fuchsia::test]
206 async fn test_block_while_stopped_stop_and_continue() {
207 spawn_kernel_and_run(async |locked, task| {
208 task.block_while_stopped(locked);
210
211 task.thread_group().set_stopped(
213 StopState::GroupStopping,
214 Some(SignalInfo::kernel(SIGSTOP)),
215 false,
216 );
217
218 let thread = std::thread::spawn({
219 let task = task.weak_task();
220 move || {
221 let task = task.upgrade().expect("task must be alive");
222 while !task.read().is_blocked() {
224 std::thread::sleep(std::time::Duration::from_millis(10));
225 }
226
227 task.thread_group().set_stopped(
229 StopState::Waking,
230 Some(SignalInfo::kernel(SIGCONT)),
231 false,
232 );
233 }
234 });
235
236 task.block_while_stopped(locked);
238
239 thread.join().expect("joined");
241
242 task.block_while_stopped(locked);
244 })
245 .await;
246 }
247
248 #[::fuchsia::test]
249 async fn test_block_while_stopped_stop_and_exit() {
250 spawn_kernel_and_run(async |locked, task| {
251 task.block_while_stopped(locked);
253
254 task.thread_group().set_stopped(
256 StopState::GroupStopping,
257 Some(SignalInfo::kernel(SIGSTOP)),
258 false,
259 );
260
261 let thread = std::thread::spawn({
262 let task = task.weak_task();
263 move || {
264 #[allow(
265 clippy::undocumented_unsafe_blocks,
266 reason = "Force documented unsafe blocks in Starnix"
267 )]
268 let locked = unsafe { Unlocked::new() };
269 let task = task.upgrade().expect("task must be alive");
270 while !task.read().is_blocked() {
272 std::thread::sleep(std::time::Duration::from_millis(10));
273 }
274
275 task.thread_group().exit(locked, ExitStatus::Exit(1), None);
277 }
278 });
279
280 task.block_while_stopped(locked);
282
283 thread.join().expect("joined");
285
286 task.block_while_stopped(locked);
288 })
289 .await;
290 }
291}