Try to CAS detached threads directly to SUSPENDED and check whether stop countdown reached zero after parking detached threads.
(&self, vm: &VirtualMachine)
| 255 | /// Try to CAS detached threads directly to SUSPENDED and check whether |
| 256 | /// stop countdown reached zero after parking detached threads. |
| 257 | fn park_detached_threads(&self, vm: &VirtualMachine) -> bool { |
| 258 | use thread::{THREAD_ATTACHED, THREAD_DETACHED, THREAD_SUSPENDED}; |
| 259 | let requester = self.requester.load(Ordering::Relaxed); |
| 260 | let registry = vm.state.thread_frames.lock(); |
| 261 | let mut attached_seen = 0u64; |
| 262 | let mut forced_parks = 0u64; |
| 263 | for (&id, slot) in registry.iter() { |
| 264 | if id == requester { |
| 265 | continue; |
| 266 | } |
| 267 | let state = slot.state.load(Ordering::Relaxed); |
| 268 | if state == THREAD_DETACHED { |
| 269 | // CAS DETACHED → SUSPENDED (park without thread cooperation) |
| 270 | match slot.state.compare_exchange( |
| 271 | THREAD_DETACHED, |
| 272 | THREAD_SUSPENDED, |
| 273 | Ordering::AcqRel, |
| 274 | Ordering::Relaxed, |
| 275 | ) { |
| 276 | Ok(_) => { |
| 277 | slot.stop_requested.store(false, Ordering::Release); |
| 278 | forced_parks = forced_parks.saturating_add(1); |
| 279 | } |
| 280 | Err(THREAD_ATTACHED) => { |
| 281 | // Set per-thread stop bit (_PY_EVAL_PLEASE_STOP_BIT). |
| 282 | slot.stop_requested.store(true, Ordering::Release); |
| 283 | // Raced with a thread re-attaching; it will self-suspend. |
| 284 | attached_seen = attached_seen.saturating_add(1); |
| 285 | } |
| 286 | Err(THREAD_DETACHED) => { |
| 287 | // Extremely unlikely race; next poll will handle it. |
| 288 | } |
| 289 | Err(THREAD_SUSPENDED) => { |
| 290 | slot.stop_requested.store(false, Ordering::Release); |
| 291 | // Another path parked it first. |
| 292 | } |
| 293 | Err(other) => { |
| 294 | debug_assert!( |
| 295 | false, |
| 296 | "unexpected thread state in park_detached_threads: {other}" |
| 297 | ); |
| 298 | } |
| 299 | } |
| 300 | } else if state == THREAD_ATTACHED { |
| 301 | // Set per-thread stop bit (_PY_EVAL_PLEASE_STOP_BIT). |
| 302 | slot.stop_requested.store(true, Ordering::Release); |
| 303 | // Thread is in bytecode — it will see `requested` and self-suspend |
| 304 | attached_seen = attached_seen.saturating_add(1); |
| 305 | } |
| 306 | // THREAD_SUSPENDED → already parked |
| 307 | } |
| 308 | if attached_seen != 0 { |
| 309 | self.stats_attached_seen |
| 310 | .fetch_add(attached_seen, Ordering::Relaxed); |
| 311 | } |
| 312 | if forced_parks != 0 { |
| 313 | self.decrement_thread_countdown(forced_parks); |
| 314 | self.stats_forced_parks |
no test coverage detected