vmcore/
interrupt.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Types to support delivering notifications to the guest.
5
6use crate::local_only::LocalOnly;
7use mesh::MeshPayload;
8use std::fmt::Debug;
9use std::sync::Arc;
10
11/// An object representing an interrupt-like signal to notify the guest of
12/// device activity.
13///
14/// This is generally an edge-triggered interrupt, but it could also be a synic
15/// event or similar notification.
16///
17/// The interrupt can be backed by a [`pal_event::Event`], a
18/// [`mesh::Cell<pal_event::Event>`], or a function. In the former two cases, the
19/// `Interrupt` can be sent across a mesh channel to remote processes.
20#[derive(Clone, Debug, MeshPayload)]
21pub struct Interrupt {
22    inner: InterruptInner,
23}
24
25impl Default for Interrupt {
26    fn default() -> Self {
27        Self::null()
28    }
29}
30
31impl Interrupt {
32    /// An interrupt that does nothing.
33    pub fn null() -> Self {
34        // Create a dummy event.
35        Self::from_event(pal_event::Event::new())
36    }
37
38    /// Creates an interrupt from an event.
39    ///
40    /// The event will be signaled when [`Self::deliver`] is called.
41    pub fn from_event(event: pal_event::Event) -> Self {
42        Self {
43            inner: InterruptInner::Event(Arc::new(event)),
44        }
45    }
46
47    /// Creates an interrupt from a mesh cell containing an event.
48    ///
49    /// The current event will be signaled when [`Self::deliver`] is called. The event
50    /// can be transparently changed without interaction from the caller.
51    pub fn from_cell(cell: mesh::Cell<pal_event::Event>) -> Self {
52        Self {
53            inner: InterruptInner::Cell(Arc::new(cell)),
54        }
55    }
56
57    /// Creates an interrupt from a function.
58    ///
59    /// The function will be called when [`Self::deliver`] is called. This type of
60    /// interrupt cannot be sent to a remote process.
61    pub fn from_fn<F>(f: F) -> Self
62    where
63        F: 'static + Send + Sync + Fn(),
64    {
65        Self {
66            inner: InterruptInner::Fn(LocalOnly(Arc::new(f))),
67        }
68    }
69
70    /// Delivers the interrupt.
71    pub fn deliver(&self) {
72        match &self.inner {
73            InterruptInner::Event(event) => event.signal(),
74            InterruptInner::Cell(cell) => cell.with(|event| event.signal()),
75            InterruptInner::Fn(LocalOnly(f)) => f(),
76        }
77    }
78
79    /// Gets a reference to the backing event, if there is one.
80    pub fn event(&self) -> Option<&pal_event::Event> {
81        match &self.inner {
82            InterruptInner::Event(event) => Some(event.as_ref()),
83            _ => None,
84        }
85    }
86}
87
88#[derive(Clone, MeshPayload)]
89enum InterruptInner {
90    Event(Arc<pal_event::Event>),
91    Cell(Arc<mesh::Cell<pal_event::Event>>),
92    Fn(LocalOnly<Arc<dyn Send + Sync + Fn()>>),
93}
94
95impl Debug for InterruptInner {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            InterruptInner::Event(_) => f.pad("Event"),
99            InterruptInner::Cell(_) => f.pad("Cell"),
100            InterruptInner::Fn(_) => f.pad("Fn"),
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::Interrupt;
108    use pal_async::async_test;
109
110    #[test]
111    fn test_interrupt_event() {
112        let event = pal_event::Event::new();
113        let interrupt = Interrupt::from_event(event.clone());
114        interrupt.deliver();
115        assert!(event.try_wait());
116    }
117
118    #[async_test]
119    async fn test_interrupt_cell() {
120        let mut event = pal_event::Event::new();
121        let (mut updater, cell) = mesh::cell(event.clone());
122        let interrupt = Interrupt::from_cell(cell);
123        interrupt.deliver();
124        assert!(event.try_wait());
125        event = pal_event::Event::new();
126        interrupt.deliver();
127        assert!(!event.try_wait());
128        updater.set(event.clone()).await;
129        interrupt.deliver();
130        assert!(event.try_wait());
131    }
132}