-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
fixes #5091; Ensure we don't wait on an exited process on Linux #23743
Conversation
I think that the proposed fix is kinda racy, because if the process is terminated between your check and blocking the signals, then we will not receive A better approach would be to check for |
Also, the fix seems to be incomplete. It doesn't make the situation worse, but there are still cases when |
That was my initial approach, but I don't see much difference between having the check after the Invoking
The |
If the process terminates between your check and blocking the signals, then we will wait for the whole timeout even if the process had already terminated, as in the original bug.
The code path without timeout uses |
I understand, but your example assumes the process has not terminated before the invocation of var running: Process
var terminated: Process An invocation
I understand your concern, but I think it's only valid if we were using |
Maybe the bug is a bit different, but the problem remains the same: if the process terminates right before
In case of timed |
I think understand where your coming from. This current change is reaping the zombie processes too early. It's not the same issue, but it is inefficient to wait the whole
I think I may have misunderstood you. When you originally said "hang", I thought you meant it blocks indefinitely. Mea culpa! I agree that we will wait |
I also don't know a simple and good solution here, at least a portable one. New Linux kernels (since 5.3) have |
import std/times
proc waitForExit(p: Process, timeout: int = -1): int =
# Max MS delay
const maxWait = 100
if p.exitFlag:
return exitStatusLikeShell(p.exitStatus)
let wait =
if timeout <= 0:
initDuration()
else:
initDuration(milliseconds = timeout)
let deadline = getTime() + wait
# starting ms delay
var delay = 1
while true:
var status: cint = 0
let pid = waitpid(p.id, status, WNOHANG)
if p.id == pid :
p.exitFlag = true
p.exitSTatus = status
break
elif pid.int == -1:
raiseOsError(osLastError())
else:
# Continue waiting if needed
if getTime() >= deadline:
# Previous version of `waitForExit`
# foricibly killed the process.
# We keep this so we don't break programs
# that depend on this behavior
if posix.kill(p.id, SIGKILL) < 0:
raiseOSError(osLastError())
else:
let newWait = getTime() + initDuration(milliseconds = delay)
var waitSpec: TimeSpec
waitSpec.tv_sec = posix.Time(newWait.toUnix())
waitSpec.tv_nsec = newWait.nanosecond.clong
discard posix.clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, waitSpec, cast[var TimeSpec](nil))
delay = min(delay * 2, maxWait)
result = exitStatusLikeShell(p.exitStatus) Note that this adds the |
Looks good to me. |
Thanks for your hard work on this PR! Hint: mm: orc; opt: speed; options: -d:release |
@maleyva1 I am unsure that programming on One of the alternative ideas is to make a global Unluckily, there is AFAIK no good generic solution for UNIX systems. See also: https://catern.com/process.html#orgc15165e. |
I am aware of this approach. I did not suggest it not only because of the aforementioned issues but also because
A valid concern, but we can adjust the sleep intervals to whatever optimal period we need (with nanosecond precision) or even expose it to users as an argument like |
Wow, I didn't know that before :) But, unlike your idea, it seems that Python's |
Actually, if you look back at the suggested rewrite, we similarly do what Python's Though you're right; perhaps waiting up to 100ms is not the right choice. |
Oops, sorry, you are right, I haven't noticed that your suggested rewrite also contains the same exponential backoff. Then, maybe your solution is good enough, considering that all the others are either very complex or don't work in some cases. |
) Addresses #23825 by using the approaching described in #23743 (comment). This takes the approach from Python's `subprocess` library which calls `waitid` in loop, while sleeping at regular intervals. CC @alex65536
Fixes #5091.
Ensure we don't wait on an exited process on Linux