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