Skip to content

Commit c9dba81

Browse files
author
Stjepan Glavina
authored
Merge pull request #14 from cynecx/fix-ub
Fix UB because of overlapping mutable reference
2 parents 9ce9fe3 + c673ae6 commit c9dba81

File tree

1 file changed

+29
-23
lines changed

1 file changed

+29
-23
lines changed

src/lib.rs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ struct Inner {
8686

8787
/// A linked list holding registered listeners.
8888
list: Spinlock<List>,
89+
90+
/// A single cached list entry to avoid allocations on the fast path of the insertion.
91+
cache: UnsafeCell<Entry>,
8992
}
9093

9194
impl Inner {
@@ -96,6 +99,12 @@ impl Inner {
9699
guard: self.list.lock(),
97100
}
98101
}
102+
103+
/// Returns the pointer to the single cached list entry.
104+
#[inline(always)]
105+
fn cache_ptr(&self) -> NonNull<Entry> {
106+
unsafe { NonNull::new_unchecked(self.cache.get()) }
107+
}
99108
}
100109

101110
/// A synchronization primitive for notifying async tasks and threads.
@@ -166,7 +175,7 @@ impl Event {
166175
let inner = self.inner();
167176
let listener = EventListener {
168177
inner: unsafe { Arc::clone(&ManuallyDrop::new(Arc::from_raw(inner))) },
169-
entry: Some(inner.lock().insert()),
178+
entry: Some(inner.lock().insert(inner.cache_ptr())),
170179
};
171180

172181
// Make sure the listener is registered before whatever happens next.
@@ -372,11 +381,11 @@ impl Event {
372381
len: 0,
373382
notified: 0,
374383
cache_used: false,
375-
cache: UnsafeCell::new(Entry {
376-
state: Cell::new(State::Created),
377-
prev: Cell::new(None),
378-
next: Cell::new(None),
379-
}),
384+
}),
385+
cache: UnsafeCell::new(Entry {
386+
state: Cell::new(State::Created),
387+
prev: Cell::new(None),
388+
next: Cell::new(None),
380389
}),
381390
});
382391
// Convert the heap-allocated state into a raw pointer.
@@ -563,7 +572,7 @@ impl EventListener {
563572
match e.state.replace(State::Notified(false)) {
564573
State::Notified(_) => {
565574
// If this listener has been notified, remove it from the list and return.
566-
list.remove(entry);
575+
list.remove(entry, self.inner.cache_ptr());
567576
return true;
568577
}
569578
// Otherwise, set the state to `Waiting`.
@@ -581,11 +590,11 @@ impl EventListener {
581590
let now = Instant::now();
582591
if now >= deadline {
583592
// Remove the entry and check if notified.
584-
if self.inner.lock().remove(entry).is_notified() {
585-
return true;
586-
} else {
587-
return false;
588-
}
593+
return self
594+
.inner
595+
.lock()
596+
.remove(entry, self.inner.cache_ptr())
597+
.is_notified();
589598
}
590599

591600
// Park until the deadline.
@@ -600,7 +609,7 @@ impl EventListener {
600609
match e.state.replace(State::Notified(false)) {
601610
State::Notified(_) => {
602611
// If this listener has been notified, remove it from the list and return.
603-
list.remove(entry);
612+
list.remove(entry, self.inner.cache_ptr());
604613
return true;
605614
}
606615
// Otherwise, set the state back to `Waiting`.
@@ -632,7 +641,7 @@ impl Future for EventListener {
632641
match state.replace(State::Notified(false)) {
633642
State::Notified(_) => {
634643
// If this listener has been notified, remove it from the list and return.
635-
list.remove(entry);
644+
list.remove(entry, self.inner.cache_ptr());
636645
drop(list);
637646
self.entry = None;
638647
return Poll::Ready(());
@@ -665,7 +674,7 @@ impl Drop for EventListener {
665674
let mut list = self.inner.lock();
666675

667676
// But if a notification was delivered to it...
668-
if let State::Notified(additional) = list.remove(entry) {
677+
if let State::Notified(additional) = list.remove(entry, self.inner.cache_ptr()) {
669678
// Then pass it on to another active listener.
670679
if additional {
671680
list.notify_additional(1);
@@ -776,14 +785,11 @@ struct List {
776785

777786
/// Whether the cached entry is used.
778787
cache_used: bool,
779-
780-
/// A single cached entry to avoid allocations on the fast path.
781-
cache: UnsafeCell<Entry>,
782788
}
783789

784790
impl List {
785791
/// Inserts a new entry into the list.
786-
fn insert(&mut self) -> NonNull<Entry> {
792+
fn insert(&mut self, cache: NonNull<Entry>) -> NonNull<Entry> {
787793
unsafe {
788794
let entry = Entry {
789795
state: Cell::new(State::Created),
@@ -797,8 +803,8 @@ impl List {
797803
} else {
798804
// No need to allocate - we can use the cached entry.
799805
self.cache_used = true;
800-
*self.cache.get() = entry;
801-
NonNull::new_unchecked(self.cache.get())
806+
cache.as_ptr().write(entry);
807+
cache
802808
};
803809

804810
// Replace the tail with the new entry.
@@ -820,7 +826,7 @@ impl List {
820826
}
821827

822828
/// Removes an entry from the list and returns its state.
823-
fn remove(&mut self, entry: NonNull<Entry>) -> State {
829+
fn remove(&mut self, entry: NonNull<Entry>, cache: NonNull<Entry>) -> State {
824830
unsafe {
825831
let prev = entry.as_ref().prev.get();
826832
let next = entry.as_ref().next.get();
@@ -843,7 +849,7 @@ impl List {
843849
}
844850

845851
// Extract the state.
846-
let state = if ptr::eq(entry.as_ptr(), self.cache.get()) {
852+
let state = if ptr::eq(entry.as_ptr(), cache.as_ptr()) {
847853
// Free the cached entry.
848854
self.cache_used = false;
849855
entry.as_ref().state.replace(State::Created)

0 commit comments

Comments
 (0)