use std::{
collections::{BTreeMap, HashMap, HashSet},
fmt,
future::Future,
io, mem,
pin::Pin,
sync::{Arc, atomic::Ordering},
task::{Context, Poll, Waker, ready},
time::{Duration, Instant},
};
use crate::{
Interest, Local, Mode, coop,
log::error,
mode::sealed::Lock,
poll::{self, Directions, Poller, RawSource},
runtime::{Core, Remote},
};
pub(crate) struct Reactor<M>
where
M: Mode,
{
sources: M::Lock<Sources<M>>,
timers: M::Lock<Timers>,
busy_poll: M::Lock<BusyPoll>,
remote: Arc<Remote>,
}
impl<M> Reactor<M>
where
M: Mode,
{
pub(crate) fn new(remote: Arc<Remote>) -> Self {
Self {
sources: Lock::new(Sources {
states: HashMap::new(),
descriptors: HashSet::new(),
next_key: 0,
next_waiter: 0,
stale: Vec::new(),
waiting: false,
retired: Vec::new(),
}),
timers: Lock::new(Timers::default()),
busy_poll: Lock::new(BusyPoll::default()),
remote,
}
}
pub(crate) fn wait(&self, at_most: Option<Duration>) -> io::Result<()> {
{
let mut sources = self.sources.lock();
sources.reconcile(&self.remote.poller)?;
sources.waiting = !Poller::LIVE;
}
let timeout = self.timeout(at_most);
let ready = match self.wait_on_poller(timeout) {
Ok(ready) => ready,
Err(e) => {
self.end_wait();
return Err(e);
}
};
self.remote.wake_pending.store(false, Ordering::Release);
let (woken, retired) = {
let mut sources = self.sources.lock();
sources.waiting = false;
let Sources {
states,
stale,
retired,
..
} = &mut *sources;
let mut woken = Vec::new();
for event in &ready {
let Some(state) = states.get_mut(&event.key) else {
continue;
};
if event.directions.readable {
state.wakers.readable.take_into(&mut woken);
}
if event.directions.writable {
state.wakers.writable.take_into(&mut woken);
}
state.armed.readable &= !event.dropped.readable;
state.armed.writable &= !event.dropped.writable;
state.mark_stale(event.key, stale);
}
(woken, mem::take(retired))
};
drop(retired);
for waker in woken {
waker.wake();
}
let due = {
let mut timers = self.timers.lock();
let later = timers.pending.split_off(&(Instant::now(), u64::MAX));
mem::replace(&mut timers.pending, later)
};
for waker in due.into_values() {
waker.wake();
}
Ok(())
}
pub(crate) fn set_busy_poll(&self, limit: Duration) {
self.busy_poll.lock().set_limit(limit);
}
fn timeout(&self, at_most: Option<Duration>) -> Option<Duration> {
let deadline = self
.timers
.lock()
.pending
.keys()
.next()
.map(|(deadline, _)| *deadline);
let until_deadline = deadline.map(|at| at.saturating_duration_since(Instant::now()));
let timeout = match (at_most, until_deadline) {
(Some(at_most), Some(until_deadline)) => Some(at_most.min(until_deadline)),
(bound, None) | (None, bound) => bound,
};
timeout.map(|timeout| timeout.min(poll::MAX_TIMEOUT))
}
fn wait_on_poller(&self, timeout: Option<Duration>) -> io::Result<Vec<poll::Ready>> {
let poller = &self.remote.poller;
if timeout == Some(Duration::ZERO) {
return poller.wait(timeout);
}
let (on, spin) = {
let busy_poll = self.busy_poll.lock();
(busy_poll.is_on(), busy_poll.spin_for(timeout))
};
if !on {
return poller.wait(timeout);
}
let started = Instant::now();
let spun = if spin.is_zero() {
None
} else {
self.spin(started, spin)?
};
let ready = match spun {
Some(ready) => ready,
None => {
poller.wait(timeout.map(|timeout| timeout.saturating_sub(started.elapsed())))?
}
};
self.busy_poll.lock().record(started.elapsed());
Ok(ready)
}
fn spin(&self, started: Instant, spin: Duration) -> io::Result<Option<Vec<poll::Ready>>> {
loop {
let ready = self.remote.poller.wait(Some(Duration::ZERO))?;
if !ready.is_empty() || self.remote.wake_pending.load(Ordering::Acquire) {
return Ok(Some(ready));
}
if started.elapsed() >= spin {
return Ok(None);
}
}
}
fn end_wait(&self) {
let retired = {
let mut sources = self.sources.lock();
sources.waiting = false;
mem::take(&mut sources.retired)
};
drop(retired);
}
pub(crate) fn wake_everything(&self) {
let mut woken = Vec::new();
{
let mut sources = self.sources.lock();
let Sources { states, stale, .. } = &mut *sources;
for (&key, state) in states.iter_mut() {
state.wakers.readable.take_into(&mut woken);
state.wakers.writable.take_into(&mut woken);
state.mark_stale(key, stale);
}
}
woken.extend(mem::take(&mut self.timers.lock().pending).into_values());
for waker in woken {
waker.wake();
}
}
#[cfg(any(test, feature = "helper"))]
pub(crate) fn is_idle(&self) -> bool {
let no_sources = self.sources.lock().states.is_empty();
no_sources && self.timers.lock().pending.is_empty()
}
#[cfg(test)]
pub(crate) fn would_spin(&self) -> bool {
!self.busy_poll.lock().spin_for(None).is_zero()
}
}
pub(crate) fn register<M>(
core: &M::Ptr<Core<M>>,
source: M::SourcePtr,
) -> io::Result<Registration<M>>
where
M: Mode,
{
#[cfg(any(unix, target_os = "wasi"))]
let descriptor = std::os::fd::AsRawFd::as_raw_fd(&M::as_source(&source));
#[cfg(windows)]
let descriptor = std::os::windows::io::AsRawSocket::as_raw_socket(&M::as_source(&source));
let key = {
let mut sources = core.reactor.sources.lock();
if !sources.descriptors.insert(descriptor) {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"this runtime watches the source's descriptor already",
));
}
let key = sources.next_key;
if let Err(e) = core.remote.poller.add(key, descriptor) {
sources.descriptors.remove(&descriptor);
return Err(e);
}
sources.next_key += 1;
sources.states.insert(
key,
SourceState {
source,
descriptor,
wakers: Wakers::default(),
armed: Directions::NONE,
stale: false,
},
);
key
};
Ok(Registration {
core: core.clone(),
key,
})
}
pub(crate) fn sleep<M>(core: &M::Ptr<Core<M>>, deadline: Option<Instant>) -> Sleep<M>
where
M: Mode,
{
Sleep {
core: core.clone(),
deadline,
id: None,
}
}
pub struct Registration<M = Local>
where
M: Mode,
{
core: M::Ptr<Core<M>>,
key: usize,
}
impl<M> fmt::Debug for Registration<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Registration").finish_non_exhaustive()
}
}
impl<M> Registration<M>
where
M: Mode,
{
pub fn poll_io<T>(
&self,
cx: &mut Context<'_>,
interest: Interest,
mut operation: impl FnMut() -> io::Result<T>,
) -> Poll<io::Result<T>> {
ready!(coop::poll_proceed(cx));
match operation() {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {}
result => {
coop::spend();
return Poll::Ready(result);
}
}
let waker = cx.waker().clone();
let stored = {
let mut sources = self.core.reactor.sources.lock();
let Some(state) = sources.states.get_mut(&self.key) else {
unreachable!("a source stays in the map for as long as its registration lives");
};
match state.arm(self.key, interest, &self.core.remote.poller) {
Ok(told) => Ok((
told,
state.wakers.get_mut(interest).operation.replace(waker),
)),
Err(e) => Err((e, waker)),
}
};
let told = match stored {
Ok((told, replaced)) => {
drop(replaced);
told
}
Err((e, waker)) => {
drop(waker);
return Poll::Ready(Err(e));
}
};
if told && !Poller::LIVE {
self.core.remote.notify();
}
Poll::Pending
}
pub fn ready(&self, interest: Interest) -> Readiness<'_, M> {
Readiness {
registration: self,
interest,
id: None,
}
}
#[cfg(any(
feature = "tcp",
all(feature = "unix", unix),
all(
feature = "process",
any(
target_vendor = "apple",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly"
)
)
))]
pub(crate) fn runtime(&self) -> crate::Runtime<M> {
crate::Runtime {
core: self.core.clone(),
}
}
}
impl<M> Drop for Registration<M>
where
M: Mode,
{
fn drop(&mut self) {
let (removed, deleted) = {
let mut sources = self.core.reactor.sources.lock();
let removed = sources.states.remove(&self.key);
let deleted = match &removed {
Some(state) => {
sources.descriptors.remove(&state.descriptor);
if sources.waiting {
sources.retired.push(state.source.clone());
}
self.core
.remote
.poller
.delete(self.key, state.descriptor, state.armed)
}
None => Ok(()),
};
(removed, deleted)
};
drop(removed);
if let Err(e) = deleted {
error!("The runtime failed to stop watching a source: {}", e);
}
self.core.remote.notify();
}
}
#[must_use = "futures do nothing unless polled"]
pub struct Readiness<'a, M = Local>
where
M: Mode,
{
registration: &'a Registration<M>,
interest: Interest,
id: Option<u64>,
}
impl<M> fmt::Debug for Readiness<'_, M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Readiness")
.field("interest", &self.interest)
.finish_non_exhaustive()
}
}
impl<M> Future for Readiness<'_, M>
where
M: Mode,
{
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let waker = cx.waker().clone();
let (done, told, unused) = {
let mut sources = this.registration.core.reactor.sources.lock();
let Sources {
states,
next_waiter,
..
} = &mut *sources;
let key = this.registration.key;
let Some(state) = states.get_mut(&key) else {
unreachable!("a source stays in the map for as long as its registration lives");
};
match this.id {
None => {
match state.arm(key, this.interest, &this.registration.core.remote.poller) {
Ok(told) => {
let id = *next_waiter;
*next_waiter += 1;
let waiting = &mut state.wakers.get_mut(this.interest).readiness;
waiting.insert(id, waker);
this.id = Some(id);
(None, told, None)
}
Err(e) => (Some(Err(e)), false, Some(waker)),
}
}
Some(id) => match state.wakers.get_mut(this.interest).readiness.get_mut(&id) {
Some(stored) => (None, false, Some(mem::replace(stored, waker))),
None => (Some(Ok(())), false, Some(waker)),
},
}
};
drop(unused);
if let Some(outcome) = done {
this.id = None;
return Poll::Ready(outcome);
}
if told && !Poller::LIVE {
this.registration.core.remote.notify();
}
Poll::Pending
}
}
impl<M> Drop for Readiness<'_, M>
where
M: Mode,
{
fn drop(&mut self) {
let Some(id) = self.id else {
return;
};
let removed = {
let mut sources = self.registration.core.reactor.sources.lock();
let Sources { states, stale, .. } = &mut *sources;
let key = self.registration.key;
let Some(state) = states.get_mut(&key) else {
unreachable!("a source stays in the map for as long as its registration lives");
};
state.mark_stale(key, stale);
state.wakers.get_mut(self.interest).readiness.remove(&id)
};
drop(removed);
}
}
pub(crate) struct Sleep<M>
where
M: Mode,
{
core: M::Ptr<Core<M>>,
deadline: Option<Instant>,
id: Option<u64>,
}
impl<M> Sleep<M>
where
M: Mode,
{
pub(crate) fn core(&self) -> &M::Ptr<Core<M>> {
&self.core
}
pub(crate) fn deadline(&self) -> Option<Instant> {
self.deadline
}
pub(crate) fn never_fires(&self) -> bool {
self.deadline.is_none()
}
pub(crate) fn reset(&mut self, deadline: Option<Instant>) {
let old = self.deadline.zip(self.id);
self.deadline = deadline;
let Some((old_deadline, id)) = old else {
return;
};
let old_key = (old_deadline, id);
let (notify, dropped) = {
let mut timers = self.core.reactor.timers.lock();
let bound = timers.pending.keys().next().copied();
match (timers.pending.remove(&old_key), deadline) {
(Some(waker), Some(new)) => {
let new_key = (new, id);
let replaced = timers.pending.insert(new_key, waker);
let earlier = bound.is_none_or(|bound| new_key < bound);
(earlier, replaced)
}
(Some(waker), None) => (bound == Some(old_key), Some(waker)),
(None, _) => (false, None),
}
};
drop(dropped);
if notify {
self.core.remote.notify();
}
}
}
impl<M> Future for Sleep<M>
where
M: Mode,
{
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = self.get_mut();
let Some(deadline) = this.deadline else {
return Poll::Pending;
};
if Instant::now() >= deadline {
return Poll::Ready(());
}
let waker = cx.waker().clone();
let (first_deadline, replaced) = {
let mut timers = this.core.reactor.timers.lock();
let id = match this.id {
Some(id) => id,
None => {
let id = timers.next_id;
timers.next_id += 1;
this.id = Some(id);
id
}
};
let key = (deadline, id);
let replaced = timers.pending.insert(key, waker);
(timers.pending.keys().next() == Some(&key), replaced)
};
let stored = replaced.is_none();
drop(replaced);
if stored && first_deadline {
this.core.remote.notify();
}
Poll::Pending
}
}
impl<M> Drop for Sleep<M>
where
M: Mode,
{
fn drop(&mut self) {
let (Some(deadline), Some(id)) = (self.deadline, self.id) else {
return;
};
let key = (deadline, id);
let (was_first, removed) = {
let mut timers = self.core.reactor.timers.lock();
let was_first = timers.pending.keys().next() == Some(&key);
(was_first, timers.pending.remove(&key))
};
drop(removed);
if was_first {
self.core.remote.notify();
}
}
}
struct Sources<M>
where
M: Mode,
{
states: HashMap<usize, SourceState<M>>,
descriptors: HashSet<RawSource>,
next_key: usize,
next_waiter: u64,
stale: Vec<usize>,
waiting: bool,
retired: Vec<M::SourcePtr>,
}
impl<M> Sources<M>
where
M: Mode,
{
fn reconcile(&mut self, poller: &Poller) -> io::Result<()> {
let Self { states, stale, .. } = self;
let mut result = Ok(());
for key in stale.drain(..) {
let Some(state) = states.get_mut(&key) else {
continue;
};
state.stale = false;
let wanted = state.wakers.directions();
if wanted == state.armed {
continue;
}
match poller.modify(key, state.descriptor, state.armed, wanted) {
Ok(()) => state.armed = wanted,
Err(e) => {
if result.is_ok() {
result = Err(e);
}
}
}
}
result
}
}
struct SourceState<M>
where
M: Mode,
{
source: M::SourcePtr,
descriptor: RawSource,
wakers: Wakers,
armed: Directions,
stale: bool,
}
impl<M> SourceState<M>
where
M: Mode,
{
fn arm(&mut self, key: usize, interest: Interest, poller: &Poller) -> io::Result<bool> {
let mut wanted = self.armed;
match interest {
Interest::Readable => wanted.readable = true,
Interest::Writable => wanted.writable = true,
}
if wanted == self.armed {
return Ok(false);
}
poller.modify(key, self.descriptor, self.armed, wanted)?;
self.armed = wanted;
Ok(true)
}
fn mark_stale(&mut self, key: usize, stale: &mut Vec<usize>) {
if !self.stale {
self.stale = true;
stale.push(key);
}
}
}
#[derive(Default)]
struct Wakers {
readable: Waiters,
writable: Waiters,
}
impl Wakers {
fn get_mut(&mut self, interest: Interest) -> &mut Waiters {
match interest {
Interest::Readable => &mut self.readable,
Interest::Writable => &mut self.writable,
}
}
fn directions(&self) -> Directions {
Directions {
readable: self.readable.any(),
writable: self.writable.any(),
}
}
}
#[derive(Default)]
struct Waiters {
operation: Option<Waker>,
readiness: HashMap<u64, Waker>,
}
impl Waiters {
fn any(&self) -> bool {
self.operation.is_some() || !self.readiness.is_empty()
}
fn take_into(&mut self, woken: &mut Vec<Waker>) {
woken.extend(self.operation.take());
woken.extend(self.readiness.drain().map(|(_, waker)| waker));
}
}
#[derive(Default)]
struct Timers {
pending: BTreeMap<(Instant, u64), Waker>,
next_id: u64,
}
#[derive(Default)]
struct BusyPoll {
limit: Duration,
history: u8,
}
impl BusyPoll {
fn is_on(&self) -> bool {
!self.limit.is_zero()
}
fn set_limit(&mut self, limit: Duration) {
self.limit = limit;
self.history = 0;
}
fn spin_for(&self, timeout: Option<Duration>) -> Duration {
if !self.is_on() || self.history == 0 {
return Duration::ZERO;
}
timeout.map_or(self.limit, |timeout| self.limit.min(timeout))
}
fn record(&mut self, waited: Duration) {
if !self.is_on() {
return;
}
self.history = (self.history << 1) | u8::from(waited <= self.limit);
}
}
#[cfg(test)]
mod tests {
use std::{
io::Write,
pin::pin,
sync::{Mutex, atomic::AtomicUsize},
task::Wake,
thread,
};
#[cfg(unix)]
use std::os::fd::OwnedFd;
#[cfg(windows)]
use std::os::windows::io::OwnedSocket;
use ntest::timeout;
use socket2::{SockRef, Socket};
use super::*;
use crate::{Shared, SharedRuntime};
#[test]
#[timeout(15000)]
fn a_readable_source_wakes_its_waker() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(read_one(®istration, &source, &mut cx).is_pending());
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(counter.count(), 1);
assert!(matches!(
read_one(®istration, &source, &mut cx),
Poll::Ready(Ok(1))
));
}
#[test]
#[timeout(15000)]
fn a_source_written_before_registration_is_seen() {
let runtime = runtime();
let (source, mut peer) = pair();
peer.write_all(&[7]).unwrap();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let read = read_one(®istration, &source, &mut Context::from_waker(&waker));
assert!(matches!(read, Poll::Ready(Ok(1))));
assert_eq!(counter.count(), 0);
}
#[test]
#[timeout(15000)]
fn notify_breaks_a_wait() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let breaker = {
let runtime = runtime.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
runtime.core.remote.notify();
})
};
let started = Instant::now();
reactor.wait(None).unwrap();
assert!(started.elapsed() < Duration::from_secs(1));
breaker.join().unwrap();
}
#[test]
#[timeout(15000)]
fn many_notifies_between_waits_write_once() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let notifying = {
let runtime = runtime.clone();
thread::spawn(move || {
let remote = &runtime.core.remote;
remote.notify();
assert!(remote.wake_pending(), "the first wake reached the channel");
for _ in 0..9 {
remote.notify();
}
assert!(remote.wake_pending(), "the wake-up is there to be taken");
})
};
notifying.join().unwrap();
reactor.wait(None).unwrap();
assert!(
!runtime.core.remote.wake_pending(),
"the wait took the wake-up out"
);
}
#[test]
#[timeout(15000)]
fn a_wake_up_flagged_and_not_yet_written_fails_no_wait() {
let runtime = runtime();
let remote = &runtime.core.remote;
remote.wake_pending.store(true, Ordering::Release);
runtime.core.reactor.wait(Some(Duration::ZERO)).unwrap();
assert!(!remote.wake_pending(), "the wait took the flag down");
}
#[test]
#[timeout(15000)]
fn a_wait_ends_at_the_nearest_deadline() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_millis(200)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
let started = Instant::now();
while counter.count() == 0 {
reactor.wait(None).unwrap();
}
assert!(started.elapsed() < Duration::from_secs(2));
assert_eq!(counter.count(), 1);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_deadline_further_off_than_a_wait_takes_bounds_it_to_what_a_wait_takes() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (_counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_secs(30 * 24 * 60 * 60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
assert_eq!(reactor.timeout(None), Some(poll::MAX_TIMEOUT));
assert_eq!(
reactor.timeout(Some(Duration::from_secs(1))),
Some(Duration::from_secs(1))
);
}
#[test]
#[timeout(15000)]
fn a_deadline_further_off_than_a_wait_takes_lets_the_wait_run() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_secs(30 * 24 * 60 * 60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
reactor.wait(None).unwrap();
assert_eq!(counter.count(), 0);
}
#[test]
#[timeout(15000)]
fn a_dropped_sleep_leaves_no_deadline() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
{
let mut sleep = pin!(runtime.sleep(Duration::from_secs(60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
assert!(!reactor.is_idle());
}
assert!(reactor.is_idle());
assert_eq!(counter.count(), 0);
}
#[test]
#[timeout(15000)]
fn a_dropped_registration_stops_the_watch() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(read_one(®istration, &source, &mut cx).is_pending());
drop(registration);
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_millis(50))).unwrap();
assert_eq!(counter.count(), 0);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn two_sleeps_with_one_deadline_both_fire() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (first, first_waker) = counting_waker();
let (second, second_waker) = counting_waker();
let deadline = Instant::now() + Duration::from_millis(5);
let mut one = pin!(sleep::<Shared>(&runtime.core, Some(deadline)));
let mut other = pin!(sleep::<Shared>(&runtime.core, Some(deadline)));
assert!(
one.as_mut()
.poll(&mut Context::from_waker(&first_waker))
.is_pending()
);
assert!(
other
.as_mut()
.poll(&mut Context::from_waker(&second_waker))
.is_pending()
);
assert_eq!(Lock::lock(&reactor.timers).pending.len(), 2);
while first.count() == 0 || second.count() == 0 {
reactor.wait(None).unwrap();
}
assert_eq!(first.count(), 1);
assert_eq!(second.count(), 1);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_failed_wait_wakes_everything() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, _peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (reader, reader_waker) = counting_waker();
let (timer, timer_waker) = counting_waker();
let mut cx = Context::from_waker(&reader_waker);
assert!(read_one(®istration, &source, &mut cx).is_pending());
let mut sleep = pin!(runtime.sleep(Duration::from_secs(60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&timer_waker));
assert!(polled.is_pending());
reactor.wake_everything();
assert_eq!(reader.count(), 1);
assert_eq!(timer.count(), 1);
}
#[test]
#[timeout(15000)]
fn a_sleep_beyond_the_clock_never_fires() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::MAX));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
assert_eq!(counter.count(), 0);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_deadline_stored_again_breaks_the_wait() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (first, first_waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_millis(500)));
assert!(
sleep
.as_mut()
.poll(&mut Context::from_waker(&first_waker))
.is_pending()
);
reactor.wait(Some(Duration::ZERO)).unwrap();
reactor.wake_everything();
assert_eq!(first.count(), 1);
assert!(reactor.is_idle());
let waiting = {
let runtime = runtime.clone();
thread::spawn(move || {
let reactor = &runtime.core.reactor;
let started = Instant::now();
reactor.wait(None).unwrap();
started.elapsed()
})
};
thread::sleep(Duration::from_millis(50));
let (second, second_waker) = counting_waker();
assert!(
sleep
.as_mut()
.poll(&mut Context::from_waker(&second_waker))
.is_pending()
);
assert!(waiting.join().unwrap() < Duration::from_secs(1));
while second.count() == 0 {
reactor.wait(None).unwrap();
}
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_reset_moves_the_deadline_and_keeps_the_waker() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_secs(60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
let started = Instant::now();
sleep.reset_after(Duration::from_millis(5));
while counter.count() == 0 {
reactor.wait(None).unwrap();
}
assert!(started.elapsed() >= Duration::from_millis(5));
assert!(started.elapsed() < Duration::from_secs(5));
assert_eq!(counter.count(), 1);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_reset_to_an_earlier_deadline_breaks_the_wait() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_secs(60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
let waiting = {
let runtime = runtime.clone();
thread::spawn(move || {
let reactor = &runtime.core.reactor;
let started = Instant::now();
reactor.wait(None).unwrap();
started.elapsed()
})
};
thread::sleep(Duration::from_millis(50));
sleep.reset_after(Duration::from_millis(5));
assert!(waiting.join().unwrap() < Duration::from_secs(1));
while counter.count() == 0 {
reactor.wait(None).unwrap();
}
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_reset_to_a_later_deadline_writes_no_wake_up() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (_counter, waker) = counting_waker();
let mut sleep = runtime.sleep(Duration::from_millis(500));
let polled = Pin::new(&mut sleep).poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
sleep.reset_after(Duration::from_secs(60));
assert!(!runtime.core.remote.wake_pending());
let earliest = Lock::lock(&reactor.timers)
.pending
.keys()
.next()
.map(|(deadline, _)| *deadline);
assert_eq!(earliest, sleep.deadline());
drop(sleep);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_reset_of_an_unpolled_sleep_stores_nothing() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let mut sleep = runtime.sleep(Duration::from_secs(60));
sleep.reset_after(Duration::from_secs(30));
assert!(reactor.is_idle());
assert!(!runtime.core.remote.wake_pending());
}
#[test]
#[timeout(15000)]
fn a_reset_beyond_the_clock_drops_the_waker() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_secs(60)));
let polled = sleep.as_mut().poll(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
drop(waker);
assert_eq!(Arc::strong_count(&counter), 2);
reactor.wait(Some(Duration::ZERO)).unwrap();
sleep.reset_after(Duration::MAX);
assert!(runtime.core.remote.wake_pending());
assert!(sleep.deadline().is_none());
assert!(reactor.is_idle());
assert_eq!(Arc::strong_count(&counter), 1);
assert_eq!(counter.count(), 0);
}
#[test]
#[timeout(15000)]
fn a_sleep_that_fired_fires_again_once_reset() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
let mut sleep = pin!(runtime.sleep(Duration::from_millis(5)));
assert!(sleep.as_mut().poll(&mut cx).is_pending());
while counter.count() == 0 {
reactor.wait(None).unwrap();
}
assert!(sleep.as_mut().poll(&mut cx).is_ready());
sleep.reset_after(Duration::from_millis(100));
assert!(reactor.is_idle());
assert!(sleep.as_mut().poll(&mut cx).is_pending());
while counter.count() == 1 {
reactor.wait(None).unwrap();
}
assert_eq!(counter.count(), 2);
assert!(sleep.as_mut().poll(&mut cx).is_ready());
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_readiness_wait_completes_once_the_source_is_ready() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let (counter, waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(poll_with(&mut ready, &waker).is_pending());
assert!(waiters(®istration, Interest::Readable, Waiters::any));
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(counter.count(), 1);
assert!(!waiters(®istration, Interest::Readable, Waiters::any));
assert!(poll_with(&mut ready, &waker).is_ready());
}
#[test]
#[timeout(15000)]
fn a_readiness_wait_stays_pending_on_a_poll_the_source_did_not_cause() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, _peer) = pair();
let registration = runtime.register(source).unwrap();
let (counter, waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(poll_with(&mut ready, &waker).is_pending());
assert!(poll_with(&mut ready, &waker).is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
reactor.wait(Some(Duration::from_millis(50))).unwrap();
assert_eq!(counter.count(), 0);
assert!(poll_with(&mut ready, &waker).is_pending());
assert!(waiters(®istration, Interest::Readable, Waiters::any));
}
#[test]
#[timeout(15000)]
fn every_readiness_wait_in_one_direction_is_woken() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let wakers = [counting_waker(), counting_waker(), counting_waker()];
let mut waits = [
registration.ready(Interest::Readable),
registration.ready(Interest::Readable),
registration.ready(Interest::Readable),
];
for (wait, (_, waker)) in waits.iter_mut().zip(&wakers) {
assert!(poll_with(wait, waker).is_pending());
}
let stored = waiters(®istration, Interest::Readable, |waiters| {
waiters.readiness.len()
});
assert_eq!(stored, 3);
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
for (wait, (counter, waker)) in waits.iter_mut().zip(&wakers) {
assert_eq!(counter.count(), 1);
assert!(poll_with(wait, waker).is_ready());
}
}
#[test]
#[timeout(15000)]
fn a_readiness_wait_and_an_operation_are_woken_together() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (operation, operation_waker) = counting_waker();
let (readiness, readiness_waker) = counting_waker();
let mut operation_cx = Context::from_waker(&operation_waker);
let mut ready = registration.ready(Interest::Readable);
assert!(read_one(®istration, &source, &mut operation_cx).is_pending());
assert!(poll_with(&mut ready, &readiness_waker).is_pending());
assert!(read_one(®istration, &source, &mut operation_cx).is_pending());
let stored = waiters(®istration, Interest::Readable, |waiters| {
(waiters.operation.is_some(), waiters.readiness.len())
});
assert_eq!(stored, (true, 1));
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(operation.count(), 1);
assert_eq!(readiness.count(), 1);
assert!(poll_with(&mut ready, &readiness_waker).is_ready());
assert!(matches!(
read_one(®istration, &source, &mut operation_cx),
Poll::Ready(Ok(1))
));
}
#[test]
#[timeout(15000)]
fn a_dropped_readiness_wait_leaves_nothing_behind() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let (counter, waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(poll_with(&mut ready, &waker).is_pending());
assert_eq!(Arc::strong_count(&counter), 3);
assert!(waiters(®istration, Interest::Readable, Waiters::any));
drop(ready);
assert!(!waiters(®istration, Interest::Readable, Waiters::any));
assert_eq!(Arc::strong_count(&counter), 2);
drop(waker);
assert_eq!(Arc::strong_count(&counter), 1);
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_millis(50))).unwrap();
assert_eq!(counter.count(), 0);
}
#[test]
#[timeout(15000)]
fn dropping_the_first_of_two_readiness_waits_leaves_the_second_waiting() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let (first, first_waker) = counting_waker();
let (second, second_waker) = counting_waker();
let mut dropped = registration.ready(Interest::Readable);
let mut kept = registration.ready(Interest::Readable);
assert!(poll_with(&mut dropped, &first_waker).is_pending());
assert!(poll_with(&mut kept, &second_waker).is_pending());
drop(dropped);
assert_eq!(Arc::strong_count(&first), 2);
assert_eq!(Arc::strong_count(&second), 3);
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(first.count(), 0);
assert_eq!(second.count(), 1);
assert!(poll_with(&mut kept, &second_waker).is_ready());
}
#[test]
#[timeout(15000)]
fn an_unpolled_readiness_wait_stores_nothing() {
let runtime = runtime();
let (source, _peer) = pair();
let registration = runtime.register(source).unwrap();
let ready = registration.ready(Interest::Readable);
assert!(!waiters(®istration, Interest::Readable, Waiters::any));
drop(ready);
assert!(!waiters(®istration, Interest::Readable, Waiters::any));
assert!(!runtime.core.remote.wake_pending());
}
#[test]
#[timeout(15000)]
fn only_the_first_poll_of_a_readiness_wait_writes_a_wake_up() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let remote = &runtime.core.remote;
let (source, _peer) = pair();
let registration = runtime.register(source).unwrap();
let (_counter, waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(!remote.wake_pending());
assert!(poll_with(&mut ready, &waker).is_pending());
assert_eq!(remote.wake_pending(), !Poller::LIVE);
reactor.wait(Some(Duration::ZERO)).unwrap();
assert!(!remote.wake_pending());
assert!(poll_with(&mut ready, &waker).is_pending());
assert!(!remote.wake_pending());
}
#[test]
#[timeout(15000)]
fn a_failed_wait_ends_a_readiness_wait() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, _peer) = pair();
let registration = runtime.register(source).unwrap();
let (counter, waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(poll_with(&mut ready, &waker).is_pending());
reactor.wake_everything();
assert_eq!(counter.count(), 1);
assert!(!waiters(®istration, Interest::Readable, Waiters::any));
assert!(poll_with(&mut ready, &waker).is_ready());
}
#[test]
#[timeout(15000)]
fn a_writable_readiness_wait_completes_on_a_fresh_pair() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, _peer) = pair();
let registration = runtime.register(source).unwrap();
let (counter, waker) = counting_waker();
let mut ready = registration.ready(Interest::Writable);
assert!(poll_with(&mut ready, &waker).is_pending());
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(counter.count(), 1);
assert!(poll_with(&mut ready, &waker).is_ready());
}
#[test]
#[timeout(15000)]
fn a_readiness_wait_is_not_woken_by_the_other_direction() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let (readable, readable_waker) = counting_waker();
let (writable, writable_waker) = counting_waker();
let mut reading = registration.ready(Interest::Readable);
let mut writing = registration.ready(Interest::Writable);
assert!(poll_with(&mut reading, &readable_waker).is_pending());
assert!(poll_with(&mut writing, &writable_waker).is_pending());
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(writable.count(), 1);
assert_eq!(readable.count(), 0);
assert!(poll_with(&mut writing, &writable_waker).is_ready());
assert!(poll_with(&mut reading, &readable_waker).is_pending());
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(readable.count(), 1);
assert_eq!(writable.count(), 1);
assert!(poll_with(&mut reading, &readable_waker).is_ready());
}
#[test]
#[timeout(15000)]
fn a_readiness_wait_is_woken_through_the_waker_of_its_last_poll() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let (first, first_waker) = counting_waker();
let (second, second_waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(poll_with(&mut ready, &first_waker).is_pending());
assert!(poll_with(&mut ready, &second_waker).is_pending());
drop(first_waker);
assert_eq!(Arc::strong_count(&first), 1);
assert_eq!(Arc::strong_count(&second), 3);
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(first.count(), 0);
assert_eq!(second.count(), 1);
assert!(poll_with(&mut ready, &second_waker).is_ready());
}
#[test]
#[timeout(15000)]
fn a_readiness_wait_polled_with_another_waker_after_readiness_completes() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source).unwrap();
let (first, first_waker) = counting_waker();
let (second, second_waker) = counting_waker();
let mut ready = registration.ready(Interest::Readable);
assert!(poll_with(&mut ready, &first_waker).is_pending());
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(first.count(), 1);
assert!(poll_with(&mut ready, &second_waker).is_ready());
assert_eq!(second.count(), 0);
assert_eq!(Arc::strong_count(&second), 2);
}
#[test]
#[timeout(15000)]
fn a_direction_nobody_waits_in_again_is_let_go_of_before_the_next_wait() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert_eq!(armed(®istration), Directions::NONE);
assert!(read_one(®istration, &source, &mut cx).is_pending());
assert_eq!(armed(®istration), READABLE);
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(counter.count(), 1);
assert_eq!(armed(®istration), READABLE);
reactor.wait(Some(Duration::ZERO)).unwrap();
assert_eq!(armed(®istration), Directions::NONE);
}
#[test]
#[timeout(15000)]
fn a_waiter_back_before_the_next_wait_keeps_its_direction_watched() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(read_one(®istration, &source, &mut cx).is_pending());
peer.write_all(&[7]).unwrap();
reactor.wait(Some(Duration::from_secs(1))).unwrap();
assert_eq!(counter.count(), 1);
assert!(matches!(
read_one(®istration, &source, &mut cx),
Poll::Ready(Ok(1))
));
assert!(read_one(®istration, &source, &mut cx).is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
assert_eq!(armed(®istration), READABLE);
assert_eq!(counter.count(), 1);
}
#[test]
#[timeout(15000)]
fn a_dropped_readiness_wait_lets_its_direction_go_before_the_next_wait() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, _peer) = pair();
let registration = runtime.register(source).unwrap();
let (_counter, waker) = counting_waker();
let mut first = registration.ready(Interest::Readable);
let mut second = registration.ready(Interest::Readable);
assert!(poll_with(&mut first, &waker).is_pending());
assert!(poll_with(&mut second, &waker).is_pending());
drop(first);
reactor.wait(Some(Duration::ZERO)).unwrap();
assert_eq!(armed(®istration), READABLE);
drop(second);
assert_eq!(armed(®istration), READABLE);
reactor.wait(Some(Duration::ZERO)).unwrap();
assert_eq!(armed(®istration), Directions::NONE);
}
#[test]
#[timeout(15000)]
fn a_hung_up_source_nobody_waits_on_lets_the_wait_block() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, peer) = pair();
let _registration = runtime.register(source).unwrap();
drop(peer);
reactor.wait(Some(Duration::ZERO)).unwrap();
reactor.wait(Some(Duration::ZERO)).unwrap();
let started = Instant::now();
reactor.wait(Some(Duration::from_millis(100))).unwrap();
assert!(started.elapsed() >= Duration::from_millis(90));
}
#[test]
#[timeout(15000)]
fn a_source_let_go_of_during_a_wait_is_kept_until_the_wait_returns() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
let (source, _peer) = pair();
let dropped = Arc::new(Mutex::new(false));
let tracked = Tracked {
source,
dropped: dropped.clone(),
};
let registration = runtime.register(tracked).unwrap();
Lock::lock(&reactor.sources).waiting = true;
drop(registration);
assert!(!*dropped.lock().unwrap());
assert_eq!(Lock::lock(&reactor.sources).retired.len(), 1);
reactor.wait(Some(Duration::ZERO)).unwrap();
assert!(*dropped.lock().unwrap());
assert!(Lock::lock(&reactor.sources).retired.is_empty());
}
#[test]
#[timeout(15000)]
fn busy_polling_is_off_by_default() {
let mut busy_poll = BusyPoll::default();
assert!(!busy_poll.is_on());
busy_poll.record(SHORT);
assert_eq!(busy_poll.history, 0);
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
assert_eq!(busy_poll.spin_for(Some(LONG)), Duration::ZERO);
}
#[test]
#[timeout(15000)]
fn a_limit_spins_only_once_a_wait_has_been_short() {
let mut busy_poll = on();
assert!(busy_poll.is_on());
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
busy_poll.record(LONG);
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
busy_poll.record(SHORT);
assert_eq!(busy_poll.spin_for(None), LIMIT);
}
#[test]
#[timeout(15000)]
fn a_wait_as_long_as_the_limit_is_a_short_one() {
let mut as_long = on();
as_long.record(LIMIT);
let mut longer = on();
longer.record(LIMIT + Duration::from_nanos(1));
assert_eq!(as_long.spin_for(None), LIMIT);
assert_eq!(longer.spin_for(None), Duration::ZERO);
}
#[test]
#[timeout(15000)]
fn a_spin_is_cut_to_the_timeout_of_its_wait() {
let mut busy_poll = on();
busy_poll.record(SHORT);
assert_eq!(busy_poll.spin_for(Some(SHORT)), SHORT);
assert_eq!(busy_poll.spin_for(Some(LIMIT)), LIMIT);
assert_eq!(busy_poll.spin_for(Some(LONG)), LIMIT);
assert_eq!(busy_poll.spin_for(None), LIMIT);
assert_eq!(busy_poll.spin_for(Some(Duration::ZERO)), Duration::ZERO);
}
#[test]
#[timeout(15000)]
fn eight_long_waits_stop_the_spinning() {
let mut busy_poll = on();
busy_poll.record(SHORT);
for _ in 0..u8::BITS {
assert_eq!(busy_poll.spin_for(None), LIMIT);
busy_poll.record(LONG);
}
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
}
#[test]
#[timeout(15000)]
fn a_burst_wastes_no_more_than_eight_spins() {
for burst in [1, 8, 100] {
let mut busy_poll = on();
for _ in 0..burst {
busy_poll.record(SHORT);
}
let mut wasted = Duration::ZERO;
for _ in 0..100 {
wasted += busy_poll.spin_for(None);
busy_poll.record(LONG);
}
assert_eq!(wasted, 8 * LIMIT, "{burst} short waits");
}
}
#[test]
#[timeout(15000)]
fn short_and_long_waits_in_turn_keep_the_spinning_going() {
let mut turns = on();
turns.record(SHORT);
let mut sevens = on();
sevens.record(SHORT);
for _ in 0..100 {
assert_eq!(turns.spin_for(None), LIMIT);
turns.record(LONG);
assert_eq!(turns.spin_for(None), LIMIT);
turns.record(SHORT);
for _ in 0..7 {
assert_eq!(sevens.spin_for(None), LIMIT);
sevens.record(LONG);
}
assert_eq!(sevens.spin_for(None), LIMIT);
sevens.record(SHORT);
}
}
#[test]
#[timeout(15000)]
fn a_short_wait_starts_the_spinning_again() {
let mut busy_poll = on();
busy_poll.record(SHORT);
for _ in 0..u8::BITS {
busy_poll.record(LONG);
}
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
busy_poll.record(SHORT);
assert_eq!(busy_poll.spin_for(None), LIMIT);
}
#[test]
#[timeout(15000)]
fn a_new_limit_forgets_the_history() {
let mut busy_poll = on();
busy_poll.record(SHORT);
let longer = LIMIT * 2;
busy_poll.set_limit(longer);
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
busy_poll.record(SHORT);
assert_eq!(busy_poll.spin_for(None), longer);
}
#[test]
#[timeout(15000)]
fn a_limit_of_zero_turns_the_polling_off() {
let mut busy_poll = on();
busy_poll.record(SHORT);
assert_eq!(busy_poll.spin_for(None), LIMIT);
busy_poll.set_limit(Duration::ZERO);
busy_poll.record(Duration::ZERO);
assert!(!busy_poll.is_on());
assert_eq!(busy_poll.history, 0);
assert_eq!(busy_poll.spin_for(None), Duration::ZERO);
}
#[test]
#[timeout(15000)]
fn a_wait_is_not_counted_unless_busy_polling_is_on() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
reactor.wait(Some(Duration::from_millis(1))).unwrap();
assert_eq!(history(reactor), 0);
}
#[test]
#[timeout(15000)]
fn only_a_wait_that_could_sleep_is_counted() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
reactor.set_busy_poll(Duration::from_secs(10));
reactor.wait(Some(Duration::ZERO)).unwrap();
assert_eq!(history(reactor), 0);
assert!(!reactor.would_spin());
reactor.wait(Some(Duration::from_millis(1))).unwrap();
assert_eq!(history(reactor), 0b1);
assert!(reactor.would_spin());
reactor.wait(Some(Duration::ZERO)).unwrap();
assert_eq!(history(reactor), 0b1);
}
#[test]
#[timeout(15000)]
fn a_wait_longer_than_the_limit_is_counted_as_a_long_one() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
busy_polling(reactor, Duration::from_millis(20));
let short = history(reactor);
reactor.wait(Some(Duration::from_millis(60))).unwrap();
assert_eq!(history(reactor), short << 1);
}
#[test]
#[timeout(15000)]
fn a_spin_ends_at_a_wake_up_that_is_on_its_way() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
busy_polling(reactor, Duration::from_secs(10));
runtime
.core
.remote
.wake_pending
.store(true, Ordering::Release);
let started = Instant::now();
reactor.wait(None).unwrap();
assert!(started.elapsed() < Duration::from_secs(1));
assert!(
!runtime.core.remote.wake_pending(),
"the wait took the flag down"
);
}
#[test]
#[timeout(15000)]
fn a_spin_ends_at_a_wake_up_from_another_thread() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
busy_polling(reactor, Duration::from_secs(10));
let breaker = {
let runtime = runtime.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
runtime.core.remote.notify();
})
};
let started = Instant::now();
reactor.wait(None).unwrap();
assert!(started.elapsed() < Duration::from_secs(1));
assert!(
!runtime.core.remote.wake_pending(),
"the wait took the flag down"
);
breaker.join().unwrap();
}
#[test]
#[timeout(15000)]
fn a_spin_ends_at_a_source_that_is_ready() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
busy_polling(reactor, Duration::from_secs(10));
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(read_one(®istration, &source, &mut cx).is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
let writer = thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
peer.write_all(&[7]).unwrap();
});
let started = Instant::now();
reactor.wait(None).unwrap();
assert!(started.elapsed() < Duration::from_secs(1));
assert_eq!(counter.count(), 1);
writer.join().unwrap();
}
#[test]
#[timeout(15000)]
fn a_spin_ends_at_the_nearest_deadline() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
busy_polling(reactor, Duration::from_secs(10));
let (counter, waker) = counting_waker();
let mut sleep = pin!(runtime.sleep(Duration::from_millis(20)));
assert!(
sleep
.as_mut()
.poll(&mut Context::from_waker(&waker))
.is_pending()
);
let started = Instant::now();
while counter.count() == 0 {
reactor.wait(None).unwrap();
}
assert!(started.elapsed() < Duration::from_secs(1));
assert_eq!(counter.count(), 1);
assert!(reactor.is_idle());
}
#[test]
#[timeout(15000)]
fn a_wait_goes_on_once_the_spin_has_run_out() {
let runtime = runtime();
let reactor = &runtime.core.reactor;
busy_polling(reactor, Duration::from_millis(10));
let (source, mut peer) = pair();
let registration = runtime.register(source.clone()).unwrap();
let (counter, waker) = counting_waker();
let mut cx = Context::from_waker(&waker);
assert!(read_one(®istration, &source, &mut cx).is_pending());
reactor.wait(Some(Duration::ZERO)).unwrap();
let started = Instant::now();
let writer = thread::spawn(move || {
thread::sleep(Duration::from_millis(200));
peer.write_all(&[7]).unwrap();
});
reactor.wait(None).unwrap();
assert!(started.elapsed() >= Duration::from_millis(200));
assert_eq!(counter.count(), 1);
assert_eq!(history(reactor) & 1, 0);
assert_ne!(history(reactor), 0);
writer.join().unwrap();
}
fn runtime() -> SharedRuntime {
SharedRuntime::new().unwrap()
}
fn counting_waker() -> (Arc<Counter>, Waker) {
let counter = Arc::new(Counter::default());
let waker = Waker::from(counter.clone());
(counter, waker)
}
#[cfg(unix)]
type TestSource = Arc<OwnedFd>;
#[cfg(windows)]
type TestSource = Arc<OwnedSocket>;
fn pair() -> (TestSource, Socket) {
let (near, far) = connected();
near.set_nonblocking(true).unwrap();
far.set_nonblocking(true).unwrap();
(owned_source(near), far)
}
#[cfg(unix)]
fn owned_source(socket: Socket) -> TestSource {
let owned: OwnedFd = socket.into();
Arc::new(owned)
}
#[cfg(windows)]
fn owned_source(socket: Socket) -> TestSource {
let owned: OwnedSocket = socket.into();
Arc::new(owned)
}
#[cfg(unix)]
fn connected() -> (Socket, Socket) {
socket2::Socket::pair(socket2::Domain::UNIX, socket2::Type::STREAM, None).unwrap()
}
#[cfg(windows)]
fn connected() -> (Socket, Socket) {
use std::net::{TcpListener, TcpStream};
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let far = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
let near = loop {
let (accepted, _) = listener.accept().unwrap();
if accepted.peer_addr().unwrap() == far.local_addr().unwrap() {
break accepted;
}
};
near.set_nodelay(true).unwrap();
far.set_nodelay(true).unwrap();
(near.into(), far.into())
}
fn read_one(
registration: &Registration<Shared>,
source: &TestSource,
cx: &mut Context<'_>,
) -> Poll<io::Result<usize>> {
let mut byte = [std::mem::MaybeUninit::<u8>::uninit(); 1];
registration.poll_io(cx, Interest::Readable, || {
SockRef::from(source).recv(&mut byte)
})
}
fn poll_with(ready: &mut Readiness<'_, Shared>, waker: &Waker) -> Poll<()> {
Pin::new(ready)
.poll(&mut Context::from_waker(waker))
.map(|outcome| outcome.expect("the runtime watches the source"))
}
fn waiters<T>(
registration: &Registration<Shared>,
interest: Interest,
read: impl FnOnce(&Waiters) -> T,
) -> T {
let mut sources = Lock::lock(®istration.core.reactor.sources);
let Some(state) = sources.states.get_mut(®istration.key) else {
unreachable!("a source stays in the map for as long as its registration lives");
};
read(state.wakers.get_mut(interest))
}
fn armed(registration: &Registration<Shared>) -> Directions {
let sources = Lock::lock(®istration.core.reactor.sources);
let Some(state) = sources.states.get(®istration.key) else {
unreachable!("a source stays in the map for as long as its registration lives");
};
state.armed
}
fn history(reactor: &Reactor<Shared>) -> u8 {
Lock::lock(&reactor.busy_poll).history
}
fn busy_polling(reactor: &Reactor<Shared>, limit: Duration) {
reactor.set_busy_poll(limit);
while !reactor.would_spin() {
reactor.wait(Some(Duration::from_micros(100))).unwrap();
}
}
fn on() -> BusyPoll {
let mut busy_poll = BusyPoll::default();
busy_poll.set_limit(LIMIT);
busy_poll
}
const READABLE: Directions = Directions {
readable: true,
writable: false,
};
const LIMIT: Duration = Duration::from_micros(20);
const SHORT: Duration = Duration::from_micros(5);
const LONG: Duration = Duration::from_millis(5);
struct Tracked {
source: TestSource,
dropped: Arc<Mutex<bool>>,
}
#[cfg(unix)]
impl std::os::fd::AsFd for Tracked {
fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
self.source.as_fd()
}
}
#[cfg(windows)]
impl std::os::windows::io::AsSocket for Tracked {
fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
self.source.as_socket()
}
}
impl Drop for Tracked {
fn drop(&mut self) {
*self.dropped.lock().unwrap() = true;
}
}
#[derive(Default)]
struct Counter(AtomicUsize);
impl Counter {
fn count(&self) -> usize {
self.0.load(Ordering::Acquire)
}
}
impl Wake for Counter {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.fetch_add(1, Ordering::AcqRel);
}
}
}