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}