149149#[ cfg( all( test, not( target_os = "emscripten" ) ) ) ]
150150mod tests;
151151
152+ mod parker;
153+
152154use crate :: any:: Any ;
153155use crate :: cell:: UnsafeCell ;
154156use crate :: ffi:: { CStr , CString } ;
@@ -159,15 +161,14 @@ use crate::num::NonZeroU64;
159161use crate :: panic;
160162use crate :: panicking;
161163use 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 ;
165165use crate :: sys:: thread as imp;
166166use crate :: sys_common:: mutex;
167167use crate :: sys_common:: thread;
168168use crate :: sys_common:: thread_info;
169169use crate :: sys_common:: { AsInner , IntoInner } ;
170170use crate :: time:: Duration ;
171+ use parker:: Parker ;
171172
172173////////////////////////////////////////////////////////////////////////////////
173174// Thread-local storage
@@ -667,6 +668,8 @@ pub fn current() -> Thread {
667668///
668669/// [`channel`]: crate::sync::mpsc
669670/// [`join`]: JoinHandle::join
671+ /// [`Condvar`]: crate::sync::Condvar
672+ /// [`Mutex`]: crate::sync::Mutex
670673#[ stable( feature = "rust1" , since = "1.0.0" ) ]
671674pub fn yield_now ( ) {
672675 imp:: Thread :: yield_now ( )
@@ -712,6 +715,8 @@ pub fn yield_now() {
712715/// panic!()
713716/// }
714717/// ```
718+ ///
719+ /// [Mutex]: crate::sync::Mutex
715720#[ inline]
716721#[ stable( feature = "rust1" , since = "1.0.0" ) ]
717722pub fn panicking ( ) -> bool {
@@ -779,11 +784,6 @@ pub fn sleep(dur: Duration) {
779784 imp:: Thread :: sleep ( dur)
780785}
781786
782- // constants for park/unpark
783- const EMPTY : usize = 0 ;
784- const PARKED : usize = 1 ;
785- const NOTIFIED : usize = 2 ;
786-
787787/// Blocks unless or until the current thread's token is made available.
788788///
789789/// A call to `park` does not guarantee that the thread will remain parked
@@ -870,45 +870,11 @@ const NOTIFIED: usize = 2;
870870///
871871/// [`unpark`]: Thread::unpark
872872/// [`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.
879873#[ stable( feature = "rust1" , since = "1.0.0" ) ]
880874pub 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 ( ) ;
912878 }
913879}
914880
@@ -970,35 +936,9 @@ pub fn park_timeout_ms(ms: u32) {
970936/// ```
971937#[ stable( feature = "park_timeout" , since = "1.4.0" ) ]
972938pub 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) ;
1002942 }
1003943}
1004944
@@ -1077,11 +1017,7 @@ impl ThreadId {
10771017struct Inner {
10781018 name : Option < CString > , // Guaranteed to be UTF-8
10791019 id : ThreadId ,
1080-
1081- // state for thread park/unpark
1082- state : AtomicUsize ,
1083- lock : Mutex < ( ) > ,
1084- cvar : Condvar ,
1020+ parker : Parker ,
10851021}
10861022
10871023#[ derive( Clone ) ]
@@ -1115,13 +1051,7 @@ impl Thread {
11151051 let cname =
11161052 name. map ( |n| CString :: new ( n) . expect ( "thread name may not contain interior null bytes" ) ) ;
11171053 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 ( ) } ) ,
11251055 }
11261056 }
11271057
@@ -1157,32 +1087,7 @@ impl Thread {
11571087 /// ```
11581088 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
11591089 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 ( ) ;
11861091 }
11871092
11881093 /// Gets the thread's unique identifier.
0 commit comments