@@ -15,11 +15,12 @@ use std::{
1515 fmt,
1616 pin:: pin,
1717 sync:: {
18- atomic:: { AtomicU16 , Ordering } ,
19- Arc , Condvar , Mutex ,
18+ atomic:: { AtomicU16 , AtomicU8 , Ordering } ,
19+ Arc ,
2020 } ,
2121 time:: { Duration , Instant } ,
2222} ;
23+ use parking_lot:: { Condvar , Mutex } ;
2324use tokio:: sync:: Notify as AsyncNotify ;
2425
2526// Error types
@@ -103,23 +104,32 @@ impl fmt::Debug for NotifyError {
103104
104105impl std:: error:: Error for NotifyError { }
105106
106- // State values for the sync Mutex<u8> .
107+ // State values for the AtomicU8 flag .
107108const UNSET : u8 = 0 ;
108109const OK : u8 = 1 ;
109110const ERR : u8 = 2 ;
110111
111112// Replace event_listener::Event + AtomicU8 with:
112- // - std Mutex<u8 > + Condvar → sync paths (wait / wait_deadline / wait_timeout)
113- // - tokio::sync::Notify → async path (wait_async), which is cancellation-safe
113+ // - AtomicU8 flag + Mutex<() > + Condvar → sync paths (wait / wait_deadline / wait_timeout)
114+ // - tokio::sync::Notify → async path (wait_async), cancellation-safe
114115//
115- // The original event_listener approach allocated a linked-list node (EventListener)
116- // on every wait() call and paid intrusive-list register/deregister cost on the hot path.
117- // tokio::time::timeout() was also able to cancel wait_async() while its inner
118- // spawn_blocking task continued running, eventually consuming the next real notification
119- // (a "zombie" thread bug). tokio::sync::Notify avoids this: a dropped Notified future
120- // returns its permit to the pool so no notification is lost.
116+ // Hot-path design (sticky notification):
117+ //
118+ // notify(): flag.fetch_update(UNSET|OK → OK) [atomic, no lock]
119+ // + brief lock()/unlock() to prevent lost wakeup
120+ // + cv.notify_one() + async_notify.notify_one()
121+ //
122+ // wait(): flag.compare_exchange(OK → UNSET) [atomic, no lock] ← hot path returns here
123+ // If UNSET: lock() → re-check flag → cv.wait() if still UNSET
124+ //
125+ // The mutex is only taken on the slow (blocking) path and briefly in notify() to
126+ // fence against the lost-wakeup window. No heap allocation on either path.
121127struct EventInner {
122- state : Mutex < u8 > ,
128+ /// Primary state. Written atomically on hot path; also read under `mutex`
129+ /// on the slow path to re-check after wakeup.
130+ flag : AtomicU8 ,
131+ /// Used exclusively as the condvar lock. Carries no state of its own.
132+ mutex : Mutex < ( ) > ,
123133 cv : Condvar ,
124134 async_notify : AsyncNotify ,
125135 notifiers : AtomicU16 ,
@@ -132,7 +142,8 @@ struct EventInner {
132142/// call returns immediately.
133143pub fn new ( ) -> ( Notifier , Waiter ) {
134144 let inner = Arc :: new ( EventInner {
135- state : Mutex :: new ( UNSET ) ,
145+ flag : AtomicU8 :: new ( UNSET ) ,
146+ mutex : Mutex :: new ( ( ) ) ,
136147 cv : Condvar :: new ( ) ,
137148 async_notify : AsyncNotify :: new ( ) ,
138149 notifiers : AtomicU16 :: new ( 1 ) ,
@@ -148,13 +159,24 @@ pub struct Notifier(Arc<EventInner>);
148159impl Notifier {
149160 #[ inline]
150161 pub fn notify ( & self ) -> Result < ( ) , NotifyError > {
151- let mut state = self . 0 . state . lock ( ) . unwrap ( ) ;
152- if * state == ERR {
153- return Err ( NotifyError ) ;
154- }
155- * state = OK ;
162+ // Atomically set flag to OK unless it is already ERR (terminal state).
163+ // fetch_update returns Err if the closure returns None (i.e. flag == ERR).
164+ self . 0
165+ . flag
166+ . fetch_update ( Ordering :: Release , Ordering :: Relaxed , |f| {
167+ if f == ERR { None } else { Some ( OK ) }
168+ } )
169+ . map_err ( |_| NotifyError ) ?;
170+
171+ // Brief lock/unlock to close the lost-wakeup window.
172+ //
173+ // A waiter on the slow path does: CAS-fails → lock() → re-check flag → cv.wait().
174+ // By taking the lock here we ensure either:
175+ // (a) waiter has not yet called lock() → it will see OK on its flag re-check, or
176+ // (b) waiter is already in cv.wait() → cv.notify_one() below will wake it.
177+ drop ( self . 0 . mutex . lock ( ) ) ;
178+
156179 self . 0 . cv . notify_one ( ) ;
157- drop ( state) ; // release before async notify to minimise lock contention
158180 self . 0 . async_notify . notify_one ( ) ;
159181 Ok ( ( ) )
160182 }
@@ -172,12 +194,11 @@ impl Drop for Notifier {
172194 fn drop ( & mut self ) {
173195 let n = self . 0 . notifiers . fetch_sub ( 1 , Ordering :: SeqCst ) ;
174196 if n == 1 {
175- // Last notifier dropped — signal error to all waiters.
176- let mut state = self . 0 . state . lock ( ) . unwrap ( ) ;
177- * state = ERR ;
197+ // Last notifier dropped — set ERR and wake all waiters.
198+ self . 0 . flag . store ( ERR , Ordering :: Release ) ;
199+ drop ( self . 0 . mutex . lock ( ) ) ; // lost-wakeup fence
178200 self . 0 . cv . notify_all ( ) ;
179- drop ( state) ;
180- self . 0 . async_notify . notify_waiters ( ) ; // wake all async waiters
201+ self . 0 . async_notify . notify_waiters ( ) ;
181202 }
182203 }
183204}
@@ -188,12 +209,29 @@ pub struct Waiter(Arc<EventInner>);
188209impl Waiter {
189210 #[ inline]
190211 pub fn wait ( & self ) -> Result < ( ) , WaitError > {
191- let mut state = self . 0 . state . lock ( ) . unwrap ( ) ;
212+ // Hot path: consume a sticky OK notification without taking the lock.
213+ match self
214+ . 0
215+ . flag
216+ . compare_exchange ( OK , UNSET , Ordering :: Acquire , Ordering :: Relaxed )
217+ {
218+ Ok ( _) => return Ok ( ( ) ) ,
219+ Err ( ERR ) => return Err ( WaitError ) ,
220+ Err ( _) => { } // UNSET → fall through to slow path
221+ }
222+
223+ // Slow path: take the lock and block on the condvar.
224+ let mut guard = self . 0 . mutex . lock ( ) ;
192225 loop {
193- match * state {
194- OK => { * state = UNSET ; return Ok ( ( ) ) ; }
226+ match self . 0 . flag . load ( Ordering :: Acquire ) {
227+ OK => {
228+ self . 0 . flag . store ( UNSET , Ordering :: Release ) ;
229+ return Ok ( ( ) ) ;
230+ }
195231 ERR => return Err ( WaitError ) ,
196- _ => { state = self . 0 . cv . wait ( state) . unwrap ( ) ; }
232+ _ => {
233+ self . 0 . cv . wait ( & mut guard) ;
234+ }
197235 }
198236 }
199237 }
@@ -206,19 +244,21 @@ impl Waiter {
206244 #[ inline]
207245 pub async fn wait_async ( & self ) -> Result < ( ) , WaitError > {
208246 loop {
209- // Create and enable the Notified future BEFORE checking state .
210- // enable() subscribes to the Notify and consumes any stored permit ,
211- // so a notify_one() that fires between here and the .await is not lost .
247+ // Subscribe BEFORE checking the flag .
248+ // enable() ensures that any notify_one() fired after this point wakes us ,
249+ // and consumes any stored permit if notify_one() already fired .
212250 let mut notified = pin ! ( self . 0 . async_notify. notified( ) ) ;
213251 notified. as_mut ( ) . enable ( ) ;
214252
253+ // Hot path: consume sticky OK without lock.
254+ match self
255+ . 0
256+ . flag
257+ . compare_exchange ( OK , UNSET , Ordering :: Acquire , Ordering :: Relaxed )
215258 {
216- let mut state = self . 0 . state . lock ( ) . unwrap ( ) ;
217- match * state {
218- OK => { * state = UNSET ; return Ok ( ( ) ) ; }
219- ERR => return Err ( WaitError ) ,
220- _ => { }
221- }
259+ Ok ( _) => return Ok ( ( ) ) ,
260+ Err ( ERR ) => return Err ( WaitError ) ,
261+ Err ( _) => { }
222262 }
223263
224264 // No pending notification — await (cancellation-safe).
@@ -230,23 +270,38 @@ impl Waiter {
230270
231271 #[ inline]
232272 pub fn wait_deadline ( & self , deadline : Instant ) -> Result < ( ) , WaitDeadlineError > {
233- let mut state = self . 0 . state . lock ( ) . unwrap ( ) ;
273+ // Hot path.
274+ match self
275+ . 0
276+ . flag
277+ . compare_exchange ( OK , UNSET , Ordering :: Acquire , Ordering :: Relaxed )
278+ {
279+ Ok ( _) => return Ok ( ( ) ) ,
280+ Err ( ERR ) => return Err ( WaitDeadlineError :: WaitError ) ,
281+ Err ( _) => { }
282+ }
283+
284+ // Slow path.
285+ let mut guard = self . 0 . mutex . lock ( ) ;
234286 loop {
235- match * state {
236- OK => { * state = UNSET ; return Ok ( ( ) ) ; }
287+ match self . 0 . flag . load ( Ordering :: Acquire ) {
288+ OK => {
289+ self . 0 . flag . store ( UNSET , Ordering :: Release ) ;
290+ return Ok ( ( ) ) ;
291+ }
237292 ERR => return Err ( WaitDeadlineError :: WaitError ) ,
238293 _ => {
239294 let now = Instant :: now ( ) ;
240295 if now >= deadline {
241296 return Err ( WaitDeadlineError :: Deadline ) ;
242297 }
243- let ( new_state , timeout ) = self . 0 . cv
244- . wait_timeout ( state , deadline - now )
245- . unwrap ( ) ;
246- state = new_state ;
247- if timeout . timed_out ( ) {
248- return match * state {
249- OK => { * state = UNSET ; Ok ( ( ) ) }
298+ let timed_out = self . 0 . cv . wait_for ( & mut guard , deadline - now ) . timed_out ( ) ;
299+ if timed_out {
300+ return match self . 0 . flag . load ( Ordering :: Acquire ) {
301+ OK => {
302+ self . 0 . flag . store ( UNSET , Ordering :: Release ) ;
303+ Ok ( ( ) )
304+ }
250305 ERR => Err ( WaitDeadlineError :: WaitError ) ,
251306 _ => Err ( WaitDeadlineError :: Deadline ) ,
252307 } ;
@@ -278,10 +333,9 @@ impl Drop for Waiter {
278333 fn drop ( & mut self ) {
279334 let n = self . 0 . waiters . fetch_sub ( 1 , Ordering :: SeqCst ) ;
280335 if n == 1 {
281- let mut state = self . 0 . state . lock ( ) . unwrap ( ) ;
282- * state = ERR ;
336+ self . 0 . flag . store ( ERR , Ordering :: Release ) ;
337+ drop ( self . 0 . mutex . lock ( ) ) ; // lost-wakeup fence
283338 self . 0 . cv . notify_all ( ) ;
284- drop ( state) ;
285339 self . 0 . async_notify . notify_waiters ( ) ;
286340 }
287341 }
0 commit comments