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:
		@@ -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 child_fun():
 | 
			
		||||
    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 child_fun():
 | 
			
		||||
                serialized_pkg,
 | 
			
		||||
                function,
 | 
			
		||||
                kwargs,
 | 
			
		||||
                child_pipe,
 | 
			
		||||
                write_pipe,
 | 
			
		||||
                input_multiprocess_fd,
 | 
			
		||||
                jobserver_fd1,
 | 
			
		||||
                jobserver_fd2,
 | 
			
		||||
@@ -1183,6 +1183,12 @@ def child_fun():
 | 
			
		||||
 | 
			
		||||
        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 child_fun():
 | 
			
		||||
        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 child_fun():
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user