Skip to content

Commit 0235e55

Browse files
authored
Replace the linked list with a safer and less allocation-heavy alternative (#38)
* Use slab to avoid unsafe code * Move send+sync impls down to Mutex * Code review * Unwrap the key earlier * Reduce the scope of one of the unsafe blocks.
1 parent 6496571 commit 0235e55

File tree

7 files changed

+224
-312
lines changed

7 files changed

+224
-312
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ __test = []
2424
[dependencies]
2525
crossbeam-utils = { version = "0.8.12", default-features = false }
2626
parking = { version = "2.0.0", optional = true }
27+
slab = { version = "0.4.7", default-features = false }
2728

2829
[dev-dependencies]
2930
waker-fn = "1"

src/inner.rs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! The inner mechanism powering the `Event` type.
22
3-
use crate::list::{Entry, List};
3+
use crate::list::List;
44
use crate::node::Node;
55
use crate::queue::Queue;
66
use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
@@ -11,7 +11,6 @@ use alloc::vec;
1111
use alloc::vec::Vec;
1212

1313
use core::ops;
14-
use core::ptr::NonNull;
1514

1615
/// Inner state of [`Event`].
1716
pub(crate) struct Inner {
@@ -25,14 +24,6 @@ pub(crate) struct Inner {
2524

2625
/// Queue of nodes waiting to be processed.
2726
queue: Queue,
28-
29-
/// A single cached list entry to avoid allocations on the fast path of the insertion.
30-
///
31-
/// This field can only be written to when the `cache_used` field in the `list` structure
32-
/// is false, or the user has a pointer to the `Entry` identical to this one and that user
33-
/// has exclusive access to that `Entry`. An immutable pointer to this field is kept in
34-
/// the `list` structure when it is in use.
35-
cache: UnsafeCell<Entry>,
3627
}
3728

3829
impl Inner {
@@ -42,7 +33,6 @@ impl Inner {
4233
notified: AtomicUsize::new(core::usize::MAX),
4334
list: Mutex::new(List::new()),
4435
queue: Queue::new(),
45-
cache: UnsafeCell::new(Entry::new()),
4636
}
4737
}
4838

@@ -62,12 +52,6 @@ impl Inner {
6252
// Acquire and drop the lock to make sure that the queue is flushed.
6353
let _guard = self.lock();
6454
}
65-
66-
/// Returns the pointer to the single cached list entry.
67-
#[inline(always)]
68-
pub(crate) fn cache_ptr(&self) -> NonNull<Entry> {
69-
unsafe { NonNull::new_unchecked(self.cache.get()) }
70-
}
7155
}
7256

7357
/// The guard returned by [`Inner::lock`].
@@ -88,11 +72,11 @@ impl ListGuard<'_> {
8872
guard: &mut MutexGuard<'_, List>,
8973
) {
9074
// Process the start node.
91-
tasks.extend(start_node.apply(guard, self.inner));
75+
tasks.extend(start_node.apply(guard));
9276

9377
// Process all remaining nodes.
9478
while let Some(node) = self.inner.queue.pop() {
95-
tasks.extend(node.apply(guard, self.inner));
79+
tasks.extend(node.apply(guard));
9680
}
9781
}
9882
}
@@ -125,7 +109,7 @@ impl Drop for ListGuard<'_> {
125109
}
126110

127111
// Update the atomic `notified` counter.
128-
let notified = if list.notified < list.len {
112+
let notified = if list.notified < list.len() {
129113
list.notified
130114
} else {
131115
core::usize::MAX
@@ -224,3 +208,6 @@ impl<'a, T> ops::DerefMut for MutexGuard<'a, T> {
224208
unsafe { &mut *self.mutex.value.get() }
225209
}
226210
}
211+
212+
unsafe impl<T: Send> Send for Mutex<T> {}
213+
unsafe impl<T: Send> Sync for Mutex<T> {}

src/lib.rs

Lines changed: 92 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ use alloc::sync::Arc;
7878

7979
use core::fmt;
8080
use core::future::Future;
81-
use core::mem::ManuallyDrop;
81+
use core::mem::{self, ManuallyDrop};
82+
use core::num::NonZeroUsize;
8283
use core::pin::Pin;
83-
use core::ptr::{self, NonNull};
84+
use core::ptr;
8485
use core::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
8586
use core::task::{Context, Poll, Waker};
8687
use core::usize;
@@ -92,7 +93,7 @@ use std::time::{Duration, Instant};
9293

9394
use inner::Inner;
9495
use list::{Entry, State};
95-
use node::Node;
96+
use node::{Node, TaskWaiting};
9697

9798
#[cfg(feature = "std")]
9899
use parking::Unparker;
@@ -168,9 +169,6 @@ pub struct Event {
168169
inner: AtomicPtr<Inner>,
169170
}
170171

171-
unsafe impl Send for Event {}
172-
unsafe impl Sync for Event {}
173-
174172
#[cfg(feature = "std")]
175173
impl UnwindSafe for Event {}
176174
#[cfg(feature = "std")]
@@ -210,31 +208,31 @@ impl Event {
210208
let inner = self.inner();
211209

212210
// Try to acquire a lock in the inner list.
213-
let entry = unsafe {
214-
if let Some(mut lock) = (*inner).lock() {
215-
let entry = lock.alloc((*inner).cache_ptr());
216-
lock.insert(entry);
211+
let state = {
212+
let inner = unsafe { &*inner };
213+
if let Some(mut lock) = inner.lock() {
214+
let entry = lock.insert(Entry::new());
217215

218-
entry
216+
ListenerState::HasNode(entry)
219217
} else {
220218
// Push entries into the queue indicating that we want to push a listener.
221219
let (node, entry) = Node::listener();
222-
(*inner).push(node);
220+
inner.push(node);
223221

224222
// Indicate that there are nodes waiting to be notified.
225-
(*inner)
223+
inner
226224
.notified
227225
.compare_exchange(usize::MAX, 0, Ordering::AcqRel, Ordering::Relaxed)
228226
.ok();
229227

230-
entry
228+
ListenerState::Queued(entry)
231229
}
232230
};
233231

234232
// Register the listener.
235233
let listener = EventListener {
236234
inner: unsafe { Arc::clone(&ManuallyDrop::new(Arc::from_raw(inner))) },
237-
entry: Some(entry),
235+
state,
238236
};
239237

240238
// Make sure the listener is registered before whatever happens next.
@@ -529,12 +527,20 @@ pub struct EventListener {
529527
/// A reference to [`Event`]'s inner state.
530528
inner: Arc<Inner>,
531529

532-
/// A pointer to this listener's entry in the linked list.
533-
entry: Option<NonNull<Entry>>,
530+
/// The current state of the listener.
531+
state: ListenerState,
534532
}
535533

536-
unsafe impl Send for EventListener {}
537-
unsafe impl Sync for EventListener {}
534+
enum ListenerState {
535+
/// The listener has a node inside of the linked list.
536+
HasNode(NonZeroUsize),
537+
538+
/// The listener has already been notified and has discarded its entry.
539+
Discarded,
540+
541+
/// The listener has an entry in the queue that may or may not have a task waiting.
542+
Queued(Arc<TaskWaiting>),
543+
}
538544

539545
#[cfg(feature = "std")]
540546
impl UnwindSafe for EventListener {}
@@ -605,11 +611,26 @@ impl EventListener {
605611

606612
fn wait_internal(mut self, deadline: Option<Instant>) -> bool {
607613
// Take out the entry pointer and set it to `None`.
608-
let entry = match self.entry.take() {
609-
None => unreachable!("cannot wait twice on an `EventListener`"),
610-
Some(entry) => entry,
611-
};
612614
let (parker, unparker) = parking::pair();
615+
let entry = match self.state.take() {
616+
ListenerState::HasNode(entry) => entry,
617+
ListenerState::Queued(task_waiting) => {
618+
// This listener is stuck in the backup queue.
619+
// Wait for the task to be notified.
620+
loop {
621+
match task_waiting.status() {
622+
Some(entry_id) => break entry_id,
623+
None => {
624+
// Register a task and park until it is notified.
625+
task_waiting.register(Task::Thread(unparker.clone()));
626+
627+
parker.park();
628+
}
629+
}
630+
}
631+
}
632+
ListenerState::Discarded => panic!("Cannot wait on a discarded listener"),
633+
};
613634

614635
// Wait for the lock to be available.
615636
let lock = || {
@@ -628,22 +649,15 @@ impl EventListener {
628649

629650
// Set this listener's state to `Waiting`.
630651
{
631-
let e = unsafe { entry.as_ref() };
632-
633-
if e.is_queued() {
634-
// Write a task to be woken once the lock is acquired.
635-
e.write_task(Task::Thread(unparker));
636-
} else {
637-
let mut list = lock();
652+
let mut list = lock();
638653

639-
// If the listener was notified, we're done.
640-
match e.state().replace(State::Notified(false)) {
641-
State::Notified(_) => {
642-
list.remove(entry, self.inner.cache_ptr());
643-
return true;
644-
}
645-
_ => e.state().set(State::Task(Task::Thread(unparker))),
654+
// If the listener was notified, we're done.
655+
match list.state(entry).replace(State::Notified(false)) {
656+
State::Notified(_) => {
657+
list.remove(entry);
658+
return true;
646659
}
660+
_ => list.state(entry).set(State::Task(Task::Thread(unparker))),
647661
}
648662
}
649663

@@ -658,7 +672,7 @@ impl EventListener {
658672
if now >= deadline {
659673
// Remove the entry and check if notified.
660674
let mut list = lock();
661-
let state = list.remove(entry, self.inner.cache_ptr());
675+
let state = list.remove(entry);
662676
return state.is_notified();
663677
}
664678

@@ -668,17 +682,16 @@ impl EventListener {
668682
}
669683

670684
let mut list = lock();
671-
let e = unsafe { entry.as_ref() };
672685

673686
// Do a dummy replace operation in order to take out the state.
674-
match e.state().replace(State::Notified(false)) {
687+
match list.state(entry).replace(State::Notified(false)) {
675688
State::Notified(_) => {
676689
// If this listener has been notified, remove it from the list and return.
677-
list.remove(entry, self.inner.cache_ptr());
690+
list.remove(entry);
678691
return true;
679692
}
680693
// Otherwise, set the state back to `Waiting`.
681-
state => e.state().set(state),
694+
state => list.state(entry).set(state),
682695
}
683696
}
684697
}
@@ -706,10 +719,10 @@ impl EventListener {
706719
/// ```
707720
pub fn discard(mut self) -> bool {
708721
// If this listener has never picked up a notification...
709-
if let Some(entry) = self.entry.take() {
722+
if let ListenerState::HasNode(entry) = self.state.take() {
710723
// Remove the listener from the list and return `true` if it was notified.
711724
if let Some(mut lock) = self.inner.lock() {
712-
let state = lock.remove(entry, self.inner.cache_ptr());
725+
let state = lock.remove(entry);
713726

714727
if let State::Notified(_) = state {
715728
return true;
@@ -772,6 +785,30 @@ impl Future for EventListener {
772785

773786
#[allow(unreachable_patterns)]
774787
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
788+
let entry = match self.state {
789+
ListenerState::Discarded => {
790+
unreachable!("cannot poll a completed `EventListener` future")
791+
}
792+
ListenerState::HasNode(ref entry) => *entry,
793+
ListenerState::Queued(ref task_waiting) => {
794+
loop {
795+
// See if the task waiting has been completed.
796+
match task_waiting.status() {
797+
Some(entry_id) => {
798+
self.state = ListenerState::HasNode(entry_id);
799+
break entry_id;
800+
}
801+
None => {
802+
// If not, wait for it to complete.
803+
task_waiting.register(Task::Waker(cx.waker().clone()));
804+
if task_waiting.status().is_none() {
805+
return Poll::Pending;
806+
}
807+
}
808+
}
809+
}
810+
}
811+
};
775812
let mut list = match self.inner.lock() {
776813
Some(list) => list,
777814
None => {
@@ -787,20 +824,15 @@ impl Future for EventListener {
787824
}
788825
}
789826
};
790-
791-
let entry = match self.entry {
792-
None => unreachable!("cannot poll a completed `EventListener` future"),
793-
Some(entry) => entry,
794-
};
795-
let state = unsafe { entry.as_ref().state() };
827+
let state = list.state(entry);
796828

797829
// Do a dummy replace operation in order to take out the state.
798830
match state.replace(State::Notified(false)) {
799831
State::Notified(_) => {
800832
// If this listener has been notified, remove it from the list and return.
801-
list.remove(entry, self.inner.cache_ptr());
833+
list.remove(entry);
802834
drop(list);
803-
self.entry = None;
835+
self.state = ListenerState::Discarded;
804836
return Poll::Ready(());
805837
}
806838
State::Created => {
@@ -827,12 +859,11 @@ impl Future for EventListener {
827859
impl Drop for EventListener {
828860
fn drop(&mut self) {
829861
// If this listener has never picked up a notification...
830-
if let Some(entry) = self.entry.take() {
862+
if let ListenerState::HasNode(entry) = self.state.take() {
831863
match self.inner.lock() {
832864
Some(mut list) => {
833865
// But if a notification was delivered to it...
834-
if let State::Notified(additional) = list.remove(entry, self.inner.cache_ptr())
835-
{
866+
if let State::Notified(additional) = list.remove(entry) {
836867
// Then pass it on to another active listener.
837868
list.notify(1, additional);
838869
}
@@ -849,6 +880,12 @@ impl Drop for EventListener {
849880
}
850881
}
851882

883+
impl ListenerState {
884+
fn take(&mut self) -> Self {
885+
mem::replace(self, ListenerState::Discarded)
886+
}
887+
}
888+
852889
/// Equivalent to `atomic::fence(Ordering::SeqCst)`, but in some cases faster.
853890
#[inline]
854891
fn full_fence() {
@@ -877,17 +914,6 @@ fn full_fence() {
877914
}
878915
}
879916

880-
/// Indicate that we're using spin-based contention and that we should yield the CPU.
881-
#[inline]
882-
fn yield_now() {
883-
#[cfg(feature = "std")]
884-
std::thread::yield_now();
885-
886-
#[cfg(not(feature = "std"))]
887-
#[allow(deprecated)]
888-
sync::atomic::spin_loop_hint();
889-
}
890-
891917
#[cfg(any(feature = "__test", test))]
892918
impl Event {
893919
/// Locks the event.

0 commit comments

Comments
 (0)