-
-
Notifications
You must be signed in to change notification settings - Fork 392
Add support for asynchronous waitpid on Linux systems. #622
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import attr | ||
| import functools | ||
| import os | ||
| import outcome | ||
| from typing import Any | ||
|
|
||
| from .. import _core | ||
| from .._sync import Event | ||
| from .._threads import run_sync_in_worker_thread | ||
|
|
||
|
|
||
| @attr.s | ||
| class WaitpidState: | ||
| pid = attr.ib() | ||
| event = attr.ib(default=attr.Factory(Event)) | ||
| outcome = attr.ib(default=None) | ||
|
|
||
|
|
||
| # https://github.com/python-trio/trio/issues/618 | ||
| class StubLimiter: | ||
| def release_on_behalf_of(self, x): | ||
| pass | ||
|
|
||
| async def acquire_on_behalf_of(self, x): | ||
| pass | ||
|
|
||
|
|
||
| waitpid_limiter = StubLimiter() | ||
|
|
||
|
|
||
| # adapted from | ||
| # https://github.com/python-trio/trio/issues/4#issuecomment-398967572 | ||
| async def _task(state: WaitpidState) -> None: | ||
| """The waitpid thread runner task. This must be spawned as a system | ||
| task.""" | ||
| partial = functools.partial( | ||
| os.waitpid, # function | ||
| state.pid, # pid | ||
| 0 # no options | ||
| ) | ||
|
|
||
| tresult = await run_sync_in_worker_thread( | ||
| outcome.capture, partial, cancellable=True, limiter=waitpid_limiter | ||
| ) | ||
| state.outcome = tresult | ||
| state.event.set() | ||
|
|
||
|
|
||
| async def waitpid(pid: int) -> Any: | ||
| """Waits for a child process with the specified PID to finish running.""" | ||
| waiter = WaitpidState(pid=pid) | ||
| _core.spawn_system_task(_task, waiter) | ||
|
|
||
| await waiter.event.wait() | ||
| return waiter.outcome.unwrap() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import sys | ||
|
|
||
| import os | ||
| import pytest | ||
| import signal | ||
|
|
||
| from ... import _core | ||
| from ..._subprocess.linux_waitpid import waitpid | ||
|
|
||
| pytestmark = pytest.mark.skipif( | ||
| sys.platform != "linux", reason="linux waitpid only works on linux" | ||
| ) | ||
|
|
||
|
|
||
| async def test_waitpid(): | ||
| pid = os.spawnvp(os.P_NOWAIT, "/bin/false", ("false",)) | ||
| result = await waitpid(pid) | ||
| # exit code is a 16-bit int: (code, signal) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory it's a bit more complicated than that, there's a "was a core dumped?" flag in there and the ability to distinguish between stop signals and termination signals (see). In practice this is a pretty pedantic distinction. If we want to be really POSIX-ly correct, though, I guess the tests should make assertions like
? |
||
| assert result[0] == pid | ||
| assert os.WIFEXITED(result[1]) and os.WEXITSTATUS(result[1]) == 1 | ||
|
|
||
| pid2 = os.spawnvp(os.P_NOWAIT, "/bin/true", ("true",)) | ||
| result = await waitpid(pid2) | ||
| assert result[0] == pid2 | ||
| assert os.WIFEXITED(result[1]) and os.WEXITSTATUS(result[1]) == 0 | ||
|
|
||
| pid3 = os.spawnvp(os.P_NOWAIT, "/bin/sleep", ("/bin/sleep", "5")) | ||
| os.kill(pid3, signal.SIGKILL) | ||
| result = await waitpid(pid3) | ||
| assert result[0] == pid3 | ||
| status = os.WTERMSIG(result[1]) | ||
| assert os.WIFSIGNALED(result[1]) and status == 9 | ||
|
|
||
|
|
||
| async def test_waitpid_no_process(): | ||
| with pytest.raises(ChildProcessError): | ||
| # this PID does exist, but it's ourselves | ||
| # which doesn't work | ||
| await waitpid(os.getpid()) | ||
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.
I guess we'll eventually need to split this up into two functions, but that's fine, no particular reason to do that now.