25
25
//! future::block_on(ex.run(task));
26
26
//! ```
27
27
28
- #![ warn( missing_docs, missing_debug_implementations, rust_2018_idioms) ]
28
+ #![ warn(
29
+ missing_docs,
30
+ missing_debug_implementations,
31
+ rust_2018_idioms,
32
+ clippy:: undocumented_unsafe_blocks
33
+ ) ]
29
34
#![ doc(
30
35
html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
31
36
) ]
@@ -37,11 +42,10 @@ use std::fmt;
37
42
use std:: marker:: PhantomData ;
38
43
use std:: panic:: { RefUnwindSafe , UnwindSafe } ;
39
44
use std:: rc:: Rc ;
40
- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
45
+ use std:: sync:: atomic:: { AtomicBool , AtomicPtr , Ordering } ;
41
46
use std:: sync:: { Arc , Mutex , RwLock , TryLockError } ;
42
47
use std:: task:: { Poll , Waker } ;
43
48
44
- use async_lock:: OnceCell ;
45
49
use async_task:: { Builder , Runnable } ;
46
50
use concurrent_queue:: ConcurrentQueue ;
47
51
use futures_lite:: { future, prelude:: * } ;
@@ -76,13 +80,15 @@ pub use async_task::Task;
76
80
/// ```
77
81
pub struct Executor < ' a > {
78
82
/// The executor state.
79
- state : OnceCell < Arc < State > > ,
83
+ state : AtomicPtr < State > ,
80
84
81
85
/// Makes the `'a` lifetime invariant.
82
86
_marker : PhantomData < std:: cell:: UnsafeCell < & ' a ( ) > > ,
83
87
}
84
88
89
+ // SAFETY: Executor stores no thread local state that can be accessed via other thread.
85
90
unsafe impl Send for Executor < ' _ > { }
91
+ // SAFETY: Executor internally synchronizes all of it's operations internally.
86
92
unsafe impl Sync for Executor < ' _ > { }
87
93
88
94
impl UnwindSafe for Executor < ' _ > { }
@@ -106,7 +112,7 @@ impl<'a> Executor<'a> {
106
112
/// ```
107
113
pub const fn new ( ) -> Executor < ' a > {
108
114
Executor {
109
- state : OnceCell :: new ( ) ,
115
+ state : AtomicPtr :: new ( std :: ptr :: null_mut ( ) ) ,
110
116
_marker : PhantomData ,
111
117
}
112
118
}
@@ -231,7 +237,7 @@ impl<'a> Executor<'a> {
231
237
// Remove the task from the set of active tasks when the future finishes.
232
238
let entry = active. vacant_entry ( ) ;
233
239
let index = entry. key ( ) ;
234
- let state = self . state ( ) . clone ( ) ;
240
+ let state = self . state_as_arc ( ) ;
235
241
let future = async move {
236
242
let _guard = CallOnDrop ( move || drop ( state. active . lock ( ) . unwrap ( ) . try_remove ( index) ) ) ;
237
243
future. await
@@ -361,7 +367,7 @@ impl<'a> Executor<'a> {
361
367
362
368
/// Returns a function that schedules a runnable task when it gets woken up.
363
369
fn schedule ( & self ) -> impl Fn ( Runnable ) + Send + Sync + ' static {
364
- let state = self . state ( ) . clone ( ) ;
370
+ let state = self . state_as_arc ( ) ;
365
371
366
372
// TODO: If possible, push into the current local queue and notify the ticker.
367
373
move |runnable| {
@@ -370,34 +376,73 @@ impl<'a> Executor<'a> {
370
376
}
371
377
}
372
378
373
- /// Returns a reference to the inner state.
374
- fn state ( & self ) -> & Arc < State > {
375
- #[ cfg( not( target_family = "wasm" ) ) ]
376
- {
377
- return self . state . get_or_init_blocking ( || Arc :: new ( State :: new ( ) ) ) ;
379
+ /// Returns a pointer to the inner state.
380
+ #[ inline]
381
+ fn state_ptr ( & self ) -> * const State {
382
+ #[ cold]
383
+ fn alloc_state ( atomic_ptr : & AtomicPtr < State > ) -> * mut State {
384
+ let state = Arc :: new ( State :: new ( ) ) ;
385
+ // TODO: Switch this to use cast_mut once the MSRV can be bumped past 1.65
386
+ let ptr = Arc :: into_raw ( state) as * mut State ;
387
+ if let Err ( actual) = atomic_ptr. compare_exchange (
388
+ std:: ptr:: null_mut ( ) ,
389
+ ptr,
390
+ Ordering :: AcqRel ,
391
+ Ordering :: Acquire ,
392
+ ) {
393
+ // SAFETY: This was just created from Arc::into_raw.
394
+ drop ( unsafe { Arc :: from_raw ( ptr) } ) ;
395
+ actual
396
+ } else {
397
+ ptr
398
+ }
378
399
}
379
400
380
- // Some projects use this on WASM for some reason. In this case get_or_init_blocking
381
- // doesn't work. Just poll the future once and panic if there is contention.
382
- #[ cfg( target_family = "wasm" ) ]
383
- future:: block_on ( future:: poll_once (
384
- self . state . get_or_init ( || async { Arc :: new ( State :: new ( ) ) } ) ,
385
- ) )
386
- . expect ( "encountered contention on WASM" )
401
+ let mut ptr = self . state . load ( Ordering :: Acquire ) ;
402
+ if ptr. is_null ( ) {
403
+ ptr = alloc_state ( & self . state ) ;
404
+ }
405
+ ptr
406
+ }
407
+
408
+ /// Returns a reference to the inner state.
409
+ #[ inline]
410
+ fn state ( & self ) -> & State {
411
+ // SAFETY: So long as an Executor lives, it's state pointer will always be valid
412
+ // when accessed through state_ptr.
413
+ unsafe { & * self . state_ptr ( ) }
414
+ }
415
+
416
+ // Clones the inner state Arc
417
+ #[ inline]
418
+ fn state_as_arc ( & self ) -> Arc < State > {
419
+ // SAFETY: So long as an Executor lives, it's state pointer will always be a valid
420
+ // Arc when accessed through state_ptr.
421
+ let arc = unsafe { Arc :: from_raw ( self . state_ptr ( ) ) } ;
422
+ let clone = arc. clone ( ) ;
423
+ std:: mem:: forget ( arc) ;
424
+ clone
387
425
}
388
426
}
389
427
390
428
impl Drop for Executor < ' _ > {
391
429
fn drop ( & mut self ) {
392
- if let Some ( state) = self . state . get ( ) {
393
- let mut active = state. active . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
394
- for w in active. drain ( ) {
395
- w. wake ( ) ;
396
- }
397
- drop ( active) ;
430
+ let ptr = * self . state . get_mut ( ) ;
431
+ if ptr. is_null ( ) {
432
+ return ;
433
+ }
434
+
435
+ // SAFETY: As ptr is not null, it was allocated via Arc::new and converted
436
+ // via Arc::into_raw in state_ptr.
437
+ let state = unsafe { Arc :: from_raw ( ptr) } ;
398
438
399
- while state. queue . pop ( ) . is_ok ( ) { }
439
+ let mut active = state. active . lock ( ) . unwrap_or_else ( |e| e. into_inner ( ) ) ;
440
+ for w in active. drain ( ) {
441
+ w. wake ( ) ;
400
442
}
443
+ drop ( active) ;
444
+
445
+ while state. queue . pop ( ) . is_ok ( ) { }
401
446
}
402
447
}
403
448
@@ -718,9 +763,7 @@ impl Sleepers {
718
763
fn update ( & mut self , id : usize , waker : & Waker ) -> bool {
719
764
for item in & mut self . wakers {
720
765
if item. 0 == id {
721
- if !item. 1 . will_wake ( waker) {
722
- item. 1 . clone_from ( waker) ;
723
- }
766
+ item. 1 . clone_from ( waker) ;
724
767
return false ;
725
768
}
726
769
}
@@ -1006,21 +1049,24 @@ fn steal<T>(src: &ConcurrentQueue<T>, dest: &ConcurrentQueue<T>) {
1006
1049
/// Debug implementation for `Executor` and `LocalExecutor`.
1007
1050
fn debug_executor ( executor : & Executor < ' _ > , name : & str , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1008
1051
// Get a reference to the state.
1009
- let state = match executor. state . get ( ) {
1010
- Some ( state) => state,
1011
- None => {
1012
- // The executor has not been initialized.
1013
- struct Uninitialized ;
1014
-
1015
- impl fmt:: Debug for Uninitialized {
1016
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1017
- f. write_str ( "<uninitialized>" )
1018
- }
1052
+ let ptr = executor. state . load ( Ordering :: Acquire ) ;
1053
+ if ptr. is_null ( ) {
1054
+ // The executor has not been initialized.
1055
+ struct Uninitialized ;
1056
+
1057
+ impl fmt:: Debug for Uninitialized {
1058
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1059
+ f. write_str ( "<uninitialized>" )
1019
1060
}
1020
-
1021
- return f. debug_tuple ( name) . field ( & Uninitialized ) . finish ( ) ;
1022
1061
}
1023
- } ;
1062
+
1063
+ return f. debug_tuple ( name) . field ( & Uninitialized ) . finish ( ) ;
1064
+ }
1065
+
1066
+ // SAFETY: If the state pointer is not null, it must have been
1067
+ // allocated properly by Arc::new and converted via Arc::into_raw
1068
+ // in state_ptr.
1069
+ let state = unsafe { & * ptr } ;
1024
1070
1025
1071
/// Debug wrapper for the number of active tasks.
1026
1072
struct ActiveTasks < ' a > ( & ' a Mutex < Slab < Waker > > ) ;
0 commit comments