Skip to content

Commit

Permalink
create a independent eventfd crate without depending other modules
Browse files Browse the repository at this point in the history
  • Loading branch information
binary-bruce committed Apr 27, 2024
1 parent 1d05532 commit 46fd761
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ members = [
"apps/task/sleep",
"apps/task/yield",
"apps/task/priority",
"apps/task/tls",
"apps/task/tls", "crates/eventfd",
]

[profile.release]
Expand Down
9 changes: 9 additions & 0 deletions crates/eventfd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "eventfd"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bitflags = "2.2"
176 changes: 176 additions & 0 deletions crates/eventfd/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use bitflags::bitflags;

bitflags! {
/// https://sites.uclouvain.be/SystInfo/usr/include/sys/eventfd.h.html
#[derive(Clone, Copy, Debug)]
pub struct EventFdFlag: u32 {
const EFD_SEMAPHORE = 0x1;
const EFD_NONBLOCK = 0x800;
const EFD_CLOEXEC = 0x80000;
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum EventFdWriteResult {
OK,
/// an attempt is made to write the value 0xffffffffffffffff (f64::MAX)
BadInput,
NotReady,
}

/// https://man7.org/linux/man-pages/man2/eventfd2.2.html
pub struct EventFd {
value: u64,
flags: u32,
}

impl EventFd {
pub fn new(initval: u64, flags: u32) -> EventFd {
EventFd {
value: initval,
flags,
}
}

pub fn read(&mut self) -> Option<u64> {
// If EFD_SEMAPHORE was not specified and the eventfd counter has a nonzero value, then a read returns 8 bytes containing that value,
// and the counter's value is reset to zero.
if !self.is_semaphore_set() && self.value != 0 {
let result = self.value;
self.value = 0;
return Some(result);
}

// If EFD_SEMAPHORE was specified and the eventfd counter has a nonzero value, then a read returns 8 bytes containing the value 1,
// and the counter's value is decremented by 1.
if self.is_semaphore_set() && self.value != 0 {
self.value -= 1;
return Some(1u64);
}

// If the eventfd counter is zero at the time of the call to read,
// then the call either blocks until the counter becomes nonzero (at which time, the read proceeds as described above)
// or fails with the error EAGAIN if the file descriptor has been made nonblocking.
None
}

pub fn write(&mut self, val: u64) -> EventFdWriteResult {
if val == u64::MAX {
return EventFdWriteResult::BadInput;
}

match self.value.checked_add(val + 1) {
// no overflow
Some(_) => {
self.value += val;
EventFdWriteResult::OK
}
// overflow
None => EventFdWriteResult::NotReady,
}
}

pub fn is_flag_set(&self, flag: EventFdFlag) -> bool {
self.flags & flag.bits() != 0
}

fn is_semaphore_set(&self) -> bool {
self.is_flag_set(EventFdFlag::EFD_SEMAPHORE)
}
}

/// create eventfd with flags of zero default value
pub fn create_eventfd(value: u64) -> EventFd {
value.into()
}

pub fn create_eventfd_with_flags(value: u64, flags: u32) -> EventFd {
(value, flags).into()
}

impl From<u64> for EventFd {
fn from(value: u64) -> Self {
EventFd {
value: value,
flags: 0,
}
}
}

impl From<(u64, u32)> for EventFd {
fn from((value, flags): (u64, u32)) -> Self {
EventFd { value, flags }
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn all_flags_should_not_set() {
let event_fd: EventFd = 42.into();
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_SEMAPHORE));
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_NONBLOCK));
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_CLOEXEC));
}

#[test]
fn only_efd_semaphore_is_set() {
let event_fd: EventFd = (42, 0x1).into();
assert!(event_fd.is_flag_set(EventFdFlag::EFD_SEMAPHORE));
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_NONBLOCK));
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_CLOEXEC));
}

#[test]
fn only_efd_nonblock_is_set() {
let event_fd: EventFd = (42, 0x800).into();
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_SEMAPHORE));
assert!(event_fd.is_flag_set(EventFdFlag::EFD_NONBLOCK));
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_CLOEXEC));
}

#[test]
fn only_efd_cloexec_is_set() {
let event_fd: EventFd = (42, 0x80000).into();
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_SEMAPHORE));
assert!(!event_fd.is_flag_set(EventFdFlag::EFD_NONBLOCK));
assert!(event_fd.is_flag_set(EventFdFlag::EFD_CLOEXEC));
}

#[test]
fn read_with_efd_semaphore_not_set() {
let mut event_fd: EventFd = 42.into();
assert_eq!(Some(42), event_fd.read());
assert_eq!(None, event_fd.read());
}

#[test]
fn read_with_efd_semaphore_set() {
let mut event_fd: EventFd = create_eventfd_with_flags(2, EventFdFlag::EFD_SEMAPHORE.bits());
assert_eq!(Some(1), event_fd.read());
assert_eq!(Some(1), event_fd.read());
assert_eq!(None, event_fd.read());
}

#[test]
fn write_max_value() {
let mut event_fd: EventFd = 42.into();
assert_eq!(EventFdWriteResult::BadInput, event_fd.write(u64::MAX))
}

#[test]
fn test_overflow_write() {
let mut event_fd: EventFd = (u64::MAX - 1).into();
assert_eq!(EventFdWriteResult::NotReady, event_fd.write(2))
}

#[test]
fn test_non_overflow_write() {
let mut event_fd: EventFd = (u64::MAX - 2).into();
assert_eq!(EventFdWriteResult::OK, event_fd.write(1));

assert_eq!(Some(u64::MAX - 1), event_fd.read());
}
}

0 comments on commit 46fd761

Please sign in to comment.