diff --git a/python/libcuopt/libcuopt/_cli_wrapper.py b/python/libcuopt/libcuopt/_cli_wrapper.py index 5f5d134ba6..78e95280b7 100644 --- a/python/libcuopt/libcuopt/_cli_wrapper.py +++ b/python/libcuopt/libcuopt/_cli_wrapper.py @@ -1,14 +1,17 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import os -import subprocess import sys def main(): """ This connects to cli binary which situated under libcuopt/bin folder + + execv replaces this process rather than spawning a child, so signals sent + to the console script's pid reach the solver directly instead of stopping + at a Python parent that forwards nothing. """ cli_path = os.path.join(os.path.dirname(__file__), "bin", "cuopt_cli") - sys.exit(subprocess.call([cli_path] + sys.argv[1:])) + os.execv(cli_path, [cli_path] + sys.argv[1:]) diff --git a/python/libcuopt/libcuopt/_grpc_server_wrapper.py b/python/libcuopt/libcuopt/_grpc_server_wrapper.py index dc60b2bbda..763bf33a36 100644 --- a/python/libcuopt/libcuopt/_grpc_server_wrapper.py +++ b/python/libcuopt/libcuopt/_grpc_server_wrapper.py @@ -2,15 +2,20 @@ # SPDX-License-Identifier: Apache-2.0 import os -import subprocess import sys def main(): """ This connects to the gRPC server binary situated under libcuopt/bin folder. + + execv replaces this process rather than spawning a child. Spawning leaves + a Python parent that waits on the server but forwards nothing to it, so a + signal sent to the console script's pid kills only the wrapper: the server + and its GPU workers survive, orphaned and still holding the listen port, + and the shutdown path that cancels jobs and reaps workers never runs. """ server_path = os.path.join( os.path.dirname(__file__), "bin", "cuopt_grpc_server" ) - sys.exit(subprocess.call([server_path] + sys.argv[1:])) + os.execv(server_path, [server_path] + sys.argv[1:])