Skip to content

Commit 9d24231

Browse files
committed
Avoid blocking a Swift Task in the read() syscall
Add a DispatchIO-based helper which returns the data asynchronously.
1 parent f0af94b commit 9d24231

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

Sources/SWBServiceCore/ServiceHostConnection.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,13 @@ final class ServiceHostConnection: @unchecked Sendable {
148148
#endif
149149

150150
// Read data.
151-
let result = read(self.inputFD.rawValue, tmp.baseAddress, numericCast(tmpBufferSize))
152-
if result < 0 {
153-
if errno == EINTR { continue }
154-
error = ServiceHostIOError(message: "read from client failed", cause: SWBUtil.POSIXError(errno, context: "read"))
151+
let result: Int
152+
do {
153+
let buf = try await DispatchFD(fileDescriptor: self.inputFD).readChunk(upToLength: tmpBufferSize)
154+
result = buf.count
155+
buf.copyBytes(to: tmp)
156+
} catch let readError {
157+
error = ServiceHostIOError(message: "read from client failed", cause: readError)
155158
break
156159
}
157160
if result == 0 {

Sources/SWBUtil/Dispatch+Async.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// This file contains helpers used to bridge GCD and Swift Concurrency.
1414
// In the long term, these ideally all go away.
1515

16-
private import Foundation
16+
public import Foundation
1717

1818
/// Runs an async function and synchronously waits for the response.
1919
/// - warning: This function is extremely dangerous because it blocks the calling thread and may lead to deadlock, and should only be used as a temporary transitional aid.
@@ -41,6 +41,24 @@ public func runAsyncAndBlock<T: Sendable, E>(_ block: @Sendable @escaping () asy
4141
return try result.value!.get()
4242
}
4343

44+
extension DispatchFD {
45+
public func readChunk(upToLength maxLength: Int) async throws -> some DataProtocol {
46+
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<DispatchData, any Error>) in
47+
SWBDispatchIO.read(
48+
fromFileDescriptor: self,
49+
maxLength: maxLength,
50+
runningHandlerOn: .global()
51+
) { data, error in
52+
if error != 0 {
53+
continuation.resume(throwing: POSIXError(error))
54+
return
55+
}
56+
continuation.resume(returning: data)
57+
}
58+
}
59+
}
60+
}
61+
4462
extension AsyncThrowingStream where Element == UInt8, Failure == any Error {
4563
/// Returns an async stream which reads bytes from the specified file descriptor. Unlike `FileHandle.bytes`, it does not block the caller.
4664
@available(macOS, deprecated: 15.0, message: "Use the AsyncSequence-returning overload.")

Sources/SWBUtil/SWBDispatch.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ public final class SWBDispatchIO: Sendable {
183183
io = DispatchIO(type: .stream, fileDescriptor: numericCast(fileDescriptor), queue: queue.queue, cleanupHandler: cleanupHandler)
184184
}
185185

186+
public static func read(fromFileDescriptor fileDescriptor: DispatchFD, maxLength: Int, runningHandlerOn queue: SWBQueue, handler: @escaping (DispatchData, Int32) -> Void) {
187+
// Most of the dispatch APIs take a parameter called "fileDescriptor". On Windows (except makeReadSource and makeWriteSource) it is actually a HANDLE, so convert it accordingly.
188+
DispatchIO.read(fileDescriptor: numericCast(fileDescriptor.rawValue), maxLength: maxLength, queue: queue.queue, handler: handler)
189+
}
190+
186191
public static func stream(fileDescriptor: DispatchFD, queue: SWBQueue, cleanupHandler: @escaping (Int32) -> Void) -> SWBDispatchIO {
187192
// Most of the dispatch APIs take a parameter called "fileDescriptor". On Windows (except makeReadSource and makeWriteSource) it is actually a HANDLE, so convert it accordingly.
188193
SWBDispatchIO(fileDescriptor: numericCast(fileDescriptor.rawValue), queue: queue, cleanupHandler: cleanupHandler)

0 commit comments

Comments
 (0)