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