-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
m: Optimize block_on by caching Parker and Waker
1 parent
1b1466a
commit 8c3c3bd
Showing
2 changed files
with
301 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
use async_io::block_on; | ||
use std::{ | ||
future::Future, | ||
pin::Pin, | ||
task::{Context, Poll, Waker}, | ||
time::{Duration, Instant}, | ||
}; | ||
|
||
#[test] | ||
fn doesnt_poll_after_ready() { | ||
#[derive(Default)] | ||
struct Bomb { | ||
returned_ready: bool, | ||
} | ||
impl Future for Bomb { | ||
type Output = (); | ||
|
||
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
if self.returned_ready { | ||
panic!("Future was polled again after returning Poll::Ready"); | ||
} else { | ||
self.returned_ready = true; | ||
Poll::Ready(()) | ||
} | ||
} | ||
} | ||
|
||
block_on(Bomb::default()) | ||
} | ||
|
||
#[test] | ||
fn recursive_wakers_are_different() { | ||
struct Outer; | ||
impl Future for Outer { | ||
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let outer_waker = cx.waker(); | ||
block_on(Inner { outer_waker }); | ||
Poll::Ready(()) | ||
} | ||
} | ||
|
||
struct Inner<'a> { | ||
pub outer_waker: &'a Waker, | ||
} | ||
impl Future for Inner<'_> { | ||
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let inner_waker = cx.waker(); | ||
assert!(!inner_waker.will_wake(self.outer_waker)); | ||
Poll::Ready(()) | ||
} | ||
} | ||
|
||
block_on(Outer); | ||
} | ||
|
||
#[test] | ||
fn inner_cannot_wake_outer() { | ||
#[derive(Default)] | ||
struct Outer { | ||
elapsed: Option<Instant>, | ||
} | ||
impl Future for Outer { | ||
type Output = (); | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
if let Some(elapsed) = self.elapsed { | ||
assert!(elapsed.elapsed() >= Duration::from_secs(1)); | ||
Poll::Ready(()) | ||
} else { | ||
let outer_waker = cx.waker().clone(); | ||
block_on(Inner); | ||
std::thread::spawn(|| { | ||
std::thread::sleep(Duration::from_secs(1)); | ||
outer_waker.wake(); | ||
}); | ||
self.elapsed = Some(Instant::now()); | ||
Poll::Pending | ||
} | ||
} | ||
} | ||
|
||
struct Inner; | ||
impl Future for Inner { | ||
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let inner_waker = cx.waker(); | ||
inner_waker.wake_by_ref(); | ||
Poll::Ready(()) | ||
} | ||
} | ||
|
||
block_on(Outer::default()); | ||
} | ||
|
||
#[test] | ||
fn outer_cannot_wake_inner() { | ||
struct Outer; | ||
impl Future for Outer { | ||
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let outer_waker = cx.waker(); | ||
outer_waker.wake_by_ref(); | ||
block_on(Inner::default()); | ||
Poll::Ready(()) | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
struct Inner { | ||
elapsed: Option<Instant>, | ||
} | ||
impl Future for Inner { | ||
type Output = (); | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
if let Some(elapsed) = self.elapsed { | ||
assert!(elapsed.elapsed() >= Duration::from_secs(1)); | ||
Poll::Ready(()) | ||
} else { | ||
let inner_waker = cx.waker().clone(); | ||
std::thread::spawn(|| { | ||
std::thread::sleep(Duration::from_secs(1)); | ||
inner_waker.wake(); | ||
}); | ||
self.elapsed = Some(Instant::now()); | ||
Poll::Pending | ||
} | ||
} | ||
} | ||
|
||
block_on(Outer); | ||
} | ||
|
||
#[test] | ||
fn first_cannot_wake_second() { | ||
struct First; | ||
impl Future for First { | ||
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let first_waker = cx.waker(); | ||
first_waker.wake_by_ref(); | ||
Poll::Ready(()) | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
struct Second { | ||
elapsed: Option<Instant>, | ||
} | ||
impl Future for Second { | ||
type Output = (); | ||
|
||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
if let Some(elapsed) = self.elapsed { | ||
assert!(elapsed.elapsed() >= Duration::from_secs(1)); | ||
Poll::Ready(()) | ||
} else { | ||
let second_waker = cx.waker().clone(); | ||
std::thread::spawn(|| { | ||
std::thread::sleep(Duration::from_secs(1)); | ||
second_waker.wake(); | ||
}); | ||
self.elapsed = Some(Instant::now()); | ||
Poll::Pending | ||
} | ||
} | ||
} | ||
|
||
block_on(First); | ||
block_on(Second::default()); | ||
} |