Skip to content

Commit

Permalink
Support for the ESP-IDF framework
Browse files Browse the repository at this point in the history
  • Loading branch information
imarkov authored and ivmarkov committed Aug 4, 2023
1 parent 6eb7679 commit 73f43c9
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl Poller {
None => TS_ZERO,
Some(t) => {
let mut ts = TS_ZERO;
ts.tv_sec = t.as_secs() as libc::time_t;
ts.tv_sec = t.as_secs() as _;
ts.tv_nsec = (t.subsec_nanos() as libc::c_long).into();
ts
}
Expand Down
95 changes: 77 additions & 18 deletions src/poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::collections::HashMap;
use std::convert::TryInto;
use std::io;
use std::mem;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Condvar, Mutex};
use std::time::{Duration, Instant};
Expand All @@ -26,13 +27,17 @@ pub struct Poller {

/// The file descriptor of the read half of the notify pipe. This is also stored as the first
/// file descriptor in `fds.poll_fds`.
///
/// On ESP-IDF this file descriptor represents an eventfd handle rather than the read half of a pipe.
notify_read: OwnedFd,
/// The file descriptor of the write half of the notify pipe.
///
/// Data is written to this to wake up the current instance of `wait`, which can occur when the
/// user notifies it (in which case `notified` would have been set) or when an operation needs
/// to occur (in which case `waiting_operations` would have been incremented).
notify_write: OwnedFd,
///
/// On ESP-IDF this file descriptor is set to None.
notify_write: Option<OwnedFd>,

/// The number of operations (`add`, `modify` or `delete`) that are currently waiting on the
/// mutex to become free. When this is nonzero, `wait` must be suspended until it reaches zero
Expand Down Expand Up @@ -74,19 +79,45 @@ struct FdData {
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
// Create the notification pipe.
let (notify_read, notify_write) = pipe_with(PipeFlags::CLOEXEC).or_else(|_| {
let (notify_read, notify_write) = pipe()?;
fcntl_setfd(&notify_read, fcntl_getfd(&notify_read)? | FdFlags::CLOEXEC)?;
fcntl_setfd(
&notify_write,
fcntl_getfd(&notify_write)? | FdFlags::CLOEXEC,
)?;
io::Result::Ok((notify_read, notify_write))
})?;

// Put the reading side into non-blocking mode.
fcntl_setfl(&notify_read, fcntl_getfl(&notify_read)? | OFlags::NONBLOCK)?;
#[cfg(not(target_os = "espidf"))]
let (notify_read, notify_write) = {
// Create the notification pipe.

use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags};
use rustix::pipe::{pipe, pipe_with, PipeFlags};

let (notify_read, notify_write) = pipe_with(PipeFlags::CLOEXEC).or_else(|_| {
let (notify_read, notify_write) = pipe()?;
fcntl_setfd(&notify_read, fcntl_getfd(&notify_read)? | FdFlags::CLOEXEC)?;
fcntl_setfd(
&notify_write,
fcntl_getfd(&notify_write)? | FdFlags::CLOEXEC,
)?;
io::Result::Ok((notify_read, notify_write))
})?;

// Put the reading side into non-blocking mode.
fcntl_setfl(&notify_read, fcntl_getfl(&notify_read)? | OFlags::NONBLOCK)?;

(notify_read, Some(notify_write))
};

#[cfg(target_os = "espidf")]
let (notify_read, notify_write) = {
// Note that the eventfd() implementation in ESP-IDF deviates from the specification in the following ways:
// 1) The file descriptor is always in a non-blocking mode, as if EFD_NONBLOCK was passed as a flag;
// passing EFD_NONBLOCK or calling fcntl(.., F_GETFL/F_SETFL) on the eventfd() file descriptor is not supported
// 2) It always returns the counter value, even if it is 0. This is contrary to the specification which mandates
// that it should instead fail with EAGAIN
//
// (1) is not a problem for us, as we want the eventfd() file descriptor to be in a non-blocking mode anyway
// (2) is also not a problem, as long as we don't try to read the counter value in an endless loop when we detect being notified

use rustix::event::{eventfd, EventfdFlags};

(eventfd(0, EventfdFlags::empty())?, None)
};

tracing::trace!(?notify_read, ?notify_write, "new");

Expand Down Expand Up @@ -119,7 +150,13 @@ impl Poller {

/// Adds a new file descriptor.
pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> {
if fd == self.notify_read.as_raw_fd() || fd == self.notify_write.as_raw_fd() {
if fd == self.notify_read.as_raw_fd()
|| self
.notify_write
.as_ref()
.map(|notify_write| fd == notify_write.as_raw_fd())
.unwrap_or(false)
{
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}

Expand Down Expand Up @@ -263,7 +300,15 @@ impl Poller {

// Read all notifications.
if notified {
while read(&self.notify_read, &mut [0; 64]).is_ok() {}
if self.notify_write.is_some() {
// When using the `pipe` syscall, we have to read all accumulated notifications in the pipe.
while read(&self.notify_read, &mut [0; 64]).is_ok() {}
} else {
// When using the `eventfd` syscall, it is OK to read just once, so as to clear the counter.
// In fact, reading in a loop will result in an endless loop on the ESP-IDF
// which is not following the specification strictly.
let _ = self.pop_notification();
}
}

// If the only event that occurred during polling was notification and it wasn't to
Expand Down Expand Up @@ -349,13 +394,27 @@ impl Poller {

/// Wake the current thread that is calling `wait`.
fn notify_inner(&self) -> io::Result<()> {
write(&self.notify_write, &[0; 1])?;
if let Some(notify_write) = self.notify_write.as_ref() {
// `pipe`
write(notify_write, &[0; 1])?;
} else {
// `eventfd`
write(&self.notify_read, &1u64.to_ne_bytes())?;
}

Ok(())
}

/// Remove a notification created by `notify_inner`.
fn pop_notification(&self) -> io::Result<()> {
read(&self.notify_read, &mut [0; 1])?;
if self.notify_write.is_some() {
// `pipe`
read(&self.notify_read, &mut [0; 1])?;
} else {
// `eventfd`
read(&self.notify_read, &mut [0; mem::size_of::<u64>()])?;
}

Ok(())
}
}
Expand Down

0 comments on commit 73f43c9

Please sign in to comment.