diff options
author | Jordan Galby <67924449+Jordan474@users.noreply.github.com> | 2023-08-18 09:41:02 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-18 09:41:02 +0200 |
commit | f8e0c8caed0a97aae9d5a3f85026dc45d0417012 (patch) | |
tree | e0092ce06a4d2d2fdb539b84aaa8ac58e9a691be /lib | |
parent | d0412c1578ff1003c72352f94776d96d45282435 (diff) | |
download | spack-f8e0c8caed0a97aae9d5a3f85026dc45d0417012.tar.gz spack-f8e0c8caed0a97aae9d5a3f85026dc45d0417012.tar.bz2 spack-f8e0c8caed0a97aae9d5a3f85026dc45d0417012.tar.xz spack-f8e0c8caed0a97aae9d5a3f85026dc45d0417012.zip |
Fix Spack freeze on install child process unexpected exit (#39015)
* Fix spack frozen on child process defunct
* Rename parent/child pipe to read/write to emphasize non-duplex mode
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/build_environment.py | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py index ff99515d66..7ecde73b8a 100644 --- a/lib/spack/spack/build_environment.py +++ b/lib/spack/spack/build_environment.py @@ -1027,7 +1027,7 @@ def get_cmake_prefix_path(pkg): def _setup_pkg_and_run( - serialized_pkg, function, kwargs, child_pipe, input_multiprocess_fd, jsfd1, jsfd2 + serialized_pkg, function, kwargs, write_pipe, input_multiprocess_fd, jsfd1, jsfd2 ): context = kwargs.get("context", "build") @@ -1048,12 +1048,12 @@ def _setup_pkg_and_run( pkg, dirty=kwargs.get("dirty", False), context=context ) return_value = function(pkg, kwargs) - child_pipe.send(return_value) + write_pipe.send(return_value) except StopPhase as e: # Do not create a full ChildError from this, it's not an error # it's a control statement. - child_pipe.send(e) + write_pipe.send(e) except BaseException: # catch ANYTHING that goes wrong in the child process exc_type, exc, tb = sys.exc_info() @@ -1102,10 +1102,10 @@ def _setup_pkg_and_run( context, package_context, ) - child_pipe.send(ce) + write_pipe.send(ce) finally: - child_pipe.close() + write_pipe.close() if input_multiprocess_fd is not None: input_multiprocess_fd.close() @@ -1149,7 +1149,7 @@ def start_build_process(pkg, function, kwargs): For more information on `multiprocessing` child process creation mechanisms, see https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods """ - parent_pipe, child_pipe = multiprocessing.Pipe() + read_pipe, write_pipe = multiprocessing.Pipe(duplex=False) input_multiprocess_fd = None jobserver_fd1 = None jobserver_fd2 = None @@ -1174,7 +1174,7 @@ def start_build_process(pkg, function, kwargs): serialized_pkg, function, kwargs, - child_pipe, + write_pipe, input_multiprocess_fd, jobserver_fd1, jobserver_fd2, @@ -1183,6 +1183,12 @@ def start_build_process(pkg, function, kwargs): p.start() + # We close the writable end of the pipe now to be sure that p is the + # only process which owns a handle for it. This ensures that when p + # closes its handle for the writable end, read_pipe.recv() will + # promptly report the readable end as being ready. + write_pipe.close() + except InstallError as e: e.pkg = pkg raise @@ -1192,7 +1198,16 @@ def start_build_process(pkg, function, kwargs): if input_multiprocess_fd is not None: input_multiprocess_fd.close() - child_result = parent_pipe.recv() + def exitcode_msg(p): + typ = "exit" if p.exitcode >= 0 else "signal" + return f"{typ} {abs(p.exitcode)}" + + try: + child_result = read_pipe.recv() + except EOFError: + p.join() + raise InstallError(f"The process has stopped unexpectedly ({exitcode_msg(p)})") + p.join() # If returns a StopPhase, raise it @@ -1212,6 +1227,10 @@ def start_build_process(pkg, function, kwargs): child_result.print_context() raise child_result + # Fallback. Usually caught beforehand in EOFError above. + if p.exitcode != 0: + raise InstallError(f"The process failed unexpectedly ({exitcode_msg(p)})") + return child_result |