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    pub fn null() -> Self {
36        // Create a dummy event.
37        Self::from_event(pal_event::Event::new())
38    }
39
40    /// Creates an interrupt from an event.
41    ///
42    /// The event will be signaled when [`Self::deliver`] is called.
43    pub fn from_event(event: pal_event::Event) -> Self {
44        Self {
45            inner: InterruptInner::Event(Arc::new(event)),
46        }
47    }
48
49    /// Creates an interrupt from a mesh cell containing an event.
50    ///
51    /// The current event will be signaled when [`Self::deliver`] is called. The event
52    /// can be transparently changed without interaction from the caller.
53    pub fn from_cell(cell: mesh::Cell<pal_event::Event>) -> Self {
54        Self {
55            inner: InterruptInner::Cell(Arc::new(cell)),
56        }
57    }
58
59    /// Creates an interrupt from a function.
60    ///
61    /// The function will be called when [`Self::deliver`] is called. This type of
62    /// interrupt cannot be sent to a remote process.
63    pub fn from_fn<F>(f: F) -> Self
64    where
65        F: 'static + Send + Sync + Fn(),
66    {
67        Self {
68            inner: InterruptInner::Fn(LocalOnly(Arc::new(f))),
69        }
70    }
71
72    /// Delivers the interrupt.
73    pub fn deliver(&self) {
74        match &self.inner {
75            InterruptInner::Event(event) => event.signal(),
76            InterruptInner::Cell(cell) => cell.with(|event| event.signal()),
77            InterruptInner::Fn(LocalOnly(f)) => f(),
78        }
79    }
80
81    /// Gets a reference to the backing event, if there is one.
82    pub fn event(&self) -> Option<&pal_event::Event> {
83        match &self.inner {
84            InterruptInner::Event(event) => Some(event.as_ref()),
85            _ => None,
86        }
87    }
88}
89
90#[derive(Clone, MeshPayload)]
91enum InterruptInner {
92    Event(Arc<pal_event::Event>),
93    Cell(Arc<mesh::Cell<pal_event::Event>>),
94    Fn(LocalOnly<Arc<dyn Send + Sync + Fn()>>),
95}
96
97impl Debug for InterruptInner {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        match self {
100            InterruptInner::Event(_) => f.pad("Event"),
101            InterruptInner::Cell(_) => f.pad("Cell"),
102            InterruptInner::Fn(_) => f.pad("Fn"),
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::Interrupt;
110    use pal_async::async_test;
111
112    #[test]
113    fn test_interrupt_event() {
114        let event = pal_event::Event::new();
115        let interrupt = Interrupt::from_event(event.clone());
116        interrupt.deliver();
117        assert!(event.try_wait());
118    }
119
120    #[async_test]
121    async fn test_interrupt_cell() {
122        let mut event = pal_event::Event::new();
123        let (mut updater, cell) = mesh::cell(event.clone());
124        let interrupt = Interrupt::from_cell(cell);
125        interrupt.deliver();
126        assert!(event.try_wait());
127        event = pal_event::Event::new();
128        interrupt.deliver();
129        assert!(!event.try_wait());
130        updater.set(event.clone()).await;
131        interrupt.deliver();
132        assert!(event.try_wait());
133    }
134}