-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Async Process IO hangs 100% of the time on Linux #5197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I've updated the reproduction a bit...
|
I can confirm, that I have also noticed unpredictable hangs when using the This is might be a duplicate of: #3275 (which would make it less unpredictable) Instead, I have now switched to reading from the file handlers more directly, see below. Note that this will buffer all output in memory. let outputData : Mutex<Data?> = Mutex(nil)
let errorData : Mutex<Data?> = Mutex(nil)
let group = DispatchGroup()
let task = Foundation.Process()
task.executableURL = executable
task.arguments = Array(arguments.dropFirst())
let outputPipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = outputPipe
task.standardError = errorPipe
try task.run()
group.enter()
Thread { // read full output in a separate thread
let data = try? outputPipe.fileHandleForReading.readToEnd()
outputData.withLock {
$0 = data
}
group.leave()
}.start()
group.enter()
Thread { // read full error output in a separate thread
let data = try? errorPipe.fileHandleForReading.readToEnd()
errorData.withLock {
$0 = data
}
group.leave()
}.start()
task.waitUntilExit()
// wait until the reader threads complete
if .timedOut == group.wait(timeout: .now() + .seconds(10)) {
fatalError("Task exited, but timed out waiting for output.")
}
guard task.terminationStatus == 0 else {
let message = errorData.withLock { $0.flatMap { String(data: $0, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) } } ?? ""
throw ShellError.failure(terminationStatus: Int(task.terminationStatus), errorMessage: message, arguments: arguments)
}
return outputData.withLock { $0 } |
I've tried this implementation but ran into issues with https://gist.github.com/danpalmer/762c1cf28fe8ea3d9013c4b73492bc36 This still exhibits the same issue, hanging, and also printing I think there might actually be two issues here:
I'd rather focus on the second here, as it's the blocking issue for me – so far I've not managed to find an implementation that successfully passes my project's test suite under Linux. Interestingly however, it does sometimes work. One of the tests goes like this:
The new detail I've figured out is that step 1 works, so I've strace'd step 3, and here's what I've got: https://gist.github.com/danpalmer/f0caed5cd673013787ac142067f1f659 - this shows:
I'm still investigating, but thought I'd give an update. |
I've re-run the test, but changing the process to run Seems to me like there's some state in the process exit handling that isn't getting cleaned up. |
Sidenote, I think I you should not run |
Makes sense, at this point I'm treating the above implementation as a simple test case for the "wakeup socket pair" issue, because mature libraries like SwiftCommand and tuist/Command exhibit the same issue, so for production code I'd just use one of those. I don't think calling |
Summary
I have a wrapper around
Process
designed to asynchronously run and read stdout/stderr in order to allow for the following API:Under macOS (15.x) this works, but under Linux, specifically using the
swift:6
Docker image, this hangs indefinitely.Detailed issue
Under some running conditions it's possible to observe the following error:
Additionally, although not my concern it may help debug, the same code crashes with a bad pointer dereference in libswift_Concurrency.so on Swift 5.9.
strace
'ing this suggests that the subprocess does indeed run and complete, so indicates an issue with the Swift run loop.One possibility is that there's a file handle limit preventing the I/O from happening, however
ulimit
suggests that this container has no limits that would be causing an issue.Given that the same code works on macOS and does not on Linux, suggests to me a bug in the framework. While I'm conscious that it's very easy to introduce races in code like this (and I have fixed several in this area), a difference in behaviour like this should be impossible, or at least documented and obvious.
Reproduction
I've create a relatively minimal reproduction, with code similar to what's in my project, available here: https://github.com/danpalmer/swift-linux-issue 1.
To run this you'll need to install a Docker provider on macOS such as OrbStack.
Happy to answer any questions or provide further follow-up. This area is not something I know a lot about – I would consider myself an intermediate Swift developer and a relatively basic Linux user, so it's quite possible that I've missed obvious things that I should go back and try before further investigation occurs.
Footnotes
Please note that this was AI generated, but a) appears to reproduce the issue correctly working as expected on macOS and hanging in the same way on Linux, and b) is highly representative of both the current code I have in my project and an open source library that both exhibit the same issue in the same way. ↩
The text was updated successfully, but these errors were encountered: