mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-04 13:24:13 +08:00
Finish owned native demo cleanup despite repeated stop signals
This commit is contained in:
@@ -98,6 +98,32 @@ def worker_arguments(project, root, out, archive, route, muted, curve_plan=None,
|
||||
return args
|
||||
|
||||
|
||||
def cleanup_owned(children, display, logs):
|
||||
"""Finish owned cleanup even if the parent sends a second stop signal."""
|
||||
previous = {sig: signal.signal(sig, signal.SIG_IGN) for sig in (signal.SIGTERM, signal.SIGINT)}
|
||||
errors = []
|
||||
try:
|
||||
for child in reversed(list(children.values())):
|
||||
try:
|
||||
if child.poll() is None:
|
||||
try: os.killpg(child.pid, signal.SIGTERM)
|
||||
except ProcessLookupError: pass
|
||||
try: child.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
try: os.killpg(child.pid, signal.SIGKILL)
|
||||
except ProcessLookupError: pass
|
||||
child.wait(timeout=5)
|
||||
except Exception as error:
|
||||
errors.append(type(error).__name__ + ': ' + str(error))
|
||||
finally:
|
||||
try:
|
||||
if display is not None: display.close()
|
||||
finally:
|
||||
for log in logs: log.close()
|
||||
for sig, handler in previous.items(): signal.signal(sig, handler)
|
||||
return errors
|
||||
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--roadscore',action='store_true');p.add_argument('--demo',action='store_true')
|
||||
@@ -220,13 +246,8 @@ def main():
|
||||
write_json(out/'demo_failure.json',dict(session_id=session,error=failure,wall=time.monotonic()))
|
||||
raise
|
||||
finally:
|
||||
for child in reversed(list(children.values())):
|
||||
if child.poll() is None:
|
||||
os.killpg(child.pid,signal.SIGTERM)
|
||||
try:child.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:os.killpg(child.pid,signal.SIGKILL);child.wait()
|
||||
if display is not None:display.close()
|
||||
for log in logs:log.close()
|
||||
cleanup_errors=cleanup_owned(children,display,logs)
|
||||
if cleanup_errors:write_json(out/'cleanup_errors.json',dict(errors=cleanup_errors))
|
||||
write_json(out/'status.json',dict(readiness='DEGRADED' if failure else 'COLD',compute='prepared-core',route=route,presentation_session_id=session,command_wall=time.monotonic(),input_mode='stopped',error=failure))
|
||||
print('Prepared native demo stopped. Existing composer ownership unchanged.',flush=True)
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import os
|
||||
import signal
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
from native_prepared_showcase import cleanup_owned
|
||||
|
||||
class CleanupTests(unittest.TestCase):
|
||||
def test_second_stop_during_wait_does_not_skip_display(self):
|
||||
child=Mock(pid=123);child.poll.return_value=None
|
||||
child.wait.side_effect=lambda **kw:os.kill(os.getpid(),signal.SIGTERM)
|
||||
display=Mock();log=Mock();old=signal.getsignal(signal.SIGTERM)
|
||||
with patch('native_prepared_showcase.os.killpg'):
|
||||
self.assertEqual(cleanup_owned({'ui':child},display,[log]),[])
|
||||
display.close.assert_called_once();log.close.assert_called_once()
|
||||
self.assertEqual(signal.getsignal(signal.SIGTERM),old)
|
||||
def test_failed_child_does_not_skip_other_children_or_display(self):
|
||||
bad=Mock(pid=1);bad.poll.side_effect=OSError('gone')
|
||||
good=Mock(pid=2);good.poll.return_value=None;display=Mock()
|
||||
with patch('native_prepared_showcase.os.killpg'):
|
||||
self.assertEqual(len(cleanup_owned({'good':good,'bad':bad},display,[])),1)
|
||||
good.wait.assert_called_once();display.close.assert_called_once()
|
||||
Reference in New Issue
Block a user