summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJordan Galby <67924449+Jordan474@users.noreply.github.com>2023-08-18 09:41:02 +0200
committerGitHub <noreply@github.com>2023-08-18 09:41:02 +0200
commitf8e0c8caed0a97aae9d5a3f85026dc45d0417012 (patch)
treee0092ce06a4d2d2fdb539b84aaa8ac58e9a691be /lib
parentd0412c1578ff1003c72352f94776d96d45282435 (diff)
downloadspack-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.py35
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