-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix signal handler after fork #7012
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughRefactors VM signal handler storage from an Option to a OnceCell-backed lazy container, adds a crate helper to create the handler array, enforces main-thread-only signal operations, and ensures signal handlers are initialized in forked child processes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
rect rgba(200,220,255,0.5)
participant Py as Python code
participant VM as VirtualMachine
participant SIG as signal_handlers (OnceCell<Box<RefCell...>>)
participant OS as OS/Thread
end
Py->>VM: call signal.signal(signum, handler)
VM->>OS: check is_main_thread()
alt not main thread
VM-->>Py: raise ValueError
else main thread
VM->>SIG: get_or_init(new_signal_handlers)
SIG->>SIG: borrow & store handler at index
SIG-->>VM: stored
VM-->>Py: return old handler
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/vm/src/stdlib/posix.rs (1)
783-798:⚠️ Potential issue | 🟡 Minor
fork()does not handlelibc::fork()returning-1(error).If
libc::fork()fails it returns-1, but the function unconditionally runspy_os_after_fork_parenton the error path (since-1 != 0). This is pre-existing behavior and not introduced by this PR, but worth noting: the-1is also returned directly to the Python caller without raisingOSError.
🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/signal.rs`:
- Around line 380-381: The error message in set_wakeup_fd is wrong; locate the
branch that checks vm.is_main_thread() inside the set_wakeup_fd implementation
and change the ValueError text from "signal only works in main thread" to
"set_wakeup_fd only works in main thread" so the raised error accurately names
the function (ensure the change occurs in the function/method named
set_wakeup_fd and only affects the vm.new_value_error call in that check).
- Around line 258-260: The getsignal() implementation currently enforces a
main-thread check using vm.is_main_thread() and returns
Err(vm.new_value_error(...)); remove that main-thread restriction so getsignal()
no longer returns an error for non-main threads: locate the getsignal function
in signal.rs, delete or bypass the vm.is_main_thread() check and its Err return,
and ensure the function simply returns the current handler value for the given
signal regardless of thread; keep the main-thread check only in signal.signal()
if present.
🧹 Nitpick comments (2)
crates/vm/src/stdlib/posix.rs (1)
714-718: Duplicatedsignal_handlersinitialization expression across files.The
Box::new(const { RefCell::new([const { None }; NSIG]) })pattern is repeated here, insignal.rs(lines 250, 263), and invm/mod.rs(line 187). Consider extracting a helper (e.g.,signal_handlers_default()) to keep this in one place and reduce divergence risk.Example helper
// In crates/vm/src/signal.rs or a shared location: pub(crate) fn new_signal_handlers() -> Box<RefCell<[Option<PyObjectRef>; NSIG]>> { Box::new(const { RefCell::new([const { None }; NSIG]) }) }Then each call site becomes:
-vm.signal_handlers.get_or_init(|| { - Box::new(const { core::cell::RefCell::new([const { None }; crate::signal::NSIG]) }) -}); +vm.signal_handlers.get_or_init(signal::new_signal_handlers);crates/vm/src/stdlib/signal.rs (1)
181-181:get().unwrap()ininit_signal_handlerscould panic if called on a worker thread.Unlike
signal()/getsignal()which useget_or_init, this usesget().unwrap(). This is safe today becausemodule_execruns during main-thread initialization where theOnceCellis pre-filled, but it's fragile. Consider usingget_or_initfor consistency, or adding a debug assertion with a clear message.Proposed fix
-vm.signal_handlers.get().unwrap().borrow_mut()[signum] = py_handler; +vm.signal_handlers + .get_or_init(|| Box::new(const { RefCell::new([const { None }; signal::NSIG]) })) + .borrow_mut()[signum] = py_handler;
- Change `signal_handlers` type from `Option<Box<...>>` to `OnceCell<Box<...>>` so fork children can lazily initialize it - Add `VirtualMachine::is_main_thread()` using runtime thread ID comparison instead of structural `signal_handlers.is_none()` check - Initialize signal handlers in `py_os_after_fork_child()` via `get_or_init()` for workers that fork - Use `get_or_init()` in `signal()` and `getsignal()` functions - Remove `set_wakeup_fd(-1)` special-case workaround (d6d0303) - Extract `signal::new_signal_handlers()` to deduplicate init expr - Allow `getsignal()` from any thread (matches CPython behavior) - Fix `set_wakeup_fd` error message to name the correct function
Summary by CodeRabbit