use alloc::sync::{Arc, Weak};
use axpoll::{IoEvents, Pollable};
use axpoll_set::PollSet;
use super::TerminalStateLock;
use crate::{
StarryError, StarryResult,
task::{ProcessGroup, Session, current_user_task},
};
pub struct JobControl {
state: TerminalStateLock<JobControlState>,
poll_fg: PollSet,
}
struct JobControlState {
foreground: Weak<ProcessGroup>,
session: Weak<Session>,
}
impl Default for JobControl {
fn default() -> Self {
Self::new()
}
}
impl JobControl {
pub fn new() -> Self {
Self {
state: TerminalStateLock::new(JobControlState {
foreground: Weak::new(),
session: Weak::new(),
}),
poll_fg: PollSet::new(),
}
}
pub fn current_in_foreground(&self) -> bool {
self.state.lock().foreground.upgrade().is_none_or(|pg| {
Arc::ptr_eq(¤t_user_task().as_thread().proc_data.proc.group(), &pg)
})
}
pub fn foreground(&self) -> Option<Arc<ProcessGroup>> {
self.state.lock().foreground.upgrade()
}
pub fn set_foreground(&self, pg: &Arc<ProcessGroup>) -> StarryResult<()> {
let mut state = self.state.lock();
let weak = Arc::downgrade(pg);
if Weak::ptr_eq(&weak, &state.foreground) {
return Ok(());
}
let Some(session) = state.session.upgrade() else {
return Err(StarryError::OperationNotPermitted);
};
if !Arc::ptr_eq(&pg.session(), &session) {
return Err(StarryError::OperationNotPermitted);
}
state.foreground = weak;
drop(state);
unsafe { self.poll_fg.wake(IoEvents::IN) };
Ok(())
}
pub fn set_session(&self, session: &Arc<Session>) -> StarryResult<()> {
let mut state = self.state.lock();
if let Some(existing) = state.session.upgrade() {
if Arc::ptr_eq(&existing, session) {
return Ok(());
}
return Err(StarryError::ResourceBusy);
}
state.session = Arc::downgrade(session);
Ok(())
}
pub fn clear_session(&self, session: &Arc<Session>) {
let mut state = self.state.lock();
if state
.session
.upgrade()
.is_some_and(|existing| Arc::ptr_eq(&existing, session))
{
state.session = Weak::new();
}
let foreground_cleared = state
.foreground
.upgrade()
.is_some_and(|pg| Arc::ptr_eq(&pg.session(), session));
if foreground_cleared {
state.foreground = Weak::new();
}
drop(state);
if foreground_cleared {
unsafe { self.poll_fg.wake(IoEvents::IN) };
}
}
}
impl Pollable for JobControl {
fn poll(&self) -> IoEvents {
let mut events = IoEvents::empty();
events.set(IoEvents::IN, self.current_in_foreground());
events
}
unsafe fn register_shared(
&self,
sink: &mut dyn axpoll::SharedRegistrationSink,
events: IoEvents,
) {
if events.contains(IoEvents::IN) {
unsafe { sink.register_shared(&self.poll_fg, IoEvents::IN) };
}
}
}