1use 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, Mutex, 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;
17use thread_create_vmars::ThreadCreateVmars;
18
19struct ExecutorVmarManager(Mutex<ThreadCreateVmars>);
23
24pub fn execute_task_with_prerun_result<L, F, R, G>(
25 locked: &mut Locked<L>,
26 task_builder: TaskBuilder,
27 pre_run: F,
28 task_complete: G,
29 ptrace_state: Option<PtraceCoreState>,
30) -> Result<R, Errno>
31where
32 L: LockBefore<TaskRelease>,
33 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> Result<R, Errno> + Send + Sync + 'static,
34 R: Send + Sync + 'static,
35 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
36{
37 let (sender, receiver) = sync_channel::<Result<R, Errno>>(1);
38 execute_task(
39 locked,
40 task_builder,
41 move |current_task, locked| match pre_run(current_task, locked) {
42 Err(errno) => {
43 let _ = sender.send(Err(errno.clone()));
44 Err(errno)
45 }
46 Ok(value) => sender.send(Ok(value)).map_err(|error| {
47 log_error!("Unable to send `pre_run` result: {error:?}");
48 errno!(EINVAL)
49 }),
50 },
51 task_complete,
52 ptrace_state,
53 )?;
54 receiver.recv().map_err(|e| {
55 log_error!("Unable to retrieve result from `pre_run`: {e:?}");
56 errno!(EINVAL)
57 })?
58}
59
60pub fn execute_task<L, F, G>(
61 locked: &mut Locked<L>,
62 task_builder: TaskBuilder,
63 pre_run: F,
64 task_complete: G,
65 ptrace_state: Option<PtraceCoreState>,
66) -> Result<(), Errno>
67where
68 L: LockBefore<TaskRelease>,
69 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> Result<(), Errno> + Send + Sync + 'static,
70 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
71{
72 let process_handle = task_builder.task.thread_group().process.raw_handle();
75
76 let kernel = task_builder.task.kernel();
77 let create_vmars =
78 kernel.expando.get_or_init(|| ExecutorVmarManager(Mutex::new(ThreadCreateVmars::new())));
79 let mut create_vmars = create_vmars.0.lock();
80
81 let old_handles = unsafe {
88 thrd_set_zx_create_handles(thrd_zx_create_handles {
89 process: process_handle,
90 machine_stack_vmar: create_vmars.machine_stack.probe()?.raw_handle(),
91 security_stack_vmar: create_vmars.security_stack.probe()?.raw_handle(),
92 thread_block_vmar: create_vmars.thread_block.probe()?.raw_handle(),
93 })
94 };
95 scopeguard::defer! {
96 unsafe {
101 thrd_set_zx_create_handles(old_handles);
102 };
103 };
104
105 let weak_task = WeakRef::from(&task_builder.task);
106 let ref_task = weak_task.upgrade().unwrap();
107 if let Some(ptrace_state) = ptrace_state {
108 let _ = ptrace_attach_from_state(
109 locked.cast_locked::<TaskRelease>(),
110 &task_builder.task,
111 ptrace_state,
112 );
113 }
114
115 let live_task = ref_task.live().unwrap();
117 let mut task_thread_guard = live_task.thread.write();
118
119 let (sender, receiver) = sync_channel::<TaskBuilder>(1);
122 let result = std::thread::Builder::new().name("user-thread".to_string()).spawn(move || {
123 #[allow(
125 clippy::undocumented_unsafe_blocks,
126 reason = "Force documented unsafe blocks in Starnix"
127 )]
128 let locked = unsafe { Unlocked::new() };
129
130 let mut current_task: CurrentTask = receiver
134 .recv()
135 .expect("caller should always send task builder before disconnecting")
136 .into();
137
138 std::mem::drop(receiver);
141
142 let pre_run_result = { pre_run(locked, &mut current_task) };
143 if pre_run_result.is_err() {
144 if current_task.exit_status().is_none() {
147 log_error!("Pre run failed from {pre_run_result:?}. The task will not be run.");
148 }
149
150 std::mem::drop(task_complete);
153 } else {
154 let exit_status = enter_syscall_loop(locked, &mut current_task);
155 current_task.write().set_exit_status(exit_status.clone());
156 task_complete(Ok(exit_status));
157 }
158
159 current_task.release(locked);
162
163 DelayedReleaser::finalize();
165 });
166 let join_handle = match result {
167 Ok(handle) => handle,
168 Err(e) => {
169 task_builder.release(locked);
170 match e.kind() {
171 std::io::ErrorKind::WouldBlock => return error!(EAGAIN),
172 other => panic!("unexpected error on thread spawn: {other}"),
173 }
174 }
175 };
176
177 task_builder.task.write().set_spawned();
179
180 let pthread = join_handle.as_pthread_t();
186 #[allow(
187 clippy::undocumented_unsafe_blocks,
188 reason = "Force documented unsafe blocks in Starnix"
189 )]
190 let raw_thread_handle =
191 unsafe { zx::Unowned::<'_, zx::Thread>::from_raw_handle(thrd_get_zx_handle(pthread)) };
192 *task_thread_guard = Some(Arc::new(
193 raw_thread_handle
194 .duplicate(zx::Rights::SAME_RIGHTS)
195 .expect("must have RIGHT_DUPLICATE on handle we created"),
196 ));
197 drop(task_thread_guard);
199 if let Err(err) = ref_task.sync_scheduler_state_to_role() {
200 log_warn!(err:?; "Couldn't update freshly spawned thread's profile.");
201 }
202
203 ref_task.record_pid_koid_mapping();
205
206 sender
210 .send(task_builder)
211 .expect("receiver should not be disconnected because thread spawned successfully");
212
213 Ok(())
214}
215
216#[repr(C)]
217#[derive(Debug)]
218pub struct thrd_zx_create_handles {
219 pub process: zx::sys::zx_handle_t,
220 pub machine_stack_vmar: zx::sys::zx_handle_t,
221 pub security_stack_vmar: zx::sys::zx_handle_t,
222 pub thread_block_vmar: zx::sys::zx_handle_t,
223}
224unsafe extern "C" {
225 fn thrd_set_zx_create_handles(handles: thrd_zx_create_handles) -> thrd_zx_create_handles;
226
227 fn thrd_get_zx_handle(thread: u64) -> zx::sys::zx_handle_t;
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::ptrace::StopState;
236 use crate::signals::SignalInfo;
237 use crate::testing::*;
238 use starnix_uapi::signals::{SIGCONT, SIGSTOP};
239
240 #[::fuchsia::test]
241 async fn test_block_while_stopped_stop_and_continue() {
242 spawn_kernel_and_run(async |locked, task| {
243 task.block_while_stopped(locked);
245
246 task.thread_group().set_stopped(
248 StopState::GroupStopping,
249 Some(SignalInfo::kernel(SIGSTOP)),
250 false,
251 );
252
253 let thread = std::thread::spawn({
254 let task = task.weak_task();
255 move || {
256 let task = task.upgrade().expect("task must be alive");
257 while !task.read().is_blocked() {
259 std::thread::sleep(std::time::Duration::from_millis(10));
260 }
261
262 task.thread_group().set_stopped(
264 StopState::Waking,
265 Some(SignalInfo::kernel(SIGCONT)),
266 false,
267 );
268 }
269 });
270
271 task.block_while_stopped(locked);
273
274 thread.join().expect("joined");
276
277 task.block_while_stopped(locked);
279 })
280 .await;
281 }
282
283 #[::fuchsia::test]
284 async fn test_block_while_stopped_stop_and_exit() {
285 spawn_kernel_and_run(async |locked, task| {
286 task.block_while_stopped(locked);
288
289 task.thread_group().set_stopped(
291 StopState::GroupStopping,
292 Some(SignalInfo::kernel(SIGSTOP)),
293 false,
294 );
295
296 let thread = std::thread::spawn({
297 let task = task.weak_task();
298 move || {
299 #[allow(
300 clippy::undocumented_unsafe_blocks,
301 reason = "Force documented unsafe blocks in Starnix"
302 )]
303 let locked = unsafe { Unlocked::new() };
304 let task = task.upgrade().expect("task must be alive");
305 while !task.read().is_blocked() {
307 std::thread::sleep(std::time::Duration::from_millis(10));
308 }
309
310 task.thread_group().exit(locked, ExitStatus::Exit(1), None);
312 }
313 });
314
315 task.block_while_stopped(locked);
317
318 thread.join().expect("joined");
320
321 task.block_while_stopped(locked);
323 })
324 .await;
325 }
326}