@@ -198,20 +198,39 @@ pub(crate) struct StopTheWorldTransaction {
198198}
199199
200200#[ cfg( feature = "threading" ) ]
201- static STW_TRANSACTION : AtomicBool = AtomicBool :: new ( false ) ;
201+ static STW_TRANSACTION : AtomicU64 = AtomicU64 :: new ( 0 ) ;
202+
203+ #[ cfg( feature = "threading" ) ]
204+ static NEXT_STW_REQUESTER : AtomicU64 = AtomicU64 :: new ( 1 ) ;
205+
206+ #[ cfg( feature = "threading" ) ]
207+ thread_local ! {
208+ // OS thread IDs can be reused after a requester exits, even while another
209+ // thread still owes its matching restart. Logical IDs are never reused.
210+ static STW_REQUESTER : u64 = NEXT_STW_REQUESTER
211+ . try_update( Ordering :: Relaxed , Ordering :: Relaxed , |id| id. checked_add( 1 ) )
212+ . expect( "stop-the-world requester IDs exhausted" ) ;
213+ }
202214
203215#[ cfg( feature = "threading" ) ]
204216impl StopTheWorldTransaction {
205217 pub ( crate ) fn acquire ( ) -> Self {
218+ let requester = STW_REQUESTER . with ( |id| * id) ;
206219 loop {
207- if STW_TRANSACTION
208- . compare_exchange ( false , true , Ordering :: AcqRel , Ordering :: Relaxed )
209- . is_ok ( )
210- {
211- return Self { _private : ( ) } ;
220+ match STW_TRANSACTION . compare_exchange (
221+ 0 ,
222+ requester,
223+ Ordering :: AcqRel ,
224+ Ordering :: Relaxed ,
225+ ) {
226+ Ok ( _) => return Self { _private : ( ) } ,
227+ Err ( owner) => assert_ne ! (
228+ owner, requester,
229+ "stop-the-world transactions cannot be nested on the same thread"
230+ ) ,
212231 }
213232 let wait = || {
214- while STW_TRANSACTION . load ( Ordering :: Acquire ) {
233+ while STW_TRANSACTION . load ( Ordering :: Acquire ) != 0 {
215234 std:: thread:: yield_now ( ) ;
216235 }
217236 } ;
@@ -228,7 +247,7 @@ impl StopTheWorldTransaction {
228247#[ cfg( feature = "threading" ) ]
229248impl Drop for StopTheWorldTransaction {
230249 fn drop ( & mut self ) {
231- STW_TRANSACTION . store ( false , Ordering :: Release ) ;
250+ STW_TRANSACTION . store ( 0 , Ordering :: Release ) ;
232251 }
233252}
234253
@@ -460,6 +479,8 @@ impl StopTheWorldState {
460479 /// Public calls own one process-wide transaction until the matching start.
461480 /// The caller must finish stop before invoking start, including when those
462481 /// calls occur on different threads; concurrent duplicate starts are invalid.
482+ /// Transactions are non-reentrant: a nested stop on the requesting thread
483+ /// panics before changing any stop state or releasing the outer transaction.
463484 pub fn stop_the_world ( & self , state : & PyGlobalState ) {
464485 let transaction = StopTheWorldTransaction :: acquire ( ) ;
465486 self . stop_with_transaction ( state, & transaction) ;
0 commit comments