transports: use a thread instead of SafeChildWatcher#22913
transports: use a thread instead of SafeChildWatcher#22913allisonkarlitskaya merged 2 commits intocockpit-project:mainfrom
Conversation
martinpitt
left a comment
There was a problem hiding this comment.
Thanks! Given the circumstances of this moving target (asyncio), this is actually a nice solution. 💯
SafeChildWatcher was removed in 3.14. We used to use this as a fallback for when we don't have pidfd support in the kernel (or Python stdlib) under the assumption that we'd never see Python 3.14 on a system with such an old kernel, but of course this happens with newer Python versions in containers running on RHEL 8 hosts. Let's divorce ourselves from the entire concept of ChildWatchers, dropping our 'qdata' cache for them. Instead, we just try to create a pidfd each time, and if that fails, start up a thread to call waitpid() and notify the event loop via call_soon_threadsafe(). This approach is a simplified form of asyncio.ThreadedChildWatcher from the standard library, which is what gets used anywhere pidfds aren't available. We had a test that caused pidfd to return ENOSYS that we started skipping on newer Python versions to avoid trying to use SafeChildWatcher in what I imagined to be an impossible configuration. Bring that back unconditionally (and rename it to remove the reference to "safe watcher").
Add tests to verify that non-zero exit codes and signal termination are correctly reported when using the threaded waitpid() fallback path (when pidfd is unavailable). Includes a concurrent test that starts multiple processes, terminates them in a controlled order, and verifies that the correct exit status is attributed to each process, and at the expected time. Assisted-by: Claude Opus 4.5 <noreply@anthropic.com>
c640edd to
60edf90
Compare
|
Python 3.6 pain made obscure by the errors of the bridge on the host being sent over ssh to the container and discarded. We really ought to fix the logging story one of these days... |
martinpitt
left a comment
There was a problem hiding this comment.
Thanks! Let's do one round of review, but I agree this is urgent.
| def flag_exit() -> None: | ||
| def child_exited(pid: int, status: int) -> None: | ||
| assert pid == process.pid | ||
| # os.waitstatus_to_exitcode() is only available since Python 3.9 |
There was a problem hiding this comment.
That could become a polyfill, for easier future clean-up.
There was a problem hiding this comment.
True. This would fit nicely in polyfills.py with the rest of them.
|
Agreed to do the polyfill of |
7157811
into
cockpit-project:main
SafeChildWatcher was removed in 3.14. We used to use this as a fallback for when we don't have pidfd support in the kernel (or Python stdlib) under the assumption that we'd never see Python 3.14 on a system with such an old kernel, but of course this happens with newer Python versions in containers running on RHEL 8 hosts.
Let's divorce ourselves from the entire concept of ChildWatchers, dropping our 'qdata' cache for them. Instead, we just try to create a pidfd each time, and if that fails, start up a thread to call waitpid() and notify the event loop via call_soon_threadsafe(). This approach is a simplified form of asyncio.ThreadedChildWatcher from the standard library, which is what gets used anywhere pidfds aren't available.
We had a test that caused pidfd to return ENOSYS that we started skipping on newer Python versions to avoid trying to use SafeChildWatcher in what I imagined to be an impossible configuration. Bring that back unconditionally (and rename it to remove the reference to "safe watcher").