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
This commit is contained in:
Jordan Galby 2023-08-18 09:41:02 +02:00 committed by GitHub
parent d0412c1578
commit f8e0c8caed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1027,7 +1027,7 @@ def get_cmake_prefix_path(pkg):
def _setup_pkg_and_run( 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") context = kwargs.get("context", "build")
@ -1048,12 +1048,12 @@ def _setup_pkg_and_run(
pkg, dirty=kwargs.get("dirty", False), context=context pkg, dirty=kwargs.get("dirty", False), context=context
) )
return_value = function(pkg, kwargs) return_value = function(pkg, kwargs)
child_pipe.send(return_value) write_pipe.send(return_value)
except StopPhase as e: except StopPhase as e:
# Do not create a full ChildError from this, it's not an error # Do not create a full ChildError from this, it's not an error
# it's a control statement. # it's a control statement.
child_pipe.send(e) write_pipe.send(e)
except BaseException: except BaseException:
# catch ANYTHING that goes wrong in the child process # catch ANYTHING that goes wrong in the child process
exc_type, exc, tb = sys.exc_info() exc_type, exc, tb = sys.exc_info()
@ -1102,10 +1102,10 @@ def _setup_pkg_and_run(
context, context,
package_context, package_context,
) )
child_pipe.send(ce) write_pipe.send(ce)
finally: finally:
child_pipe.close() write_pipe.close()
if input_multiprocess_fd is not None: if input_multiprocess_fd is not None:
input_multiprocess_fd.close() input_multiprocess_fd.close()
@ -1149,7 +1149,7 @@ def child_fun():
For more information on `multiprocessing` child process creation For more information on `multiprocessing` child process creation
mechanisms, see https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods 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 input_multiprocess_fd = None
jobserver_fd1 = None jobserver_fd1 = None
jobserver_fd2 = None jobserver_fd2 = None
@ -1174,7 +1174,7 @@ def child_fun():
serialized_pkg, serialized_pkg,
function, function,
kwargs, kwargs,
child_pipe, write_pipe,
input_multiprocess_fd, input_multiprocess_fd,
jobserver_fd1, jobserver_fd1,
jobserver_fd2, jobserver_fd2,
@ -1183,6 +1183,12 @@ def child_fun():
p.start() 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: except InstallError as e:
e.pkg = pkg e.pkg = pkg
raise raise
@ -1192,7 +1198,16 @@ def child_fun():
if input_multiprocess_fd is not None: if input_multiprocess_fd is not None:
input_multiprocess_fd.close() 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() p.join()
# If returns a StopPhase, raise it # If returns a StopPhase, raise it
@ -1212,6 +1227,10 @@ def child_fun():
child_result.print_context() child_result.print_context()
raise child_result 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 return child_result