diff --git a/Sources/SWBServiceCore/ServiceHostConnection.swift b/Sources/SWBServiceCore/ServiceHostConnection.swift index a3af56e5..59d1a172 100644 --- a/Sources/SWBServiceCore/ServiceHostConnection.swift +++ b/Sources/SWBServiceCore/ServiceHostConnection.swift @@ -148,10 +148,13 @@ final class ServiceHostConnection: @unchecked Sendable { #endif // Read data. - let result = read(self.inputFD.rawValue, tmp.baseAddress, numericCast(tmpBufferSize)) - if result < 0 { - if errno == EINTR { continue } - error = ServiceHostIOError(message: "read from client failed", cause: SWBUtil.POSIXError(errno, context: "read")) + let result: Int + do { + let buf = try await DispatchFD(fileDescriptor: self.inputFD).readChunk(upToLength: tmpBufferSize) + result = buf.count + buf.copyBytes(to: tmp) + } catch let readError { + error = ServiceHostIOError(message: "read from client failed", cause: readError) break } if result == 0 { diff --git a/Sources/SWBUtil/Dispatch+Async.swift b/Sources/SWBUtil/Dispatch+Async.swift index cde7d4c1..bf70a86d 100644 --- a/Sources/SWBUtil/Dispatch+Async.swift +++ b/Sources/SWBUtil/Dispatch+Async.swift @@ -13,7 +13,7 @@ // This file contains helpers used to bridge GCD and Swift Concurrency. // In the long term, these ideally all go away. -private import Foundation +import Foundation /// Runs an async function and synchronously waits for the response. /// - 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(_ block: @Sendable @escaping () asy return try result.value!.get() } +extension DispatchFD { + public func readChunk(upToLength maxLength: Int) async throws -> SWBDispatchData { + return try await withCheckedThrowingContinuation { continuation in + SWBDispatchIO.read( + fromFileDescriptor: self, + maxLength: maxLength, + runningHandlerOn: .global() + ) { data, error in + if error != 0 { + continuation.resume(throwing: POSIXError(error)) + return + } + continuation.resume(returning: data) + } + } + } +} + extension AsyncThrowingStream where Element == UInt8, Failure == any Error { /// Returns an async stream which reads bytes from the specified file descriptor. Unlike `FileHandle.bytes`, it does not block the caller. @available(macOS, deprecated: 15.0, message: "Use the AsyncSequence-returning overload.") diff --git a/Sources/SWBUtil/SWBDispatch.swift b/Sources/SWBUtil/SWBDispatch.swift index 089bd2e9..4845461f 100644 --- a/Sources/SWBUtil/SWBDispatch.swift +++ b/Sources/SWBUtil/SWBDispatch.swift @@ -15,7 +15,7 @@ private import Dispatch public import SWBLibc -import Foundation +public import Foundation #if canImport(System) public import System @@ -123,6 +123,12 @@ extension SWBDispatchData: RandomAccessCollection { } } +extension SWBDispatchData: DataProtocol { + public var regions: DispatchData.Regions { + dispatchData.regions + } +} + /// Thin wrapper for `DispatchSemaphore` to isolate it from the rest of the codebase and help migration away from it. internal final class SWBDispatchSemaphore: Sendable { private let semaphore: DispatchSemaphore @@ -183,6 +189,13 @@ public final class SWBDispatchIO: Sendable { io = DispatchIO(type: .stream, fileDescriptor: numericCast(fileDescriptor), queue: queue.queue, cleanupHandler: cleanupHandler) } + public static func read(fromFileDescriptor fileDescriptor: DispatchFD, maxLength: Int, runningHandlerOn queue: SWBQueue, handler: @escaping (SWBDispatchData, Int32) -> Void) { + // 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. + DispatchIO.read(fromFileDescriptor: numericCast(fileDescriptor.rawValue), maxLength: maxLength, runningHandlerOn: queue.queue) { data, error in + handler(SWBDispatchData(data), error) + } + } + public static func stream(fileDescriptor: DispatchFD, queue: SWBQueue, cleanupHandler: @escaping (Int32) -> Void) -> SWBDispatchIO { // 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. SWBDispatchIO(fileDescriptor: numericCast(fileDescriptor.rawValue), queue: queue, cleanupHandler: cleanupHandler)