Skip to main content

starry_process/
session.rs

1use alloc::{
2    sync::{Arc, Weak},
3    vec::Vec,
4};
5use core::{any::Any, convert::Infallible, fmt};
6
7use ax_kspin::SpinNoIrq;
8use weak_map::WeakMap;
9
10use crate::{Pid, ProcessGroup};
11
12/// A [`Session`] is a collection of [`ProcessGroup`]s.
13pub struct Session {
14    sid: Pid,
15    pub(crate) process_groups: SpinNoIrq<WeakMap<Pid, Weak<ProcessGroup>>>,
16    terminal: SpinNoIrq<Option<Arc<dyn Any + Send + Sync>>>,
17}
18
19impl Session {
20    /// Create a new [`Session`].
21    pub(crate) fn new(sid: Pid) -> Arc<Self> {
22        Arc::new(Self {
23            sid,
24            process_groups: SpinNoIrq::new(WeakMap::new()),
25            terminal: SpinNoIrq::new(None),
26        })
27    }
28}
29
30impl Session {
31    /// The [`Session`] ID.
32    pub fn sid(&self) -> Pid {
33        self.sid
34    }
35
36    /// The [`ProcessGroup`]s that belong to this [`Session`].
37    pub fn process_groups(&self) -> Vec<Arc<ProcessGroup>> {
38        self.process_groups.lock().values().collect()
39    }
40
41    /// Sets the terminal for this session.
42    pub fn set_terminal_with(&self, terminal: impl FnOnce() -> Arc<dyn Any + Send + Sync>) -> bool {
43        self.try_set_terminal_with(|| Ok::<_, Infallible>(terminal()))
44            .unwrap()
45    }
46
47    /// Sets the terminal for this session with a fallible terminal initializer.
48    pub fn try_set_terminal_with<E>(
49        &self,
50        terminal: impl FnOnce() -> Result<Arc<dyn Any + Send + Sync>, E>,
51    ) -> Result<bool, E> {
52        let mut guard = self.terminal.lock();
53        if guard.is_some() {
54            return Ok(false);
55        }
56        *guard = Some(terminal()?);
57        Ok(true)
58    }
59
60    /// Unsets the terminal for this session if it is the given terminal.
61    pub fn unset_terminal(&self, term: &Arc<dyn Any + Send + Sync>) -> bool {
62        let mut guard = self.terminal.lock();
63        if guard.as_ref().is_some_and(|it| Arc::ptr_eq(it, term)) {
64            *guard = None;
65            true
66        } else {
67            false
68        }
69    }
70
71    /// Gets the terminal for this session, if it exists.
72    pub fn terminal(&self) -> Option<Arc<dyn Any + Send + Sync>> {
73        self.terminal.lock().clone()
74    }
75}
76
77impl fmt::Debug for Session {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "Session({})", self.sid)
80    }
81}