149
149
#[ cfg( all( test, not( target_os = "emscripten" ) ) ) ]
150
150
mod tests;
151
151
152
+ mod parker;
153
+
152
154
use crate :: any:: Any ;
153
155
use crate :: cell:: UnsafeCell ;
154
156
use crate :: ffi:: { CStr , CString } ;
@@ -159,15 +161,14 @@ use crate::num::NonZeroU64;
159
161
use crate :: panic;
160
162
use crate :: panicking;
161
163
use crate :: str;
162
- use crate :: sync:: atomic:: AtomicUsize ;
163
- use crate :: sync:: atomic:: Ordering :: SeqCst ;
164
- use crate :: sync:: { Arc , Condvar , Mutex } ;
164
+ use crate :: sync:: Arc ;
165
165
use crate :: sys:: thread as imp;
166
166
use crate :: sys_common:: mutex;
167
167
use crate :: sys_common:: thread;
168
168
use crate :: sys_common:: thread_info;
169
169
use crate :: sys_common:: { AsInner , IntoInner } ;
170
170
use crate :: time:: Duration ;
171
+ use parker:: Parker ;
171
172
172
173
////////////////////////////////////////////////////////////////////////////////
173
174
// Thread-local storage
@@ -667,6 +668,8 @@ pub fn current() -> Thread {
667
668
///
668
669
/// [`channel`]: crate::sync::mpsc
669
670
/// [`join`]: JoinHandle::join
671
+ /// [`Condvar`]: crate::sync::Condvar
672
+ /// [`Mutex`]: crate::sync::Mutex
670
673
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
671
674
pub fn yield_now ( ) {
672
675
imp:: Thread :: yield_now ( )
@@ -712,6 +715,8 @@ pub fn yield_now() {
712
715
/// panic!()
713
716
/// }
714
717
/// ```
718
+ ///
719
+ /// [Mutex]: crate::sync::Mutex
715
720
#[ inline]
716
721
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
717
722
pub fn panicking ( ) -> bool {
@@ -779,11 +784,6 @@ pub fn sleep(dur: Duration) {
779
784
imp:: Thread :: sleep ( dur)
780
785
}
781
786
782
- // constants for park/unpark
783
- const EMPTY : usize = 0 ;
784
- const PARKED : usize = 1 ;
785
- const NOTIFIED : usize = 2 ;
786
-
787
787
/// Blocks unless or until the current thread's token is made available.
788
788
///
789
789
/// A call to `park` does not guarantee that the thread will remain parked
@@ -870,45 +870,11 @@ const NOTIFIED: usize = 2;
870
870
///
871
871
/// [`unpark`]: Thread::unpark
872
872
/// [`thread::park_timeout`]: park_timeout
873
- //
874
- // The implementation currently uses the trivial strategy of a Mutex+Condvar
875
- // with wakeup flag, which does not actually allow spurious wakeups. In the
876
- // future, this will be implemented in a more efficient way, perhaps along the lines of
877
- // http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp
878
- // or futuxes, and in either case may allow spurious wakeups.
879
873
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
880
874
pub fn park ( ) {
881
- let thread = current ( ) ;
882
-
883
- // If we were previously notified then we consume this notification and
884
- // return quickly.
885
- if thread. inner . state . compare_exchange ( NOTIFIED , EMPTY , SeqCst , SeqCst ) . is_ok ( ) {
886
- return ;
887
- }
888
-
889
- // Otherwise we need to coordinate going to sleep
890
- let mut m = thread. inner . lock . lock ( ) . unwrap ( ) ;
891
- match thread. inner . state . compare_exchange ( EMPTY , PARKED , SeqCst , SeqCst ) {
892
- Ok ( _) => { }
893
- Err ( NOTIFIED ) => {
894
- // We must read here, even though we know it will be `NOTIFIED`.
895
- // This is because `unpark` may have been called again since we read
896
- // `NOTIFIED` in the `compare_exchange` above. We must perform an
897
- // acquire operation that synchronizes with that `unpark` to observe
898
- // any writes it made before the call to unpark. To do that we must
899
- // read from the write it made to `state`.
900
- let old = thread. inner . state . swap ( EMPTY , SeqCst ) ;
901
- assert_eq ! ( old, NOTIFIED , "park state changed unexpectedly" ) ;
902
- return ;
903
- } // should consume this notification, so prohibit spurious wakeups in next park.
904
- Err ( _) => panic ! ( "inconsistent park state" ) ,
905
- }
906
- loop {
907
- m = thread. inner . cvar . wait ( m) . unwrap ( ) ;
908
- match thread. inner . state . compare_exchange ( NOTIFIED , EMPTY , SeqCst , SeqCst ) {
909
- Ok ( _) => return , // got a notification
910
- Err ( _) => { } // spurious wakeup, go back to sleep
911
- }
875
+ // SAFETY: park_timeout is called on the parker owned by this thread.
876
+ unsafe {
877
+ current ( ) . inner . parker . park ( ) ;
912
878
}
913
879
}
914
880
@@ -970,35 +936,9 @@ pub fn park_timeout_ms(ms: u32) {
970
936
/// ```
971
937
#[ stable( feature = "park_timeout" , since = "1.4.0" ) ]
972
938
pub fn park_timeout ( dur : Duration ) {
973
- let thread = current ( ) ;
974
-
975
- // Like `park` above we have a fast path for an already-notified thread, and
976
- // afterwards we start coordinating for a sleep.
977
- // return quickly.
978
- if thread. inner . state . compare_exchange ( NOTIFIED , EMPTY , SeqCst , SeqCst ) . is_ok ( ) {
979
- return ;
980
- }
981
- let m = thread. inner . lock . lock ( ) . unwrap ( ) ;
982
- match thread. inner . state . compare_exchange ( EMPTY , PARKED , SeqCst , SeqCst ) {
983
- Ok ( _) => { }
984
- Err ( NOTIFIED ) => {
985
- // We must read again here, see `park`.
986
- let old = thread. inner . state . swap ( EMPTY , SeqCst ) ;
987
- assert_eq ! ( old, NOTIFIED , "park state changed unexpectedly" ) ;
988
- return ;
989
- } // should consume this notification, so prohibit spurious wakeups in next park.
990
- Err ( _) => panic ! ( "inconsistent park_timeout state" ) ,
991
- }
992
-
993
- // Wait with a timeout, and if we spuriously wake up or otherwise wake up
994
- // from a notification we just want to unconditionally set the state back to
995
- // empty, either consuming a notification or un-flagging ourselves as
996
- // parked.
997
- let ( _m, _result) = thread. inner . cvar . wait_timeout ( m, dur) . unwrap ( ) ;
998
- match thread. inner . state . swap ( EMPTY , SeqCst ) {
999
- NOTIFIED => { } // got a notification, hurray!
1000
- PARKED => { } // no notification, alas
1001
- n => panic ! ( "inconsistent park_timeout state: {}" , n) ,
939
+ // SAFETY: park_timeout is called on the parker owned by this thread.
940
+ unsafe {
941
+ current ( ) . inner . parker . park_timeout ( dur) ;
1002
942
}
1003
943
}
1004
944
@@ -1077,11 +1017,7 @@ impl ThreadId {
1077
1017
struct Inner {
1078
1018
name : Option < CString > , // Guaranteed to be UTF-8
1079
1019
id : ThreadId ,
1080
-
1081
- // state for thread park/unpark
1082
- state : AtomicUsize ,
1083
- lock : Mutex < ( ) > ,
1084
- cvar : Condvar ,
1020
+ parker : Parker ,
1085
1021
}
1086
1022
1087
1023
#[ derive( Clone ) ]
@@ -1115,13 +1051,7 @@ impl Thread {
1115
1051
let cname =
1116
1052
name. map ( |n| CString :: new ( n) . expect ( "thread name may not contain interior null bytes" ) ) ;
1117
1053
Thread {
1118
- inner : Arc :: new ( Inner {
1119
- name : cname,
1120
- id : ThreadId :: new ( ) ,
1121
- state : AtomicUsize :: new ( EMPTY ) ,
1122
- lock : Mutex :: new ( ( ) ) ,
1123
- cvar : Condvar :: new ( ) ,
1124
- } ) ,
1054
+ inner : Arc :: new ( Inner { name : cname, id : ThreadId :: new ( ) , parker : Parker :: new ( ) } ) ,
1125
1055
}
1126
1056
}
1127
1057
@@ -1157,32 +1087,7 @@ impl Thread {
1157
1087
/// ```
1158
1088
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
1159
1089
pub fn unpark ( & self ) {
1160
- // To ensure the unparked thread will observe any writes we made
1161
- // before this call, we must perform a release operation that `park`
1162
- // can synchronize with. To do that we must write `NOTIFIED` even if
1163
- // `state` is already `NOTIFIED`. That is why this must be a swap
1164
- // rather than a compare-and-swap that returns if it reads `NOTIFIED`
1165
- // on failure.
1166
- match self . inner . state . swap ( NOTIFIED , SeqCst ) {
1167
- EMPTY => return , // no one was waiting
1168
- NOTIFIED => return , // already unparked
1169
- PARKED => { } // gotta go wake someone up
1170
- _ => panic ! ( "inconsistent state in unpark" ) ,
1171
- }
1172
-
1173
- // There is a period between when the parked thread sets `state` to
1174
- // `PARKED` (or last checked `state` in the case of a spurious wake
1175
- // up) and when it actually waits on `cvar`. If we were to notify
1176
- // during this period it would be ignored and then when the parked
1177
- // thread went to sleep it would never wake up. Fortunately, it has
1178
- // `lock` locked at this stage so we can acquire `lock` to wait until
1179
- // it is ready to receive the notification.
1180
- //
1181
- // Releasing `lock` before the call to `notify_one` means that when the
1182
- // parked thread wakes it doesn't get woken only to have to wait for us
1183
- // to release `lock`.
1184
- drop ( self . inner . lock . lock ( ) . unwrap ( ) ) ;
1185
- self . inner . cvar . notify_one ( )
1090
+ self . inner . parker . unpark ( ) ;
1186
1091
}
1187
1092
1188
1093
/// Gets the thread's unique identifier.
0 commit comments