Created
April 13, 2016 01:17
-
-
Save modocache/c035decc101f7d53be65c45b6cbdfae2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb b/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb | |
index f03e020..73e99d7 100644 | |
--- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb | |
+++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb | |
@@ -516,6 +516,7 @@ struct _ParentProcess { | |
internal var _childStdin: _FDOutputStream = _FDOutputStream(fd: -1) | |
internal var _childStdout: _FDInputStream = _FDInputStream(fd: -1) | |
internal var _childStderr: _FDInputStream = _FDInputStream(fd: -1) | |
+ internal var _childToParent: _FDInputStream = _FDInputStream(fd: -1) | |
internal var _runTestsInProcess: Bool | |
internal var _filter: String? | |
@@ -529,22 +530,30 @@ struct _ParentProcess { | |
mutating func _spawnChild() { | |
let params = [ "--stdlib-unittest-run-child" ] + _args | |
- let (pid, childStdinFD, childStdoutFD, childStderrFD) = spawnChild(params) | |
+ let ( | |
+ pid, | |
+ childStdinFD, | |
+ childStdoutFD, | |
+ childStderrFD, | |
+ childToParentFD) = spawnChild(params) | |
_pid = pid | |
_childStdin = _FDOutputStream(fd: childStdinFD) | |
_childStdout = _FDInputStream(fd: childStdoutFD) | |
_childStderr = _FDInputStream(fd: childStderrFD) | |
+ _childToParent = _FDInputStream(fd: childToParentFD) | |
} | |
mutating func _waitForChild() -> ProcessTerminationStatus { | |
- let status = posixWaitpid(_pid) | |
+ let status = posixWaitpid(_pid, childToParentFD: _childToParent.fd) | |
_pid = -1 | |
_childStdin.close() | |
_childStdout.close() | |
_childStderr.close() | |
+ _childToParent.close() | |
_childStdin = _FDOutputStream(fd: -1) | |
_childStdout = _FDInputStream(fd: -1) | |
_childStderr = _FDInputStream(fd: -1) | |
+ _childToParent = _FDInputStream(fd: -1) | |
return status | |
} | |
diff --git a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift | |
index 2857a93..84419bb 100644 | |
--- a/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift | |
+++ b/stdlib/private/SwiftPrivateLibcExtras/Subprocess.swift | |
@@ -74,19 +74,36 @@ func posixPipe() -> (readFD: CInt, writeFD: CInt) { | |
/// Start the same executable as a child process, redirecting its stdout and | |
/// stderr. | |
public func spawnChild(_ args: [String]) | |
- -> (pid: pid_t, stdinFD: CInt, stdoutFD: CInt, stderrFD: CInt) { | |
+ -> ( | |
+ pid: pid_t, | |
+ stdinFD: CInt, | |
+ stdoutFD: CInt, | |
+ stderrFD: CInt, | |
+ childToParentFD: CInt) { | |
// The stdout, stdin, and stderr from the child process will be redirected | |
// to these pipes. | |
let childStdout = posixPipe() | |
let childStdin = posixPipe() | |
let childStderr = posixPipe() | |
-#if os(Android) | |
+ var childArgs = args | |
+ childArgs.insert(Process.arguments[0], at: 0) | |
+ let interpreter = getenv("SWIFT_INTERPRETER") | |
+ if interpreter != nil { | |
+ if let invocation = String(validatingUTF8: interpreter) { | |
+ childArgs.insert(invocation, at: 0) | |
+ } | |
+ } | |
+ | |
// posix_spawn isn't available on Android. Instead, we fork and exec. | |
// To correctly communicate the exit status of the child process to this | |
// (parent) process, we'll use this pipe. | |
let childToParentPipe = posixPipe() | |
+ if close(childToParentPipe.readFD) != 0 { | |
+ preconditionFailure("close() failed") | |
+ } | |
+#if os(Android) | |
let pid = fork() | |
precondition(pid >= 0, "fork() failed") | |
if pid == 0 { | |
@@ -109,7 +126,7 @@ public func spawnChild(_ args: [String]) | |
// Start the executable. If execve() does not encounter an error, the | |
// code after this block will never be executed, and the parent write pipe | |
// will be closed. | |
- withArrayOfCStrings([Process.arguments[0]] + args) { | |
+ withArrayOfCStrings(childArgs) { | |
execve(Process.arguments[0], $0, _getEnviron()) | |
} | |
@@ -138,6 +155,12 @@ public func spawnChild(_ args: [String]) | |
close(childToParentPipe.writeFD) | |
} | |
#else | |
+ // Close the write end of the child-to-parent pipe, as we don't use it on | |
+ // non-Android platforms. | |
+ if close(childStderr.writeFD) != 0 { | |
+ preconditionFailure("close() failed") | |
+ } | |
+ | |
var fileActions = _make_posix_spawn_file_actions_t() | |
if swift_posix_spawn_file_actions_init(&fileActions) != 0 { | |
preconditionFailure("swift_posix_spawn_file_actions_init() failed") | |
@@ -180,14 +203,6 @@ public func spawnChild(_ args: [String]) | |
} | |
var pid: pid_t = -1 | |
- var childArgs = args | |
- childArgs.insert(Process.arguments[0], at: 0) | |
- let interpreter = getenv("SWIFT_INTERPRETER") | |
- if interpreter != nil { | |
- if let invocation = String(validatingUTF8: interpreter) { | |
- childArgs.insert(invocation, at: 0) | |
- } | |
- } | |
let spawnResult = withArrayOfCStrings(childArgs) { | |
swift_posix_spawn( | |
&pid, childArgs[0], &fileActions, nil, $0, _getEnviron()) | |
@@ -217,7 +232,12 @@ public func spawnChild(_ args: [String]) | |
preconditionFailure("close() failed") | |
} | |
- return (pid, childStdin.writeFD, childStdout.readFD, childStderr.readFD) | |
+ return ( | |
+ pid, | |
+ childStdin.writeFD, | |
+ childStdout.readFD, | |
+ childStderr.readFD, | |
+ childToParentPipe.writeFD) | |
} | |
#if !os(Android) | |
@@ -284,11 +304,29 @@ public enum ProcessTerminationStatus : CustomStringConvertible { | |
} | |
} | |
-public func posixWaitpid(_ pid: pid_t) -> ProcessTerminationStatus { | |
+public func posixWaitpid(_ pid: pid_t, childToParentFD: CInt) -> ProcessTerminationStatus { | |
var status: CInt = 0 | |
if waitpid(pid, &status, 0) < 0 { | |
preconditionFailure("waitpid() failed") | |
} | |
+#if os(Android) | |
+ // Read the results from our child-to-parent pipe. | |
+ var result: Int = 0 | |
+ let bufferSize = 256 | |
+ var buffer = [Int8](repeating: 0, count: bufferSize) | |
+ buffer.withUnsafeMutableBufferPointer { | |
+ result = read(childToParentFD, $0.baseAddress, bufferSize) | |
+ } | |
+ precondition(result == 0, "Could not read from child-to-parent pipe.") | |
+ | |
+ withUnsafePointer(&buffer[0]) { | |
+ if let outputString = String(validatingUTF8: $0) { | |
+ if let output = CInt(outputString) { | |
+ status = output | |
+ } | |
+ } | |
+ } | |
+#endif | |
if (WIFEXITED(status)) { | |
return .exit(Int(WEXITSTATUS(status))) | |
} | |
@@ -300,7 +338,7 @@ public func posixWaitpid(_ pid: pid_t) -> ProcessTerminationStatus { | |
public func runChild(_ args: [String]) | |
-> (stdout: String, stderr: String, status: ProcessTerminationStatus) { | |
- let (pid, _, stdoutFD, stderrFD) = spawnChild(args) | |
+ let (pid, _, stdoutFD, stderrFD, childToParentFD) = spawnChild(args) | |
// FIXME: reading stdout and stderr sequentially can block. Should use | |
// select(). This is not so simple to implement because of: | |
@@ -314,7 +352,10 @@ public func runChild(_ args: [String]) | |
if close(stderrFD) != 0 { | |
preconditionFailure("close() failed") | |
} | |
- let status = posixWaitpid(pid) | |
+ let status = posixWaitpid(pid, childToParentFD: childToParentFD) | |
+ if close(childToParentFD) != 0 { | |
+ preconditionFailure("close() failed") | |
+ } | |
return (stdout, stderr, status) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment