1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Types to support delivering notifications to the guest.

use crate::local_only::LocalOnly;
use mesh::MeshPayload;
use std::fmt::Debug;
use std::sync::Arc;

/// An object representing an interrupt-like signal to notify the guest of
/// device activity.
///
/// This is generally an edge-triggered interrupt, but it could also be a synic
/// event or similar notification.
///
/// The interrupt can be backed by a [`pal_event::Event`], a
/// [`mesh::Cell<pal_event::Event>`], or a function. In the former two cases, the
/// `Interrupt` can be sent across a mesh channel to remote processes.
#[derive(Clone, Debug, MeshPayload)]
pub struct Interrupt {
    inner: InterruptInner,
}

impl Default for Interrupt {
    fn default() -> Self {
        Self::null()
    }
}

impl Interrupt {
    /// An interrupt that does nothing.
    pub fn null() -> Self {
        // Create a dummy event.
        Self::from_event(pal_event::Event::new())
    }

    /// Creates an interrupt from an event.
    ///
    /// The event will be signaled when [`Self::deliver`] is called.
    pub fn from_event(event: pal_event::Event) -> Self {
        Self {
            inner: InterruptInner::Event(Arc::new(event)),
        }
    }

    /// Creates an interrupt from a mesh cell containing an event.
    ///
    /// The current event will be signaled when [`Self::deliver`] is called. The event
    /// can be transparently changed without interaction from the caller.
    pub fn from_cell(cell: mesh::Cell<pal_event::Event>) -> Self {
        Self {
            inner: InterruptInner::Cell(Arc::new(cell)),
        }
    }

    /// Creates an interrupt from a function.
    ///
    /// The function will be called when [`Self::deliver`] is called. This type of
    /// interrupt cannot be sent to a remote process.
    pub fn from_fn<F>(f: F) -> Self
    where
        F: 'static + Send + Sync + Fn(),
    {
        Self {
            inner: InterruptInner::Fn(LocalOnly(Arc::new(f))),
        }
    }

    /// Delivers the interrupt.
    pub fn deliver(&self) {
        match &self.inner {
            InterruptInner::Event(event) => event.signal(),
            InterruptInner::Cell(cell) => cell.with(|event| event.signal()),
            InterruptInner::Fn(LocalOnly(f)) => f(),
        }
    }

    /// Gets a reference to the backing event, if there is one.
    pub fn event(&self) -> Option<&pal_event::Event> {
        match &self.inner {
            InterruptInner::Event(event) => Some(event.as_ref()),
            _ => None,
        }
    }
}

#[derive(Clone, MeshPayload)]
enum InterruptInner {
    Event(Arc<pal_event::Event>),
    Cell(Arc<mesh::Cell<pal_event::Event>>),
    Fn(LocalOnly<Arc<dyn Send + Sync + Fn()>>),
}

impl Debug for InterruptInner {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InterruptInner::Event(_) => f.pad("Event"),
            InterruptInner::Cell(_) => f.pad("Cell"),
            InterruptInner::Fn(_) => f.pad("Fn"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Interrupt;
    use pal_async::async_test;

    #[test]
    fn test_interrupt_event() {
        let event = pal_event::Event::new();
        let interrupt = Interrupt::from_event(event.clone());
        interrupt.deliver();
        assert!(event.try_wait());
    }

    #[async_test]
    async fn test_interrupt_cell() {
        let mut event = pal_event::Event::new();
        let (mut updater, cell) = mesh::cell(event.clone());
        let interrupt = Interrupt::from_cell(cell);
        interrupt.deliver();
        assert!(event.try_wait());
        event = pal_event::Event::new();
        interrupt.deliver();
        assert!(!event.try_wait());
        updater.set(event.clone()).await;
        interrupt.deliver();
        assert!(event.try_wait());
    }
}