(state: WatchdogHandle)
| 831 | } |
| 832 | |
| 833 | fn watchdog_thread(state: WatchdogHandle) { |
| 834 | let (lock, cvar) = &*state; |
| 835 | |
| 836 | loop { |
| 837 | // Hold lock across wait_timeout to avoid race condition |
| 838 | let mut guard = lock.lock(); |
| 839 | if guard.cancel { |
| 840 | return; |
| 841 | } |
| 842 | let timeout = Duration::from_micros(guard.timeout_us); |
| 843 | cvar.wait_for(&mut guard, timeout); |
| 844 | |
| 845 | // Check if cancelled after wait |
| 846 | if guard.cancel { |
| 847 | return; |
| 848 | } |
| 849 | |
| 850 | // Extract values before releasing lock for I/O |
| 851 | let repeat = guard.repeat; |
| 852 | let exit = guard.exit; |
| 853 | let fd = guard.fd; |
| 854 | let header = guard.header.clone(); |
| 855 | #[cfg(feature = "threading")] |
| 856 | let thread_frame_slots = guard.thread_frame_slots.clone(); |
| 857 | drop(guard); // Release lock before I/O |
| 858 | |
| 859 | // Timeout occurred, dump traceback |
| 860 | #[cfg(target_arch = "wasm32")] |
| 861 | let _ = (exit, fd, &header); |
| 862 | |
| 863 | #[cfg(not(target_arch = "wasm32"))] |
| 864 | { |
| 865 | puts_bytes(fd, header.as_bytes()); |
| 866 | |
| 867 | // Use thread frame slots when threading is enabled (includes all threads). |
| 868 | // Fall back to live frame walking for non-threaded builds. |
| 869 | #[cfg(feature = "threading")] |
| 870 | { |
| 871 | for (tid, slot) in &thread_frame_slots { |
| 872 | let frames = slot.frames.lock(); |
| 873 | dump_traceback_thread_frames(fd, *tid, false, &frames); |
| 874 | } |
| 875 | } |
| 876 | #[cfg(not(feature = "threading"))] |
| 877 | { |
| 878 | write_thread_id(fd, current_thread_id(), false); |
| 879 | dump_live_frames(fd); |
| 880 | } |
| 881 | |
| 882 | if exit { |
| 883 | std::process::exit(1); |
| 884 | } |
| 885 | } |
| 886 | |
| 887 | if !repeat { |
| 888 | return; |
| 889 | } |
| 890 | } |
no test coverage detected