rust_shell/
local_shell.rs

1// Copyright 2017 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use shell_child::ShellChildArc;
16use libc::c_int;
17use process_manager::PROCESS_MANAGER;
18use std::any::Any;
19use std::cell::RefCell;
20use std::ops::Deref;
21use std::sync::Arc;
22use std::sync::Mutex;
23use std::thread::JoinHandle;
24use std::thread::ThreadId;
25use std::thread;
26
27/// Thread local shell.
28pub struct LocalShell {
29    processes: Vec<ShellChildArc>,
30    signaled: bool
31}
32
33impl LocalShell {
34    fn new() -> LocalShell {
35        LocalShell {
36            processes: Vec::new(),
37            signaled: false
38        }
39    }
40
41    pub fn add_process(&mut self, process: &ShellChildArc) {
42        self.processes.push(process.clone());
43    }
44
45    pub fn remove_process(&mut self, process: &ShellChildArc) {
46        self.processes.retain(|p| !Arc::ptr_eq(p, process));
47    }
48
49    pub fn signal(&mut self, signal: c_int) {
50        self.signaled = true;
51        for process in &self.processes {
52            let lock = process.read().unwrap();
53            if let Some(child) = lock.as_ref() {
54                if let Err(error) = child.signal(signal) {
55                    error!("Failed to send a signal {:?}", error);
56                }
57            }
58        }
59    }
60
61    pub fn wait(&mut self) {
62        for process in &self.processes {
63            let mut lock = process.write().unwrap();
64            if let Some(child) = lock.take() {
65                if let Err(error) = child.wait() {
66                    error!("Failed to wait process {:?}", error);
67                }
68            }
69        }
70    }
71
72    pub fn signaled(&self) -> bool {
73        self.signaled
74    }
75}
76
77struct LocalShellScope(ThreadId, Arc<Mutex<LocalShell>>);
78
79impl LocalShellScope {
80    fn new(arc: &Arc<Mutex<LocalShell>>) -> LocalShellScope {
81        let mut lock = PROCESS_MANAGER.lock().unwrap();
82        let id = thread::current().id();
83        lock.add_local_shell(&id, arc);
84
85        LocalShellScope(id, arc.clone())
86    }
87}
88
89impl Default for LocalShellScope {
90    fn default() -> LocalShellScope {
91        LocalShellScope::new(&Arc::new(Mutex::new(LocalShell::new())))
92    }
93}
94
95impl Drop for LocalShellScope {
96    fn drop(&mut self) {
97        let mut lock = PROCESS_MANAGER.lock().unwrap();
98        lock.remove_local_shell(&self.0);
99    }
100}
101
102pub struct ShellHandle<T> {
103    join_handle: JoinHandle<T>,
104    shell: Arc<Mutex<LocalShell>>
105}
106
107impl <T> ShellHandle<T> {
108    pub fn signal(&self, signal: c_int) {
109        let mut lock = self.shell.lock().unwrap();
110        lock.signal(signal);
111    }
112
113    pub fn join(self) -> Result<T, Box<Any + Send + 'static>> {
114        self.join_handle.join()
115    }
116}
117
118impl <T> Deref for ShellHandle<T> {
119    type Target = JoinHandle<T>;
120    fn deref(&self) -> &Self::Target {
121        &self.join_handle
122    }
123}
124
125pub fn spawn<F, T>(f: F) -> ShellHandle<T> where
126        F: FnOnce() -> T, F: Send + 'static, T: Send + 'static {
127    let arc = Arc::new(Mutex::new(LocalShell::new()));
128    let arc_clone = arc.clone();
129    let join_handle = thread::spawn(move || -> T {
130        LOCAL_SHELL_SCOPE.with(|shell| {
131            let mut shell = shell.borrow_mut();
132            if shell.is_some() {
133                panic!("Shell has already registered");
134            }
135            *shell = Some(LocalShellScope::new(&arc_clone));
136        });
137        f()
138    });
139    ShellHandle {
140        join_handle: join_handle,
141        shell: arc
142    }
143}
144
145pub fn current_shell() -> Arc<Mutex<LocalShell>> {
146    LOCAL_SHELL_SCOPE.with(|shell| {
147        shell.borrow_mut()
148            .get_or_insert(LocalShellScope::default()).1.clone()
149    })
150}
151
152thread_local! {
153    static LOCAL_SHELL_SCOPE: RefCell<Option<LocalShellScope>> =
154        RefCell::new(None);
155}
156