vmcore/
notify.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Guest-to-host notification infrastructure, abstracting over [platform
5//! events](pal_event::Event), and [task events](SlimEvent).
6
7#![forbid(unsafe_code)]
8
9use crate::interrupt::Interrupt;
10use crate::local_only::LocalOnly;
11use crate::slim_event::SlimEvent;
12use mesh::MeshPayload;
13use pal_async::driver::Driver;
14use pal_async::wait::PolledWait;
15use pal_event::Event;
16use parking_lot::Mutex;
17use std::future::Future;
18use std::future::poll_fn;
19use std::sync::Arc;
20use std::task::Context;
21use std::task::Poll;
22
23/// An object that can be signaled when the guest needs attention.
24#[derive(Debug, Clone, MeshPayload)]
25pub struct Notify(Inner);
26
27impl Notify {
28    /// Creates an object from an event.
29    pub fn from_event(event: Event) -> Self {
30        Self(Inner::Event(event))
31    }
32
33    /// Creates an object from a slim event.
34    pub fn from_slim_event(event: Arc<SlimEvent>) -> Self {
35        Self(Inner::SlimEvent(LocalOnly(event)))
36    }
37
38    /// Returns a pollable version, using `driver` to poll an underlying event
39    /// if there is one.
40    pub fn pollable(self, driver: &(impl Driver + ?Sized)) -> std::io::Result<PolledNotify> {
41        Ok(PolledNotify(match self.0 {
42            Inner::Event(e) => PolledInner::Event(Mutex::new(PolledWait::new(driver, e)?)),
43            Inner::SlimEvent(LocalOnly(e)) => PolledInner::SlimEvent(e),
44        }))
45    }
46
47    /// Gets the underlying OS event, if there is one.
48    pub fn event(&self) -> Option<&Event> {
49        match &self.0 {
50            Inner::Event(e) => Some(e),
51            Inner::SlimEvent(_) => None,
52        }
53    }
54
55    /// Gets an interrupt object that can be used to signal the underlying
56    /// event.
57    pub fn interrupt(self) -> Interrupt {
58        match self.0 {
59            Inner::Event(e) => Interrupt::from_event(e),
60            Inner::SlimEvent(LocalOnly(e)) => Interrupt::from_fn(move || e.signal()),
61        }
62    }
63}
64
65/// A [`Notify`] object that is ready to be polled.
66pub struct PolledNotify(PolledInner);
67
68#[derive(Debug, MeshPayload)]
69enum Inner {
70    Event(Event),
71    SlimEvent(LocalOnly<Arc<SlimEvent>>),
72}
73
74impl Clone for Inner {
75    fn clone(&self) -> Self {
76        match self {
77            Self::Event(event) => Self::Event(event.clone()),
78            Self::SlimEvent(event) => Self::SlimEvent(event.clone()),
79        }
80    }
81}
82
83enum PolledInner {
84    Event(Mutex<PolledWait<Event>>),
85    SlimEvent(Arc<SlimEvent>),
86}
87
88impl PolledNotify {
89    /// Polls for the notify object to be signaled.
90    pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<()> {
91        match &self.0 {
92            PolledInner::Event(e) => e
93                .lock()
94                .poll_wait(cx)
95                .map(|r| r.expect("waits on Event cannot fail")),
96            PolledInner::SlimEvent(e) => e.poll_wait(cx),
97        }
98    }
99
100    /// Waits for the notify object to be signaled.
101    pub fn wait(&mut self) -> impl '_ + Unpin + Future<Output = ()> {
102        poll_fn(move |cx| match &mut self.0 {
103            PolledInner::Event(e) => e
104                .get_mut()
105                .poll_wait(cx)
106                .map(|r| r.expect("waits on Event cannot fail")),
107            PolledInner::SlimEvent(e) => e.poll_wait(cx),
108        })
109    }
110
111    /// Returns the inner notify object.
112    pub fn into_inner(self) -> Notify {
113        Notify(match self.0 {
114            PolledInner::Event(e) => Inner::Event(e.into_inner().into_inner()),
115            PolledInner::SlimEvent(e) => Inner::SlimEvent(LocalOnly(e)),
116        })
117    }
118}