84 lines
3.5 KiB
Diff
84 lines
3.5 KiB
Diff
|
commit 7d0b78d00f6ad5278e9273a10415cf36fe349129
|
|||
|
Author: Daniel Rodríguez Troitiño <danielrodriguez@fb.com>
|
|||
|
Date: Mon May 6 11:06:51 2019 -0700
|
|||
|
|
|||
|
[android] Stop leaking FDs in parent test process.
|
|||
|
|
|||
|
In the Android paths of the spawnChild function, the parent was creating
|
|||
|
a pipe that was never closed, which led to FD starvation. In some tests
|
|||
|
with a lots of expected crashes, the childs will not spawn anymore since
|
|||
|
the linker would not have enough descriptors to open the shared
|
|||
|
libraries, while in other tests which closed the child descriptors as
|
|||
|
part of the last test, the parent process will hang waiting those
|
|||
|
descriptors to be closed, which will never had happened.
|
|||
|
|
|||
|
The solution is implement the missing parts of the code, which tried to
|
|||
|
read from the pipe in the parent side (using select and read, taking
|
|||
|
pieces from other parts of the code). This should match the fork/execv
|
|||
|
path used by Android and Haiku to the spawn code used by the rest of the
|
|||
|
platforms.
|
|||
|
|
|||
|
This change fixes StdlibUnittest/Stdin.swift,
|
|||
|
stdlib/InputStream.swift.gyb,
|
|||
|
stdlib/Collection/FlattenCollection.swift.gyb and
|
|||
|
stdlib/Collection/LazyFilterCollection.swift.gyb, which were the last 4
|
|||
|
tests failing in Android AArch64.
|
|||
|
|
|||
|
diff --git a/swift/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/swift/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift
|
|||
|
index e31b3993e6..e95e07e142 100644
|
|||
|
--- a/swift/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift
|
|||
|
+++ b/swift/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift
|
|||
|
@@ -217,6 +217,10 @@ public func spawnChild(_ args: [String])
|
|||
|
if pid == 0 {
|
|||
|
// pid of 0 means we are now in the child process.
|
|||
|
// Capture the output before executing the program.
|
|||
|
+ close(childStdout.readFD)
|
|||
|
+ close(childStdin.writeFD)
|
|||
|
+ close(childStderr.readFD)
|
|||
|
+ close(childToParentPipe.readFD)
|
|||
|
dup2(childStdout.writeFD, STDOUT_FILENO)
|
|||
|
dup2(childStdin.readFD, STDIN_FILENO)
|
|||
|
dup2(childStderr.writeFD, STDERR_FILENO)
|
|||
|
@@ -261,6 +265,41 @@ public func spawnChild(_ args: [String])
|
|||
|
|
|||
|
// Close the pipe when we're done writing the error.
|
|||
|
close(childToParentPipe.writeFD)
|
|||
|
+ } else {
|
|||
|
+ close(childToParentPipe.writeFD)
|
|||
|
+
|
|||
|
+ // Figure out if the child’s call to execve was successful or not.
|
|||
|
+ var readfds = _stdlib_fd_set()
|
|||
|
+ readfds.set(childToParentPipe.readFD)
|
|||
|
+ var writefds = _stdlib_fd_set()
|
|||
|
+ var errorfds = _stdlib_fd_set()
|
|||
|
+ errorfds.set(childToParentPipe.readFD)
|
|||
|
+
|
|||
|
+ var ret: CInt
|
|||
|
+ repeat {
|
|||
|
+ ret = _stdlib_select(&readfds, &writefds, &errorfds, nil)
|
|||
|
+ } while ret == -1 && errno == EINTR
|
|||
|
+ if ret <= 0 {
|
|||
|
+ fatalError("select() returned an error: \(errno)")
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ if readfds.isset(childToParentPipe.readFD) || errorfds.isset(childToParentPipe.readFD) {
|
|||
|
+ var childErrno: CInt = 0
|
|||
|
+ let readResult: ssize_t = withUnsafeMutablePointer(to: &childErrno) {
|
|||
|
+ return read(childToParentPipe.readFD, $0, MemoryLayout.size(ofValue: $0.pointee))
|
|||
|
+ }
|
|||
|
+ if readResult == 0 {
|
|||
|
+ // We read an EOF indicating that the child's call to execve was successful.
|
|||
|
+ } else if readResult < 0 {
|
|||
|
+ fatalError("read() returned error: \(errno)")
|
|||
|
+ } else {
|
|||
|
+ // We read an error from the child.
|
|||
|
+ print(String(cString: strerror(childErrno)))
|
|||
|
+ preconditionFailure("execve() failed")
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ close(childToParentPipe.readFD)
|
|||
|
}
|
|||
|
#else
|
|||
|
var fileActions = _make_posix_spawn_file_actions_t()
|