use libc::c_int;
use process_manager::PROCESS_MANAGER;
use shell_child::ShellChildArc;
use std::any::Any;
use std::cell::RefCell;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::thread::JoinHandle;
use std::thread::ThreadId;
pub struct LocalShell {
processes: Vec<ShellChildArc>,
signaled: bool,
}
impl LocalShell {
fn new() -> LocalShell {
LocalShell {
processes: Vec::new(),
signaled: false,
}
}
pub fn add_process(&mut self, process: &ShellChildArc) {
self.processes.push(process.clone());
}
pub fn remove_process(&mut self, process: &ShellChildArc) {
self.processes.retain(|p| !Arc::ptr_eq(p, process));
}
pub fn signal(&mut self, signal: c_int) {
self.signaled = true;
for process in &self.processes {
let lock = process.read().unwrap();
if let Some(child) = lock.as_ref() {
if let Err(error) = child.signal(signal) {
error!("Failed to send a signal {:?}", error);
}
}
}
}
pub fn wait(&mut self) {
for process in &self.processes {
let mut lock = process.write().unwrap();
if let Some(child) = lock.take() {
if let Err(error) = child.wait() {
error!("Failed to wait process {:?}", error);
}
}
}
}
pub fn signaled(&self) -> bool {
self.signaled
}
}
struct LocalShellScope(ThreadId, Arc<Mutex<LocalShell>>);
impl LocalShellScope {
fn new(arc: &Arc<Mutex<LocalShell>>) -> LocalShellScope {
let mut lock = PROCESS_MANAGER.lock().unwrap();
let id = thread::current().id();
lock.add_local_shell(&id, arc);
LocalShellScope(id, arc.clone())
}
}
impl Default for LocalShellScope {
fn default() -> LocalShellScope {
LocalShellScope::new(&Arc::new(Mutex::new(LocalShell::new())))
}
}
impl Drop for LocalShellScope {
fn drop(&mut self) {
let mut lock = PROCESS_MANAGER.lock().unwrap();
lock.remove_local_shell(&self.0);
}
}
pub struct ShellHandle<T> {
join_handle: JoinHandle<T>,
shell: Arc<Mutex<LocalShell>>,
}
impl<T> ShellHandle<T> {
pub fn signal(&self, signal: c_int) {
let mut lock = self.shell.lock().unwrap();
lock.signal(signal);
}
pub fn join(self) -> Result<T, Box<dyn Any + Send + 'static>> {
self.join_handle.join()
}
}
impl<T> Deref for ShellHandle<T> {
type Target = JoinHandle<T>;
fn deref(&self) -> &Self::Target {
&self.join_handle
}
}
pub fn spawn<F, T>(f: F) -> ShellHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
{
let arc = Arc::new(Mutex::new(LocalShell::new()));
let arc_clone = arc.clone();
let join_handle = thread::spawn(move || -> T {
LOCAL_SHELL_SCOPE.with(|shell| {
let mut shell = shell.borrow_mut();
if shell.is_some() {
panic!("Shell has already registered");
}
*shell = Some(LocalShellScope::new(&arc_clone));
});
f()
});
ShellHandle {
join_handle: join_handle,
shell: arc,
}
}
pub fn current_shell() -> Arc<Mutex<LocalShell>> {
LOCAL_SHELL_SCOPE.with(|shell| {
shell
.borrow_mut()
.get_or_insert(LocalShellScope::default())
.1
.clone()
})
}
thread_local! {
static LOCAL_SHELL_SCOPE: RefCell<Option<LocalShellScope>> =
RefCell::new(None);
}