diff --git a/Sources/SWBBuildSystem/BuildOperation.swift b/Sources/SWBBuildSystem/BuildOperation.swift index 49673a9f..9efc58ee 100644 --- a/Sources/SWBBuildSystem/BuildOperation.swift +++ b/Sources/SWBBuildSystem/BuildOperation.swift @@ -1254,7 +1254,7 @@ private class InProcessCommand: SWBLLBuild.ExternalCommand, SWBLLBuild.ExternalD // Get the current output delegate from the adaptor. // // FIXME: This should never fail (since we are executing), but we have seen a crash here with that assumption. For now we are defensive until the source can be tracked down: Diagnose unexpected missing output delegate from: Crash in InProcessCommand.execute() - guard let outputDelegate = adaptor.getActiveOutputDelegate(command) else { + guard let outputDelegate = await adaptor.getActiveOutputDelegate(command) else { return .failed } @@ -1604,9 +1604,9 @@ internal final class OperationSystemAdaptor: SWBLLBuild.BuildSystemDelegate, Act /// Get the active output delegate for an executing command. /// /// - returns: The active delegate, or nil if not found. - func getActiveOutputDelegate(_ command: Command) -> (any TaskOutputDelegate)? { + func getActiveOutputDelegate(_ command: Command) async -> (any TaskOutputDelegate)? { // FIXME: This is a very bad idea, doing a sync against the response queue is introducing artificial latency when an in-process command needs to wait for the response queue to flush. However, we also can't simply move to a decoupled lock, because we don't want the command to start reporting output before it has been fully reported as having started. We need to move in-process task to another model. - return queue.blocking_sync { + return await queue.sync { self.commandOutputDelegates[command] } } diff --git a/Sources/SWBCore/LibSwiftDriver/LibSwiftDriver.swift b/Sources/SWBCore/LibSwiftDriver/LibSwiftDriver.swift index fa323660..bd6cd847 100644 --- a/Sources/SWBCore/LibSwiftDriver/LibSwiftDriver.swift +++ b/Sources/SWBCore/LibSwiftDriver/LibSwiftDriver.swift @@ -238,9 +238,9 @@ public final class SwiftModuleDependencyGraph: SwiftGlobalExplicitDependencyGrap return fileDependencies } - public func queryTransitiveDependencyModuleNames(for key: String) throws -> [String] { - let graph = try registryQueue.blocking_sync { - guard let driver = registry[key] else { + public func queryTransitiveDependencyModuleNames(for key: String) async throws -> [String] { + let graph = try await registryQueue.sync { + guard let driver = self.registry[key] else { throw StubError.error("Unable to find jobs for key \(key). Be sure to plan the build ahead of fetching results.") } return driver.intermoduleDependencyGraph diff --git a/Sources/SWBTaskExecution/TaskActions/SwiftDriverTaskAction.swift b/Sources/SWBTaskExecution/TaskActions/SwiftDriverTaskAction.swift index 864b1f72..512673c1 100644 --- a/Sources/SWBTaskExecution/TaskActions/SwiftDriverTaskAction.swift +++ b/Sources/SWBTaskExecution/TaskActions/SwiftDriverTaskAction.swift @@ -94,7 +94,7 @@ final public class SwiftDriverTaskAction: TaskAction, BuildValueValidatingTaskAc } if driverPayload.reportRequiredTargetDependencies != .no && driverPayload.explicitModulesEnabled, let target = task.forTarget { - let dependencyModuleNames = try dependencyGraph.queryTransitiveDependencyModuleNames(for: driverPayload.uniqueID) + let dependencyModuleNames = try await dependencyGraph.queryTransitiveDependencyModuleNames(for: driverPayload.uniqueID) for dependencyModuleName in dependencyModuleNames { if let targetDependencies = dynamicExecutionDelegate.operationContext.definingTargetsByModuleName[dependencyModuleName] { for targetDependency in targetDependencies { diff --git a/Sources/SWBUtil/SWBDispatch.swift b/Sources/SWBUtil/SWBDispatch.swift index 4845461f..26aad751 100644 --- a/Sources/SWBUtil/SWBDispatch.swift +++ b/Sources/SWBUtil/SWBDispatch.swift @@ -295,6 +295,20 @@ public final class SWBQueue: Sendable { } } + /// Submits a block object for execution and returns after that block finishes executing. + /// - note: This implementation won't block the calling thread, unlike the synchronous overload of ``sync()``. + public func sync(qos: SWBQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute block: @Sendable @escaping () throws -> T) async throws -> T { + try await withCheckedThrowingContinuation { continuation in + queue.async(qos: qos.dispatchQoS, flags: flags.dispatchFlags) { + do { + continuation.resume(returning: try block()) + } catch { + continuation.resume(throwing: error) + } + } + } + } + public func async(group: SWBDispatchGroup? = nil, qos: SWBQoS = .unspecified, execute body: @escaping @Sendable () -> Void) { return queue.async(group: group?.group, qos: qos.dispatchQoS, execute: body) }