| 649 | |
| 650 | #[pyfunction] |
| 651 | fn _shutdown(vm: &VirtualMachine) { |
| 652 | // Wait for all non-daemon threads to finish |
| 653 | let current_ident = get_ident(); |
| 654 | |
| 655 | loop { |
| 656 | // Find a thread that's not finished and not the current thread |
| 657 | let handle_to_join = { |
| 658 | let mut handles = vm.state.shutdown_handles.lock(); |
| 659 | // Clean up finished entries |
| 660 | handles.retain(|(inner_weak, _): &ShutdownEntry| { |
| 661 | inner_weak.upgrade().is_some_and(|inner| { |
| 662 | let guard = inner.lock(); |
| 663 | guard.state != ThreadHandleState::Done && guard.ident != current_ident |
| 664 | }) |
| 665 | }); |
| 666 | |
| 667 | // Find first unfinished handle |
| 668 | handles |
| 669 | .iter() |
| 670 | .find_map(|(inner_weak, done_event_weak): &ShutdownEntry| { |
| 671 | let inner = inner_weak.upgrade()?; |
| 672 | let done_event = done_event_weak.upgrade()?; |
| 673 | let guard = inner.lock(); |
| 674 | if guard.state != ThreadHandleState::Done && guard.ident != current_ident { |
| 675 | Some((inner.clone(), done_event.clone())) |
| 676 | } else { |
| 677 | None |
| 678 | } |
| 679 | }) |
| 680 | }; |
| 681 | |
| 682 | match handle_to_join { |
| 683 | Some((inner, done_event)) => { |
| 684 | if let Err(exc) = ThreadHandle::join_internal(&inner, &done_event, None, vm) { |
| 685 | vm.run_unraisable( |
| 686 | exc, |
| 687 | Some( |
| 688 | "Exception ignored while joining a thread in _thread._shutdown()" |
| 689 | .to_owned(), |
| 690 | ), |
| 691 | vm.ctx.none(), |
| 692 | ); |
| 693 | return; |
| 694 | } |
| 695 | } |
| 696 | None => break, // No more threads to wait on |
| 697 | } |
| 698 | } |
| 699 | } |
| 700 | |
| 701 | /// Add a non-daemon thread handle to the shutdown registry |
| 702 | fn add_to_shutdown_handles( |