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