@@ -9,6 +9,7 @@ use std::ptr;
99use std:: sync:: atomic:: { self , AtomicIsize , AtomicPtr , AtomicUsize , Ordering } ;
1010use std:: sync:: Arc ;
1111
12+ use atomic_maybe_uninit:: PerByteAtomicMaybeUninit ;
1213use crossbeam_epoch:: { self as epoch, Atomic , Owned } ;
1314use crossbeam_utils:: { Backoff , CachePadded } ;
1415
@@ -41,7 +42,7 @@ impl<T> Buffer<T> {
4142
4243 let ptr = Box :: into_raw (
4344 ( 0 ..cap)
44- . map ( |_| MaybeUninit :: < T > :: uninit ( ) )
45+ . map ( |_| PerByteAtomicMaybeUninit :: new ( MaybeUninit :: < T > :: uninit ( ) ) )
4546 . collect :: < Box < [ _ ] > > ( ) ,
4647 )
4748 . cast :: < T > ( ) ;
@@ -68,22 +69,37 @@ impl<T> Buffer<T> {
6869 }
6970
7071 /// Writes `task` into the specified `index`.
71- ///
72- /// This method might be concurrently called with another `read` at the same index, which is
73- /// technically speaking a data race and therefore UB. We should use an atomic store here, but
74- /// that would be more expensive and difficult to implement generically for all types `T`.
75- /// Hence, as a hack, we use a volatile write instead.
7672 unsafe fn write ( & self , index : isize , task : MaybeUninit < T > ) {
73+ atomic_maybe_uninit:: cfg_has_atomic_memcpy! {
74+ if cfg!( not( any( miri, crossbeam_sanitize_any) ) ) {
75+ unsafe {
76+ ( * self . at( index) . cast:: <PerByteAtomicMaybeUninit <T >>( ) ) . store( task, Ordering :: Relaxed ) ;
77+ }
78+ return ;
79+ }
80+ }
81+ // This method might be concurrently called with another `read` at the same index, which is
82+ // technically speaking a data race and therefore UB. We should use an atomic store here
83+ // like the code above, but inline assembly is not stable on some tier 2/3 targets and
84+ // unsupported in Miri/Sanitizer. Hence, as a hack, we use a volatile write instead.
85+ // See also https://github.com/rust-lang/rfcs/pull/3301.
7786 unsafe { ptr:: write_volatile ( self . at ( index) . cast :: < MaybeUninit < T > > ( ) , task) }
7887 }
7988
8089 /// Reads a task from the specified `index`.
81- ///
82- /// This method might be concurrently called with another `write` at the same index, which is
83- /// technically speaking a data race and therefore UB. We should use an atomic load here, but
84- /// that would be more expensive and difficult to implement generically for all types `T`.
85- /// Hence, as a hack, we use a volatile load instead.
8690 unsafe fn read ( & self , index : isize ) -> MaybeUninit < T > {
91+ atomic_maybe_uninit:: cfg_has_atomic_memcpy! {
92+ if cfg!( not( any( miri, crossbeam_sanitize_any) ) ) {
93+ unsafe {
94+ return ( * self . at( index) . cast:: <PerByteAtomicMaybeUninit <T >>( ) ) . load( Ordering :: Relaxed ) ;
95+ }
96+ }
97+ }
98+ // This method might be concurrently called with another `write` at the same index, which is
99+ // technically speaking a data race and therefore UB. We should use an atomic load here
100+ // like the code above, but inline assembly is not stable on some tier 2/3 targets and
101+ // unsupported in Miri/Sanitizer. Hence, as a hack, we use a volatile write instead.
102+ // See also https://github.com/rust-lang/rfcs/pull/3301.
87103 unsafe { ptr:: read_volatile ( self . at ( index) . cast :: < MaybeUninit < T > > ( ) ) }
88104 }
89105}
0 commit comments