use alloc::sync::Arc;
use core::task::Poll;
use axpoll::IoEvents;
use axpoll_set::PollSet;
use starry_signal::Signo;
use super::{PidRoleLease, ProcessData, Tid, TidNumber, current_user_task, future};
use crate::sync::{IrqMutex, Mutex};
struct VforkDone {
done: bool,
poll: Arc<PollSet>,
}
struct RetiredLeader {
nice: i32,
tid_lease: PidRoleLease<Tid>,
}
impl VforkDone {
fn new(poll: Arc<PollSet>) -> Self {
Self { done: false, poll }
}
}
pub(super) struct ProcessWaitState {
child_exit_event: Arc<PollSet>,
exit_event: Arc<PollSet>,
thread_exit_event: Arc<PollSet>,
exec_lock: Mutex<()>,
exit_signal: Option<Signo>,
wait_parent_tid: TidNumber,
retired_leader: IrqMutex<Option<RetiredLeader>>,
vfork_done: IrqMutex<Option<VforkDone>>,
}
impl ProcessWaitState {
pub(super) fn new(exit_signal: Option<Signo>, wait_parent_tid: TidNumber) -> Self {
Self {
child_exit_event: Arc::default(),
exit_event: Arc::default(),
thread_exit_event: Arc::default(),
exec_lock: Mutex::new(()),
exit_signal,
wait_parent_tid,
retired_leader: IrqMutex::new(None),
vfork_done: IrqMutex::new(None),
}
}
pub(super) fn exit_event_arc(&self) -> Arc<PollSet> {
self.exit_event.clone()
}
}
pub async fn wait_on_pollset<T>(poll: &PollSet, mut check: impl FnMut() -> Option<T>) -> T {
future::poll_shared(
|| check().map_or(Poll::Pending, Poll::Ready),
|registrar| unsafe { registrar.register(poll, IoEvents::IN) },
)
.await
}
impl ProcessData {
pub fn child_exit_event(&self) -> &PollSet {
&self.wait.child_exit_event
}
pub fn exit_event(&self) -> &PollSet {
&self.wait.exit_event
}
pub fn thread_exit_event(&self) -> &PollSet {
&self.wait.thread_exit_event
}
pub fn exec_lock(&self) -> &Mutex<()> {
&self.wait.exec_lock
}
pub fn exit_signal(&self) -> Option<Signo> {
self.wait.exit_signal
}
pub fn wait_parent_tid(&self) -> TidNumber {
self.wait.wait_parent_tid
}
pub(crate) fn retire_leader(&self, nice: i32, tid_lease: PidRoleLease<Tid>) {
let previous = self
.wait
.retired_leader
.lock()
.replace(RetiredLeader { nice, tid_lease });
assert!(previous.is_none(), "process retired its leader twice");
}
pub(crate) fn retired_leader_transfer_ready(&self) -> bool {
self.wait
.retired_leader
.lock()
.as_ref()
.is_some_and(|leader| leader.tid_lease.task_transfer_ready())
}
pub fn retired_leader_nice(&self) -> Option<i32> {
self.wait
.retired_leader
.lock()
.as_ref()
.map(|leader| leader.nice)
}
pub(crate) fn take_retired_leader_for_zombie(&self) -> (i32, PidRoleLease<Tid>) {
let leader = self
.wait
.retired_leader
.lock()
.take()
.expect("process lost its retired leader state");
(leader.nice, leader.tid_lease)
}
pub(crate) fn take_retired_leader_for_exec(&self) -> (i32, PidRoleLease<Tid>) {
let leader = self
.wait
.retired_leader
.lock()
.take()
.expect("process lost its retired leader state");
assert!(
leader.tid_lease.task_transfer_ready(),
"exec transferred a leader identity before its exit path completed"
);
(leader.nice, leader.tid_lease)
}
pub fn is_clone_child(&self) -> bool {
self.wait.exit_signal != Some(Signo::SIGCHLD)
}
pub fn set_vfork_done(&self, poll: Arc<PollSet>) {
*self.wait.vfork_done.lock() = Some(VforkDone::new(poll));
}
pub fn wait_vfork_done(&self) {
let poll = {
let guard = self.wait.vfork_done.lock();
match guard.as_ref() {
Some(vfork) => vfork.poll.clone(),
None => return,
}
};
let curr_task = current_user_task();
let curr_thr = curr_task.as_thread();
loop {
let result = future::block_on_user(
&curr_task,
wait_on_pollset(&poll, || {
self.wait
.vfork_done
.lock()
.as_ref()
.map(|vfork| vfork.done)
.unwrap_or(true)
.then_some(())
}),
);
match result {
future::UserWaitOutcome::Ready(()) => return,
future::UserWaitOutcome::Interrupted if curr_thr.has_exit_request() => return,
future::UserWaitOutcome::Interrupted => continue,
future::UserWaitOutcome::TimedOut => {
unreachable!("vfork completion wait has no deadline")
}
}
}
}
pub fn notify_vfork_done(&self) {
let poll = {
let mut guard = self.wait.vfork_done.lock();
match guard.as_mut() {
Some(vfork) => {
vfork.done = true;
vfork.poll.clone()
}
None => return,
}
};
unsafe { poll.wake(IoEvents::IN) };
}
}