blob: 0dc861be3abc7abd9c399aced553cace3f8df455 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::{
mem,
ops::Deref,
os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
ptr,
time::Duration,
};
use libc::{c_void, eventfd, read, write, POLLIN};
use serde::{Deserialize, Serialize};
use super::{
duration_to_timespec, errno_result, AsRawDescriptor, FromRawDescriptor, IntoRawDescriptor,
RawDescriptor, Result, SafeDescriptor,
};
use crate::generate_scoped_event;
/// A safe wrapper around a Linux eventfd (man 2 eventfd).
///
/// An eventfd is useful because it is sendable across processes and can be used for signaling in
/// and out of the KVM API. They can also be polled like any other file descriptor.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct EventFd {
event_handle: SafeDescriptor,
}
/// Wrapper around the return value of doing a read on an EventFd which distinguishes between
/// getting a valid count of the number of times the eventfd has been written to and timing out
/// waiting for the count to be non-zero.
#[derive(Debug, PartialEq, Eq)]
pub enum EventReadResult {
Count(u64),
Timeout,
}
impl EventFd {
/// Creates a new blocking EventFd with an initial value of 0.
pub fn new() -> Result<EventFd> {
// This is safe because eventfd merely allocated an eventfd for our process and we handle
// the error case.
let ret = unsafe { eventfd(0, 0) };
if ret < 0 {
return errno_result();
}
// This is safe because we checked ret for success and know the kernel gave us an fd that we
// own.
Ok(EventFd {
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
})
}
/// Adds `v` to the eventfd's count, blocking until this won't overflow the count.
pub fn write(&self, v: u64) -> Result<()> {
// This is safe because we made this fd and the pointer we pass can not overflow because we
// give the syscall's size parameter properly.
let ret = unsafe {
write(
self.as_raw_fd(),
&v as *const u64 as *const c_void,
mem::size_of::<u64>(),
)
};
if ret <= 0 {
return errno_result();
}
Ok(())
}
/// Blocks until the the eventfd's count is non-zero, then resets the count to zero.
pub fn read(&self) -> Result<u64> {
let mut buf: u64 = 0;
let ret = unsafe {
// This is safe because we made this fd and the pointer we pass can not overflow because
// we give the syscall's size parameter properly.
read(
self.as_raw_fd(),
&mut buf as *mut u64 as *mut c_void,
mem::size_of::<u64>(),
)
};
if ret <= 0 {
return errno_result();
}
Ok(buf)
}
/// Blocks for a maximum of `timeout` duration until the the eventfd's count is non-zero. If
/// a timeout does not occur then the count is returned as a EventReadResult::Count(count),
/// and the count is reset to 0. If a timeout does occur then this function will return
/// EventReadResult::Timeout.
pub fn read_timeout(&self, timeout: Duration) -> Result<EventReadResult> {
let mut pfd = libc::pollfd {
fd: self.as_raw_descriptor(),
events: POLLIN,
revents: 0,
};
let timeoutspec: libc::timespec = duration_to_timespec(timeout);
// Safe because this only modifies |pfd| and we check the return value
let ret = unsafe {
libc::ppoll(
&mut pfd as *mut libc::pollfd,
1,
&timeoutspec,
ptr::null_mut(),
)
};
if ret < 0 {
return errno_result();
}
// no return events (revents) means we got a timeout
if pfd.revents == 0 {
return Ok(EventReadResult::Timeout);
}
let mut buf = 0u64;
// This is safe because we made this fd and the pointer we pass can not overflow because
// we give the syscall's size parameter properly.
let ret = unsafe {
libc::read(
self.as_raw_descriptor(),
&mut buf as *mut _ as *mut c_void,
mem::size_of::<u64>(),
)
};
if ret < 0 {
return errno_result();
}
Ok(EventReadResult::Count(buf))
}
/// Clones this EventFd, internally creating a new file descriptor. The new EventFd will share
/// the same underlying count within the kernel.
pub fn try_clone(&self) -> Result<EventFd> {
self.event_handle
.try_clone()
.map(|event_handle| EventFd { event_handle })
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.event_handle.as_raw_fd()
}
}
impl AsRawDescriptor for EventFd {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.event_handle.as_raw_descriptor()
}
}
impl FromRawFd for EventFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
EventFd {
event_handle: SafeDescriptor::from_raw_descriptor(fd),
}
}
}
impl IntoRawFd for EventFd {
fn into_raw_fd(self) -> RawFd {
self.event_handle.into_raw_descriptor()
}
}
impl From<EventFd> for SafeDescriptor {
fn from(evt: EventFd) -> Self {
evt.event_handle
}
}
generate_scoped_event!(EventFd);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
EventFd::new().unwrap();
}
#[test]
fn read_write() {
let evt = EventFd::new().unwrap();
evt.write(55).unwrap();
assert_eq!(evt.read(), Ok(55));
}
#[test]
fn clone() {
let evt = EventFd::new().unwrap();
let evt_clone = evt.try_clone().unwrap();
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();
}
#[test]
fn timeout() {
let evt = EventFd::new().expect("failed to create eventfd");
assert_eq!(
evt.read_timeout(Duration::from_millis(1))
.expect("failed to read from eventfd with timeout"),
EventReadResult::Timeout
);
}
}