monitor/mon/space/
unmapper.rs

1use std::{panic::catch_unwind, sync::mpsc::Sender, thread::JoinHandle};
2
3use super::MapInfo;
4use crate::mon::get_monitor;
5
6/// Manages a background thread that unmaps mappings.
7pub struct Unmapper {
8    sender: Sender<UnmapCommand>,
9    _thread: JoinHandle<()>,
10}
11
12#[derive(Copy, Clone, Debug)]
13pub enum UnmapCommand {
14    SpaceUnmap(MapInfo),
15}
16
17impl Unmapper {
18    /// Make a new unmapper.
19    pub fn new() -> Self {
20        let (sender, receiver) = std::sync::mpsc::channel();
21        Self {
22            _thread: std::thread::Builder::new()
23                .name("unmapper".to_string())
24                .spawn(move || loop {
25                    match receiver.recv() {
26                        Ok(info) => {
27                            tracing::debug!("unmapper command {:?}", info);
28                            if catch_unwind(|| {
29                                let monitor = get_monitor();
30                                match info {
31                                    UnmapCommand::SpaceUnmap(info) => {
32                                        let mut space = monitor.space.lock().unwrap();
33                                        space.handle_drop(info);
34                                    }
35                                }
36                            })
37                            .is_err()
38                            {
39                                tracing::error!(
40                                    "clean_call panicked -- exiting map cleaner thread"
41                                );
42                                break;
43                            }
44                        }
45                        Err(_) => {
46                            // If receive fails, we can't recover, but this probably doesn't happen
47                            // since the sender won't get dropped since this
48                            // struct is used in the MapMan static.
49                            break;
50                        }
51                    }
52                })
53                .unwrap(),
54            sender,
55        }
56    }
57
58    /// Enqueue a mapping to be unmapped.
59    pub(super) fn background_unmap_info(&self, info: MapInfo) {
60        // If the receiver is down, this will fail, but that also shouldn't happen, unless the
61        // call to clean_call above panics. In any case, handle this gracefully.
62        if self.sender.send(UnmapCommand::SpaceUnmap(info)).is_err() {
63            tracing::warn!("failed to enqueue Unmap {:?} onto cleaner thread", info);
64        }
65    }
66}