Skip to content

Instantly share code, notes, and snippets.

@modocache
Created April 13, 2016 01:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save modocache/c035decc101f7d53be65c45b6cbdfae2 to your computer and use it in GitHub Desktop.
Save modocache/c035decc101f7d53be65c45b6cbdfae2 to your computer and use it in GitHub Desktop.
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