hinix/
eventfd.rs

1// hinix/src/eventfd.rs
2//
3// This is part of the Rust 'hinix' crate
4//
5// Copyright (c) 2018-2020, Frank Pagliughi
6//
7// Licensed under the MIT license:
8//   <LICENSE or http://opensource.org/licenses/MIT>
9// This file may not be copied, modified, or distributed except according
10// to those terms.
11//
12
13//! Module to manage Linux event objects.
14//!
15//! See:
16//! <https://man7.org/linux/man-pages/man2/eventfd.2.html>
17//!
18
19use crate::{Error, Result};
20use nix::{self, sys::eventfd, unistd};
21use std::{
22    mem,
23    os::{
24        raw::c_uint,
25        unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
26    },
27    slice,
28};
29
30/// The size, in bytes, of the value held by an eventfd.
31/// This is the required size of a buffer that is used for reads and writes,
32/// as the value is a u64.
33const EFD_VAL_SIZE: usize = mem::size_of::<u64>();
34
35/// The flags used to create an EventFd
36pub type EfdFlags = eventfd::EfdFlags;
37
38/// An event object that can be used as a wait/notify mechanism between
39/// user-space applications, threads in an app, or between the kernel and
40/// user-space.
41///
42/// This is a simpler, more efficient signaling mechanism than a pipe, if
43/// event notification is all that is required by the application.
44///
45/// The event is seen as a normal file handle, and thus can be used in
46/// combination with other handles such as from sockets, pipes, etc,
47/// in a poll/epoll/select call to provide additional signaling
48/// capabilities.
49#[derive(Debug)]
50pub struct EventFd(OwnedFd);
51
52impl EventFd {
53    /// Create a new event object.
54    ///
55    /// This is the default configuration of the event object with no flags.
56    /// When read, the value is returned and the count is reset to zero.
57    ///
58    /// # Parameters
59    ///
60    /// `initval` The initial value held by the object
61    pub fn new(initval: u64) -> Result<EventFd> {
62        Self::with_flags(initval, EfdFlags::empty())
63    }
64
65    /// Create a new event object with the semaphore option.
66    ///
67    /// This is applies the EDF_SEMAPHORE flag. When read, the value
68    /// returned is 1, and the value is decremented by 1.
69    ///
70    /// # Parameters
71    ///
72    /// `initval` The initial value held by the object
73    pub fn new_semaphore(initval: u64) -> Result<EventFd> {
74        Self::with_flags(initval, EfdFlags::EFD_SEMAPHORE)
75    }
76
77    /// Create a new event object with the specified flags.
78    ///
79    /// # Parameters
80    /// `initval` The initial value held by the object
81    /// `flags` The flags used to create the object
82    ///
83    /// <http://man7.org/linux/man-pages/man2/eventfd.2.html>
84    pub fn with_flags(initval: u64, flags: EfdFlags) -> Result<EventFd> {
85        let fd = eventfd::eventfd(initval as c_uint, flags)?;
86        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
87        Ok(EventFd(fd))
88    }
89
90    /// Try to clone the event object by making a dup() of the OS file handle.
91    pub fn try_clone(&self) -> Result<Self> {
92        let fd = self
93            .0
94            .try_clone()
95            .map_err(|e| Error::try_from(e).unwrap_or_else(|_| Error::from_i32(0)))?;
96        Ok(EventFd(fd))
97    }
98
99    /// Reads the value of the event object.
100    pub fn read(&self) -> Result<u64> {
101        let mut buf: [u8; 8] = [0; EFD_VAL_SIZE];
102        if unistd::read(self.0.as_raw_fd(), &mut buf)? != EFD_VAL_SIZE {
103            return Err(Error::EIO);
104        }
105        let val: u64 = unsafe { *(&buf as *const u8 as *const u64) };
106        Ok(val)
107    }
108
109    /// Writes a value to the event object.
110    ///
111    /// # Parameters
112    /// `val` The value to _add_ to the one held by the object.
113    pub fn write(&self, val: u64) -> Result<()> {
114        let buf = unsafe { slice::from_raw_parts(&val as *const u64 as *const u8, EFD_VAL_SIZE) };
115        if unistd::write(self.0.as_raw_fd(), buf)? != EFD_VAL_SIZE {
116            return Err(Error::EIO);
117        }
118        Ok(())
119    }
120}
121
122impl AsFd for EventFd {
123    /// Gets the raw file handle for the event object.
124    fn as_fd(&self) -> BorrowedFd<'_> {
125        self.0.as_fd()
126    }
127}
128
129impl AsRawFd for EventFd {
130    /// Gets the raw file handle for the event object.
131    fn as_raw_fd(&self) -> RawFd {
132        self.0.as_raw_fd()
133    }
134}
135
136/////////////////////////////////////////////////////////////////////////////
137// Unit Tests
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_normal() {
145        let evtfd = EventFd::new(0).unwrap();
146        assert!(evtfd.as_raw_fd() >= 0);
147
148        // Writing a value should get us the same back on a read.
149        evtfd.write(1).unwrap();
150        let n = evtfd.read().unwrap();
151        assert_eq!(1, n);
152
153        // Try another value that's not '1'
154        evtfd.write(42).unwrap();
155        let n = evtfd.read().unwrap();
156        assert_eq!(42, n);
157
158        // Multiple writes should sunm the value
159        evtfd.write(5).unwrap();
160        evtfd.write(6).unwrap();
161        let n = evtfd.read().unwrap();
162        assert_eq!(11, n);
163    }
164
165    #[test]
166    fn test_non_blocking() {
167        let evtfd = EventFd::with_flags(0, EfdFlags::EFD_NONBLOCK).unwrap();
168        assert!(evtfd.as_raw_fd() >= 0);
169
170        // No value in object should get us an EAGAIN error.
171        match evtfd.read() {
172            Ok(_) => assert!(false),
173            Err(err) => assert_eq!(Error::EAGAIN, err),
174        }
175
176        // Writing a value should get us the same back on a read.
177        evtfd.write(6).unwrap();
178        let n = evtfd.read().unwrap();
179        assert_eq!(6, n);
180
181        // The read should have cleared the value, so another is an error.
182        match evtfd.read() {
183            Ok(_) => assert!(false),
184            Err(err) => assert_eq!(Error::EAGAIN, err),
185        }
186
187        // Try another value that's not '1'
188        evtfd.write(42).unwrap();
189        let n = evtfd.read().unwrap();
190        assert_eq!(42, n);
191    }
192
193    #[test]
194    fn test_semaphore() {
195        let evtfd = EventFd::new_semaphore(0).unwrap();
196        assert!(evtfd.as_raw_fd() >= 0);
197
198        // Signal then read back should get us a 1.
199        evtfd.write(1).unwrap();
200        let n = evtfd.read().unwrap();
201        assert_eq!(1, n);
202
203        // Try another value that's not 1.
204        evtfd.write(2).unwrap();
205
206        // Each read should return 1.
207        let n = evtfd.read().unwrap();
208        assert_eq!(1, n);
209
210        let n = evtfd.read().unwrap();
211        assert_eq!(1, n);
212    }
213
214    #[test]
215    fn test_semaphore_non_blocking() {
216        let evtfd =
217            EventFd::with_flags(0, EfdFlags::EFD_SEMAPHORE | EfdFlags::EFD_NONBLOCK).unwrap();
218        assert!(evtfd.as_raw_fd() >= 0);
219
220        // Try another value that's not '1'
221        evtfd.write(2).unwrap();
222
223        let n = evtfd.read().unwrap();
224        assert_eq!(1, n);
225
226        let n = evtfd.read().unwrap();
227        assert_eq!(1, n);
228
229        // The read should have cleared the value, so another is an error.
230        match evtfd.read() {
231            Ok(_) => assert!(false),
232            Err(err) => assert_eq!(Error::EAGAIN, err),
233        }
234    }
235}