1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use std::mem::MaybeUninit;
15 use std::process::Child as StdChild;
16 use std::sync::{Mutex, MutexGuard, Once};
17 
18 use crate::signal::unix::signal_return_watch;
19 use crate::signal::SignalKind;
20 use crate::sync::watch;
21 use crate::sync::watch::Receiver;
22 
23 pub(crate) struct GlobalZombieChild {
24     child_signal: Mutex<Option<watch::Receiver<()>>>,
25     vec: Mutex<Vec<StdChild>>,
26 }
27 
28 impl GlobalZombieChild {
get_instance() -> &'static GlobalZombieChild29     pub(crate) fn get_instance() -> &'static GlobalZombieChild {
30         static mut GLOBAL_ZOMBIE_CHILD: MaybeUninit<GlobalZombieChild> = MaybeUninit::uninit();
31         static ONCE: Once = Once::new();
32 
33         unsafe {
34             ONCE.call_once(|| {
35                 let global = GlobalZombieChild {
36                     child_signal: Mutex::new(None),
37                     vec: Mutex::new(Vec::new()),
38                 };
39                 GLOBAL_ZOMBIE_CHILD = MaybeUninit::new(global);
40             });
41             &*GLOBAL_ZOMBIE_CHILD.as_ptr()
42         }
43     }
44 
push(&self, child: StdChild)45     pub(crate) fn push(&self, child: StdChild) {
46         self.vec.lock().unwrap().push(child)
47     }
48 
release_zombie(&self)49     pub(crate) fn release_zombie(&self) {
50         if let Ok(mut guard) = self.child_signal.try_lock() {
51             match &mut *guard {
52                 Some(child_signal) => self.release_zombie_when_some_receiver(child_signal),
53                 None => self.release_zombile_when_no_receiver(&mut guard),
54             }
55         }
56     }
57 
release_zombie_when_some_receiver(&self, child_signal: &mut Receiver<()>)58     fn release_zombie_when_some_receiver(&self, child_signal: &mut Receiver<()>) {
59         if child_signal.try_notified().and_then(Result::ok).is_some() {
60             reap_vec(self.vec.lock().unwrap())
61         }
62     }
63 
release_zombile_when_no_receiver(&self, guard: &mut MutexGuard<Option<Receiver<()>>>)64     fn release_zombile_when_no_receiver(&self, guard: &mut MutexGuard<Option<Receiver<()>>>) {
65         let vec = self.vec.lock().unwrap();
66         if !vec.is_empty() {
67             if let Ok(recv) = signal_return_watch(SignalKind::child()) {
68                 **guard = Some(recv);
69                 reap_vec(vec);
70             }
71         }
72     }
73 }
74 
reap_vec(mut vec: MutexGuard<'_, Vec<StdChild>>)75 fn reap_vec(mut vec: MutexGuard<'_, Vec<StdChild>>) {
76     vec.retain_mut(|child| {
77         match child.try_wait() {
78             // `Ok(None)` means the child has not exited
79             Ok(None) => true,
80             _ => false,
81         }
82     });
83     drop(vec)
84 }
85