Skip to content

Commit

Permalink
docs: Specify behavior when registered in multiple pollers
Browse files Browse the repository at this point in the history
This adds documentation to add() describing what happens when a source
is registered in multiple pollers. A test is also added to ensure this
behavior.

Signed-off-by: John Nunley <[email protected]>
  • Loading branch information
notgull authored Aug 16, 2023
1 parent 2c279b8 commit c7cc91a
Show file tree
Hide file tree
Showing 2 changed files with 369 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,17 @@ impl Poller {
/// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in
/// the next event of the same kind.
///
/// It is possible to register interest in the same file descriptor or socket using multiple
/// separate [`Poller`] instances. When the event is delivered, one or more [`Poller`]s are
/// notified with that event. The exact number of [`Poller`]s notified depends on the
/// underlying platform. When registering multiple sources into one event, the user should
/// be careful to accommodate for events lost to other pollers.
///
/// One may also register one source into other, non-`polling` event loops, like GLib's
/// context. While the plumbing will vary from platform to platform, in general the [`Poller`]
/// will act as if the source was registered with another [`Poller`], with the same caveats
/// as above.
///
/// # Safety
///
/// The source must be [`delete()`]d from this `Poller` before it is dropped.
Expand Down
358 changes: 358 additions & 0 deletions tests/multiple_pollers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,358 @@
//! Test registering one source into multiple pollers.

use polling::{Event, Events, PollMode, Poller};

use std::io::{self, prelude::*};
use std::net::{TcpListener, TcpStream};
use std::time::Duration;

#[test]
fn level_triggered() {
let poller1 = Poller::new().unwrap();
let poller2 = Poller::new().unwrap();
let mut events = Events::new();

if !poller1.supports_level() || !poller2.supports_level() {
return;
}

// Register the source into both pollers.
let (mut reader, mut writer) = tcp_pair().unwrap();
unsafe {
poller1
.add_with_mode(&reader, Event::readable(1), PollMode::Level)
.unwrap();
poller2
.add_with_mode(&reader, Event::readable(2), PollMode::Level)
.unwrap();
}

// Neither poller should have any events.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());

// Write to the source.
writer.write_all(&[1]).unwrap();

// At least one poller should have an event.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
1
);
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(1)
);

events.clear();
// poller2 should have zero or one events.
match poller2.wait(&mut events, Some(Duration::from_secs(1))) {
Ok(1) => {
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(2)
);
}
Ok(0) => assert!(events.is_empty()),
_ => panic!("unexpected error"),
}

// Writing more data should cause the same event.
writer.write_all(&[1]).unwrap();
events.clear();
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
1
);
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(1)
);

// poller2 should have zero or one events.
events.clear();
match poller2.wait(&mut events, Some(Duration::from_secs(1))) {
Ok(1) => {
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(2)
);
}
Ok(0) => assert!(events.is_empty()),
_ => panic!("unexpected error"),
}

// Read from the source.
reader.read_exact(&mut [0; 2]).unwrap();

// Both pollers should not have any events.
events.clear();
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());

// Dereference the pollers.
poller1.delete(&reader).unwrap();
poller2.delete(&reader).unwrap();
}

#[test]
fn edge_triggered() {
let poller1 = Poller::new().unwrap();
let poller2 = Poller::new().unwrap();
let mut events = Events::new();

if !poller1.supports_edge() || !poller2.supports_edge() {
return;
}

// Register the source into both pollers.
let (mut reader, mut writer) = tcp_pair().unwrap();
unsafe {
poller1
.add_with_mode(&reader, Event::readable(1), PollMode::Edge)
.unwrap();
poller2
.add_with_mode(&reader, Event::readable(2), PollMode::Edge)
.unwrap();
}

// Neither poller should have any events.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());

// Write to the source.
writer.write_all(&[1]).unwrap();

// Both pollers should have an event.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
1
);
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(1)
);

events.clear();
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
1
);
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(2)
);

// Writing to the poller again should cause an event.
writer.write_all(&[1]).unwrap();

// Both pollers should have one event.
events.clear();
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
1
);
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(1)
);

events.clear();
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
1
);
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(2)
);

// Read from the source.
reader.read_exact(&mut [0; 2]).unwrap();

// Both pollers should not have any events.
events.clear();
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());

// Dereference the pollers.
poller1.delete(&reader).unwrap();
poller2.delete(&reader).unwrap();
}

#[test]
fn oneshot_triggered() {
let poller1 = Poller::new().unwrap();
let poller2 = Poller::new().unwrap();
let mut events = Events::new();

// Register the source into both pollers.
let (mut reader, mut writer) = tcp_pair().unwrap();
unsafe {
poller1
.add_with_mode(&reader, Event::readable(1), PollMode::Oneshot)
.unwrap();
poller2
.add_with_mode(&reader, Event::readable(2), PollMode::Oneshot)
.unwrap();
}

// Neither poller should have any events.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());

// Write to the source.
writer.write_all(&[1]).unwrap();

// Sources should have either one or no events.
match poller1.wait(&mut events, Some(Duration::from_secs(1))) {
Ok(1) => {
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(1)
);
}
Ok(0) => assert!(events.is_empty()),
_ => panic!("unexpected error"),
}
events.clear();

match poller2.wait(&mut events, Some(Duration::from_secs(1))) {
Ok(1) => {
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(2)
);
}
Ok(0) => assert!(events.is_empty()),
_ => panic!("unexpected error"),
}
events.clear();

// Writing more data should not cause an event.
writer.write_all(&[1]).unwrap();

// Sources should have no events.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());

// Read from the source.
reader.read_exact(&mut [0; 2]).unwrap();

// Sources should have no events.
assert_eq!(
poller1
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
assert_eq!(
poller2
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
0
);
assert!(events.is_empty());
}

fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let a = TcpStream::connect(listener.local_addr()?)?;
let (b, _) = listener.accept()?;
Ok((a, b))
}

0 comments on commit c7cc91a

Please sign in to comment.