Skip to content

Commit c659cf8

Browse files
committed
Use a cached Parker/Unparker pair when possible
This optimization is inspired by what futures_lite does for the block_on function. This should prevent users from dealing with the overhead of creating a parker and unparker every time wait() is called.
1 parent 09ded13 commit c659cf8

File tree

1 file changed

+47
-8
lines changed

1 file changed

+47
-8
lines changed

src/lib.rs

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -610,15 +610,54 @@ unsafe impl<B: Borrow<Inner> + Unpin + Sync> Sync for Listener<B> {}
610610
impl<B: Borrow<Inner> + Unpin> Listener<B> {
611611
/// Wait until the provided deadline.
612612
#[cfg(feature = "std")]
613-
fn wait_internal(mut self, deadline: Option<Instant>) -> bool {
614-
let (parker, unparker) = parking::pair();
613+
fn wait_internal(self, deadline: Option<Instant>) -> bool {
614+
use std::cell::RefCell;
615615

616+
std::thread_local! {
617+
/// Cached thread-local parker/unparker pair.
618+
static PARKER: RefCell<Option<(parking::Parker, Task)>> = RefCell::new(None);
619+
}
620+
621+
// Try to borrow the thread-local parker/unparker pair.
622+
let mut this = Some(self);
623+
PARKER
624+
.try_with({
625+
|parker| {
626+
let mut pair = parker
627+
.try_borrow_mut()
628+
.expect("Shouldn't be able to borrow parker reentrantly");
629+
let (parker, unparker) = pair.get_or_insert_with(|| {
630+
let (parker, unparker) = parking::pair();
631+
(parker, Task::Unparker(unparker))
632+
});
633+
634+
this.take()
635+
.unwrap()
636+
.wait_with_parker(deadline, parker, unparker.as_task_ref())
637+
}
638+
})
639+
.unwrap_or_else(|_| {
640+
// If the pair isn't accessible, we may be being called in a destructor.
641+
// Just create a new pair.
642+
let (parker, unparker) = parking::pair();
643+
this.take().unwrap().wait_with_parker(
644+
deadline,
645+
&parker,
646+
TaskRef::Unparker(&unparker),
647+
)
648+
})
649+
}
650+
651+
/// Wait until the provided deadline using the specified parker/unparker pair.
652+
#[cfg(feature = "std")]
653+
fn wait_with_parker(
654+
mut self,
655+
deadline: Option<Instant>,
656+
parker: &parking::Parker,
657+
unparker: TaskRef<'_>,
658+
) -> bool {
616659
// Set the listener's state to `Task`.
617-
match self
618-
.event
619-
.borrow()
620-
.register(&mut self.listener, TaskRef::Unparker(&unparker))
621-
{
660+
match self.event.borrow().register(&mut self.listener, unparker) {
622661
Some(true) => {
623662
// We were already notified, so we don't need to park.
624663
return true;
@@ -658,7 +697,7 @@ impl<B: Borrow<Inner> + Unpin> Listener<B> {
658697
if self
659698
.event
660699
.borrow()
661-
.register(&mut self.listener, TaskRef::Unparker(&unparker))
700+
.register(&mut self.listener, unparker)
662701
.expect("We never removed ourself from the list")
663702
{
664703
return true;

0 commit comments

Comments
 (0)