From 98419968d521b4e081dc79651f9602d46c6184ca Mon Sep 17 00:00:00 2001 From: Zach Reizner Date: Wed, 29 Jan 2020 15:34:09 -0800 Subject: sys_util: add ScopedEvent to trigger and EventFd on Drop TEST=cargo test -p sys_util BUG=None Change-Id: I1190947835549f872c6df7616e05a0a7be06d51e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2029926 Reviewed-by: Chirantan Ekbote Tested-by: kokoro Tested-by: Zach Reizner Commit-Queue: Zach Reizner --- sys_util/src/eventfd.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/sys_util/src/eventfd.rs b/sys_util/src/eventfd.rs index fc1ead2..57e5ffb 100644 --- a/sys_util/src/eventfd.rs +++ b/sys_util/src/eventfd.rs @@ -4,7 +4,9 @@ use std::fs::File; use std::mem; +use std::ops::Deref; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use std::ptr; use libc::{c_void, dup, eventfd, read, write}; @@ -106,6 +108,50 @@ impl IntoRawFd for EventFd { } } +/// An `EventFd` wrapper which triggers when it goes out of scope. +/// +/// If the underlying `EventFd` fails to trigger during drop, a panic is triggered instead. +pub struct ScopedEvent(EventFd); + +impl ScopedEvent { + /// Creates a new `ScopedEvent` which triggers when it goes out of scope. + pub fn new() -> Result { + Ok(EventFd::new()?.into()) + } +} + +impl From for ScopedEvent { + fn from(e: EventFd) -> Self { + Self(e) + } +} + +impl From for EventFd { + fn from(scoped_event: ScopedEvent) -> Self { + // Rust doesn't allow moving out of types with a Drop implementation, so we have to use + // something that copies instead of moves. This is safe because we prevent the drop of + // `scoped_event` using `mem::forget`, so the underlying `EventFd` will not experience a + // double-drop. + let evt = unsafe { ptr::read(&scoped_event.0) }; + mem::forget(scoped_event); + evt + } +} + +impl Deref for ScopedEvent { + type Target = EventFd; + + fn deref(&self) -> &EventFd { + &self.0 + } +} + +impl Drop for ScopedEvent { + fn drop(&mut self) { + self.write(1).expect("failed to trigger scoped event"); + } +} + #[cfg(test)] mod tests { use super::*; @@ -129,4 +175,19 @@ mod tests { evt.write(923).unwrap(); assert_eq!(evt_clone.read(), Ok(923)); } + + #[test] + fn scoped_event() { + let scoped_evt = ScopedEvent::new().unwrap(); + let evt_clone: EventFd = scoped_evt.try_clone().unwrap(); + drop(scoped_evt); + assert_eq!(evt_clone.read(), Ok(1)); + } + + #[test] + fn eventfd_from_scoped_event() { + let scoped_evt = ScopedEvent::new().unwrap(); + let evt: EventFd = scoped_evt.into(); + evt.write(1).unwrap(); + } } -- cgit 1.4.1