Skip to content
Snippets Groups Projects
Commit edce4535 authored by Yaroslav Dynnikov's avatar Yaroslav Dynnikov
Browse files

fix: killpg permission error on macos

Behavior of `killpg` slightly differs in Mac and Linux. For some reason,
`killpg` returns error EPERM when sending a signal to a zomibie process.
And that is the reason of `test_process_management` failure on mac -
there's a small gap between killing child and and subreaper calls
`waitpid`.

Now pytest handles this exception properly.

Close https://git.picodata.io/picodata/picodata/picodata/-/issues/70

See also:

- Stackoverflow: Why would `killpg` return "not permitted" when ownership is correct?
  https://stackoverflow.com/questions/12521705/why-would-killpg-return-not-permitted-when-ownership-is-correct

- Linux `man 2 killpg`:
  https://linux.die.net/man/2/killpg#Notes

  > Notes
  >
  > There are various differences between the permission checking in
  > BSD-type systems and System V-type systems. See the POSIX rationale
  > for kill(). A difference not mentioned by POSIX concerns the return
  > value EPERM: BSD documents that no signal is sent and EPERM returned
  > when the permission check failed for **at least one** target process,
  > while POSIX documents EPERM only when the permission check failed for
  > **all** target processes.

- MacOS `man 2 killpg`:
  https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/killpg.2.html

  > [EPERM] The sending process is not the super-user and
  >         **one or more** of the target processes has an effective
  >         user ID different from that of the sending process.

- Linux `man 2 kill`:
  https://linux.die.net/man/2/kill

  >  EPERM  The process does not have permission to send the signal
  >         *to any* of the target processes.
  >

- Process states in Linux:
  https://kerneltalks.com/linux/process-states-in-linux/

- Reproduce killpg returning EPERM on MacOS:
  https://git.picodata.io/picodata/picodata/picodata/-/snippets/7
parent 46eaf810
No related branches found
No related tags found
1 merge request!113fix: killpg permission error on macos
Pipeline #5075 passed
......@@ -141,6 +141,12 @@ def test_process_management(instance: Instance):
os.killpg(pgrp, 0)
except ProcessLookupError:
return True
except PermissionError:
# According to `man 2 kill`, MacOS raises it if at least one process
# in the process group has insufficient permissions. In fact, it also
# returns EPERM if the targed process is a zombie.
# See https://git.picodata.io/picodata/picodata/picodata/-/snippets/7
raise StillAlive
else:
raise StillAlive
......@@ -162,16 +168,24 @@ def test_process_management(instance: Instance):
assert exc.value.errno == errno.ECONNRESET
with pytest.raises(StillAlive):
waitpg(pgrp)
print(f"{instance} is still alive")
# Kill the remaining child in the process group
instance.killpg()
# When the supervisor is killed, the orphaned child is reparented to
# a subreaper. Pytest isn't the one, and therefore it can't do `waitpid`.
# Instead, the test retries `killpg` until it succeeds.
# When the supervisor is killed, the orphaned child is reparented
# to a subreaper. Pytest isn't the one, and therefore it can't do
# `waitpid` directly. Instead, the test retries `killpg` until
# it succeeds.
#
# Also, note, that after the child is killed, it remains
# a zombie for a while. The child is removed from the process
# table when a supreaper calls `waitpid`.
#
waitpg(pgrp)
print(f"{instance} is finally dead")
# Ensure the child is dead
waitpg(pgrp)
with pytest.raises(ProcessLookupError):
os.killpg(pgrp, 0)
......@@ -181,7 +195,6 @@ def test_process_management(instance: Instance):
instance.start()
pid2 = instance.process.pid
assert pid1 == pid2
instance.terminate()
instance.terminate()
instance.killpg()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment