1 use std::io;
2 use std::process::ExitStatus;
3 use std::sync::Mutex;
4 
5 /// An interface for waiting on a process to exit.
6 pub(crate) trait Wait {
7     /// Get the identifier for this process or diagnostics.
id(&self) -> u328     fn id(&self) -> u32;
9     /// Try waiting for a process to exit in a non-blocking manner.
try_wait(&mut self) -> io::Result<Option<ExitStatus>>10     fn try_wait(&mut self) -> io::Result<Option<ExitStatus>>;
11 }
12 
13 impl<T: Wait> Wait for &mut T {
id(&self) -> u3214     fn id(&self) -> u32 {
15         (**self).id()
16     }
17 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>18     fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
19         (**self).try_wait()
20     }
21 }
22 
23 /// An interface for queueing up an orphaned process so that it can be reaped.
24 pub(crate) trait OrphanQueue<T> {
25     /// Adds an orphan to the queue.
push_orphan(&self, orphan: T)26     fn push_orphan(&self, orphan: T);
27     /// Attempts to reap every process in the queue, ignoring any errors and
28     /// enqueueing any orphans which have not yet exited.
reap_orphans(&self)29     fn reap_orphans(&self);
30 }
31 
32 impl<T, O: OrphanQueue<T>> OrphanQueue<T> for &O {
push_orphan(&self, orphan: T)33     fn push_orphan(&self, orphan: T) {
34         (**self).push_orphan(orphan);
35     }
36 
reap_orphans(&self)37     fn reap_orphans(&self) {
38         (**self).reap_orphans()
39     }
40 }
41 
42 /// An implementation of `OrphanQueue`.
43 #[derive(Debug)]
44 pub(crate) struct OrphanQueueImpl<T> {
45     queue: Mutex<Vec<T>>,
46 }
47 
48 impl<T> OrphanQueueImpl<T> {
new() -> Self49     pub(crate) fn new() -> Self {
50         Self {
51             queue: Mutex::new(Vec::new()),
52         }
53     }
54 
55     #[cfg(test)]
len(&self) -> usize56     fn len(&self) -> usize {
57         self.queue.lock().unwrap().len()
58     }
59 }
60 
61 impl<T: Wait> OrphanQueue<T> for OrphanQueueImpl<T> {
push_orphan(&self, orphan: T)62     fn push_orphan(&self, orphan: T) {
63         self.queue.lock().unwrap().push(orphan)
64     }
65 
reap_orphans(&self)66     fn reap_orphans(&self) {
67         let mut queue = self.queue.lock().unwrap();
68         let queue = &mut *queue;
69 
70         let mut i = 0;
71         while i < queue.len() {
72             match queue[i].try_wait() {
73                 Ok(Some(_)) => {}
74                 Err(_) => {
75                     // TODO: bubble up error some how. Is this an internal bug?
76                     // Shoudl we panic? Is it OK for this to be silently
77                     // dropped?
78                 }
79                 // Still not done yet
80                 Ok(None) => {
81                     i += 1;
82                     continue;
83                 }
84             }
85 
86             queue.remove(i);
87         }
88     }
89 }
90 
91 #[cfg(all(test, not(loom)))]
92 mod test {
93     use super::Wait;
94     use super::{OrphanQueue, OrphanQueueImpl};
95     use std::cell::Cell;
96     use std::io;
97     use std::os::unix::process::ExitStatusExt;
98     use std::process::ExitStatus;
99     use std::rc::Rc;
100 
101     struct MockWait {
102         total_waits: Rc<Cell<usize>>,
103         num_wait_until_status: usize,
104         return_err: bool,
105     }
106 
107     impl MockWait {
new(num_wait_until_status: usize) -> Self108         fn new(num_wait_until_status: usize) -> Self {
109             Self {
110                 total_waits: Rc::new(Cell::new(0)),
111                 num_wait_until_status,
112                 return_err: false,
113             }
114         }
115 
with_err() -> Self116         fn with_err() -> Self {
117             Self {
118                 total_waits: Rc::new(Cell::new(0)),
119                 num_wait_until_status: 0,
120                 return_err: true,
121             }
122         }
123     }
124 
125     impl Wait for MockWait {
id(&self) -> u32126         fn id(&self) -> u32 {
127             42
128         }
129 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>130         fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
131             let waits = self.total_waits.get();
132 
133             let ret = if self.num_wait_until_status == waits {
134                 if self.return_err {
135                     Ok(Some(ExitStatus::from_raw(0)))
136                 } else {
137                     Err(io::Error::new(io::ErrorKind::Other, "mock err"))
138                 }
139             } else {
140                 Ok(None)
141             };
142 
143             self.total_waits.set(waits + 1);
144             ret
145         }
146     }
147 
148     #[test]
drain_attempts_a_single_reap_of_all_queued_orphans()149     fn drain_attempts_a_single_reap_of_all_queued_orphans() {
150         let first_orphan = MockWait::new(0);
151         let second_orphan = MockWait::new(1);
152         let third_orphan = MockWait::new(2);
153         let fourth_orphan = MockWait::with_err();
154 
155         let first_waits = first_orphan.total_waits.clone();
156         let second_waits = second_orphan.total_waits.clone();
157         let third_waits = third_orphan.total_waits.clone();
158         let fourth_waits = fourth_orphan.total_waits.clone();
159 
160         let orphanage = OrphanQueueImpl::new();
161         orphanage.push_orphan(first_orphan);
162         orphanage.push_orphan(third_orphan);
163         orphanage.push_orphan(second_orphan);
164         orphanage.push_orphan(fourth_orphan);
165 
166         assert_eq!(orphanage.len(), 4);
167 
168         orphanage.reap_orphans();
169         assert_eq!(orphanage.len(), 2);
170         assert_eq!(first_waits.get(), 1);
171         assert_eq!(second_waits.get(), 1);
172         assert_eq!(third_waits.get(), 1);
173         assert_eq!(fourth_waits.get(), 1);
174 
175         orphanage.reap_orphans();
176         assert_eq!(orphanage.len(), 1);
177         assert_eq!(first_waits.get(), 1);
178         assert_eq!(second_waits.get(), 2);
179         assert_eq!(third_waits.get(), 2);
180         assert_eq!(fourth_waits.get(), 1);
181 
182         orphanage.reap_orphans();
183         assert_eq!(orphanage.len(), 0);
184         assert_eq!(first_waits.get(), 1);
185         assert_eq!(second_waits.get(), 2);
186         assert_eq!(third_waits.get(), 3);
187         assert_eq!(fourth_waits.get(), 1);
188 
189         orphanage.reap_orphans(); // Safe to reap when empty
190     }
191 }
192