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:
parent
d0412c1578
commit
f8e0c8caed
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user