Warn if forking from a multi-threaded process. `num_os_threads` should be captured before parent after-fork hooks run.
(name: &str, num_os_threads: isize, vm: &VirtualMachine)
| 953 | /// Warn if forking from a multi-threaded process. |
| 954 | /// `num_os_threads` should be captured before parent after-fork hooks run. |
| 955 | fn warn_if_multi_threaded(name: &str, num_os_threads: isize, vm: &VirtualMachine) { |
| 956 | let num_threads = if num_os_threads > 0 { |
| 957 | num_os_threads as usize |
| 958 | } else { |
| 959 | // CPython fallback: if OS-level count isn't available, use the |
| 960 | // threading module's active+limbo view. |
| 961 | // Only check threading if it was already imported. Avoid vm.import() |
| 962 | // which can execute arbitrary Python code in the fork path. |
| 963 | let threading = match vm |
| 964 | .sys_module |
| 965 | .get_attr("modules", vm) |
| 966 | .and_then(|m| m.get_item("threading", vm)) |
| 967 | { |
| 968 | Ok(m) => m, |
| 969 | Err(_) => return, |
| 970 | }; |
| 971 | let active = threading.get_attr("_active", vm).ok(); |
| 972 | let limbo = threading.get_attr("_limbo", vm).ok(); |
| 973 | |
| 974 | // Match threading module internals and avoid sequence overcounting: |
| 975 | // count only dict-backed _active/_limbo containers. |
| 976 | let count_dict = |obj: Option<crate::PyObjectRef>| -> usize { |
| 977 | obj.and_then(|o| { |
| 978 | o.downcast_ref::<crate::builtins::PyDict>() |
| 979 | .map(|d| d.__len__()) |
| 980 | }) |
| 981 | .unwrap_or(0) |
| 982 | }; |
| 983 | |
| 984 | count_dict(active) + count_dict(limbo) |
| 985 | }; |
| 986 | |
| 987 | if num_threads > 1 { |
| 988 | let pid = unsafe { libc::getpid() }; |
| 989 | let msg = format!( |
| 990 | "This process (pid={}) is multi-threaded, use of {}() may lead to deadlocks in the child.", |
| 991 | pid, name |
| 992 | ); |
| 993 | |
| 994 | // Match PyErr_WarnFormat(..., stacklevel=1) in CPython. |
| 995 | // Best effort: ignore failures like CPython does in this path. |
| 996 | let _ = |
| 997 | crate::stdlib::_warnings::warn(vm.ctx.exceptions.deprecation_warning, msg, 1, vm); |
| 998 | } |
| 999 | } |
| 1000 | |
| 1001 | #[pyfunction] |
| 1002 | fn fork(vm: &VirtualMachine) -> PyResult<i32> { |