use alloc::sync::{Arc, Weak};
use core::task::Context;
use ax_task::current;
use axpoll::{IoEvents, PollSet, Pollable};
use crate::{
StarryError, StarryResult,
sync::IrqMutex,
task::{AsThread, ProcessGroup, Session},
};
pub struct JobControl {
state: IrqMutex<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: IrqMutex::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().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
}
fn register(&self, context: &mut Context<'_>, events: IoEvents) {
if events.contains(IoEvents::IN) {
unsafe { self.poll_fg.register(context.waker(), IoEvents::IN) };
}
}
}