Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Rework the internal structure of the event-listener crate #51

Merged
merged 21 commits into from
Apr 2, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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.
  • Loading branch information
notgull committed Mar 31, 2023
commit c659cf84f3b28cc3d3d012438a19d05ea81f695f
55 changes: 47 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,15 +610,54 @@ unsafe impl<B: Borrow<Inner> + Unpin + Sync> Sync for Listener<B> {}
impl<B: Borrow<Inner> + Unpin> Listener<B> {
/// Wait until the provided deadline.
#[cfg(feature = "std")]
fn wait_internal(mut self, deadline: Option<Instant>) -> bool {
let (parker, unparker) = parking::pair();
fn wait_internal(self, deadline: Option<Instant>) -> bool {
use std::cell::RefCell;

std::thread_local! {
/// Cached thread-local parker/unparker pair.
static PARKER: RefCell<Option<(parking::Parker, Task)>> = RefCell::new(None);
}

// Try to borrow the thread-local parker/unparker pair.
let mut this = Some(self);
PARKER
.try_with({
|parker| {
let mut pair = parker
.try_borrow_mut()
.expect("Shouldn't be able to borrow parker reentrantly");
let (parker, unparker) = pair.get_or_insert_with(|| {
let (parker, unparker) = parking::pair();
(parker, Task::Unparker(unparker))
});

this.take()
.unwrap()
.wait_with_parker(deadline, parker, unparker.as_task_ref())
}
})
.unwrap_or_else(|_| {
// If the pair isn't accessible, we may be being called in a destructor.
// Just create a new pair.
let (parker, unparker) = parking::pair();
this.take().unwrap().wait_with_parker(
deadline,
&parker,
TaskRef::Unparker(&unparker),
)
})
}

/// Wait until the provided deadline using the specified parker/unparker pair.
#[cfg(feature = "std")]
fn wait_with_parker(
mut self,
deadline: Option<Instant>,
parker: &parking::Parker,
unparker: TaskRef<'_>,
) -> bool {
// Set the listener's state to `Task`.
match self
.event
.borrow()
.register(&mut self.listener, TaskRef::Unparker(&unparker))
{
match self.event.borrow().register(&mut self.listener, unparker) {
Some(true) => {
// We were already notified, so we don't need to park.
return true;
Expand Down Expand Up @@ -658,7 +697,7 @@ impl<B: Borrow<Inner> + Unpin> Listener<B> {
if self
.event
.borrow()
.register(&mut self.listener, TaskRef::Unparker(&unparker))
.register(&mut self.listener, unparker)
.expect("We never removed ourself from the list")
{
return true;
Expand Down