From a6c926fa3435daeb21bd2110109736529eb16746 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 21 May 2026 11:16:17 -0700 Subject: [PATCH] Relax atomic memory orderings; switch semaphore to release/acquire For ID counters and standalone flags (most sites), drop to .relaxed since nothing reads other memory based on the atomic's value. MultiEntrySemaphore.signaled is the exception: it is used as a signal/wait primitive where waiters proceed to use state set up before signal(). Use .releasing on store and .acquiring on load so that pre-signal writes are visible to waiters that observe `true`. .relaxed there would be incorrect on weakly-ordered architectures. --- .../SwiftPMBuildServer.swift | 2 +- .../TestExtensions.swift | 4 ++-- .../InProcessSourceKitLSPClient.swift | 2 +- Sources/SKTestSupport/MultiEntrySemaphore.swift | 4 ++-- .../SKTestSupport/TestSourceKitLSPClient.swift | 4 ++-- .../PreparationTaskDescription.swift | 2 +- Sources/SemanticIndex/TaskScheduler.swift | 16 ++++++++-------- .../UpdateIndexStoreTaskDescription.swift | 2 +- .../CodeCompletionSession.swift | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Sources/BuildServerIntegration/SwiftPMBuildServer.swift b/Sources/BuildServerIntegration/SwiftPMBuildServer.swift index 136b68ab..9a814ab8 100644 --- a/Sources/BuildServerIntegration/SwiftPMBuildServer.swift +++ b/Sources/BuildServerIntegration/SwiftPMBuildServer.swift @@ -832,7 +832,7 @@ package actor SwiftPMBuildServer: BuiltInBuildServer { let start = ContinuousClock.now let taskID: TaskId = TaskId( - id: "preparation-\(preparationTaskID.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue)" + id: "preparation-\(preparationTaskID.wrappingAdd(1, ordering: .relaxed).oldValue)" ) connectionToSourceKitLSP.send( BuildServerProtocol.OnBuildLogMessageNotification( diff --git a/Sources/CompletionScoringTestSupport/TestExtensions.swift b/Sources/CompletionScoringTestSupport/TestExtensions.swift index 70187569..1e4ec91d 100644 --- a/Sources/CompletionScoringTestSupport/TestExtensions.swift +++ b/Sources/CompletionScoringTestSupport/TestExtensions.swift @@ -116,9 +116,9 @@ extension XCTestCase { } let logFD = try FileHandle(forWritingAtPath: path).unwrap(orThrow: "Opening \(path) failed") try logFD.seekToEnd() - if printBeginingOfLog.load(ordering: .sequentiallyConsistent) { + if printBeginingOfLog.load(ordering: .relaxed) { try logFD.print("========= \(Date().description(with: .current)) =========") - printBeginingOfLog.store(false, ordering: .sequentiallyConsistent) + printBeginingOfLog.store(false, ordering: .relaxed) } return logFD } diff --git a/Sources/InProcessClient/InProcessSourceKitLSPClient.swift b/Sources/InProcessClient/InProcessSourceKitLSPClient.swift index 04caa12e..ca0febb9 100644 --- a/Sources/InProcessClient/InProcessSourceKitLSPClient.swift +++ b/Sources/InProcessClient/InProcessSourceKitLSPClient.swift @@ -125,7 +125,7 @@ public final class InProcessSourceKitLSPClient: Sendable { reply: @Sendable @escaping (LSPResult) -> Void ) -> RequestID { let requestID = RequestID.string( - "sk-\(Int(nextRequestID.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue))" + "sk-\(Int(nextRequestID.wrappingAdd(1, ordering: .relaxed).oldValue))" ) server.handle(request, id: requestID, reply: reply) return requestID diff --git a/Sources/SKTestSupport/MultiEntrySemaphore.swift b/Sources/SKTestSupport/MultiEntrySemaphore.swift index 85e9d629..6880a51e 100644 --- a/Sources/SKTestSupport/MultiEntrySemaphore.swift +++ b/Sources/SKTestSupport/MultiEntrySemaphore.swift @@ -29,13 +29,13 @@ package final class MultiEntrySemaphore: Sendable { } package func signal() { - signaled.store(true, ordering: .sequentiallyConsistent) + signaled.store(true, ordering: .releasing) } package func waitOrThrow() async throws { do { try await repeatUntilExpectedResult(sleepInterval: .seconds(0.01)) { - signaled.load(ordering: .sequentiallyConsistent) + signaled.load(ordering: .acquiring) } } catch { struct TimeoutError: Error, CustomStringConvertible { diff --git a/Sources/SKTestSupport/TestSourceKitLSPClient.swift b/Sources/SKTestSupport/TestSourceKitLSPClient.swift index 91c7ae48..bb1a9185 100644 --- a/Sources/SKTestSupport/TestSourceKitLSPClient.swift +++ b/Sources/SKTestSupport/TestSourceKitLSPClient.swift @@ -245,7 +245,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable { let shutdownSemaphore = WrappedSemaphore(name: "Shutdown") server.handle( ShutdownRequest(), - id: .number(Int(nextRequestID.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue)) + id: .number(Int(nextRequestID.wrappingAdd(1, ordering: .relaxed).oldValue)) ) { result in shutdownSemaphore.signal() } @@ -293,7 +293,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable { _ request: R, completionHandler: @Sendable @escaping (LSPResult) -> Void ) -> RequestID { - let requestID = RequestID.number(Int(nextRequestID.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue)) + let requestID = RequestID.number(Int(nextRequestID.wrappingAdd(1, ordering: .relaxed).oldValue)) let replyOutstanding = ThreadSafeBox(initialValue: true) let timeoutTask = Task { try await Task.sleep(for: defaultTimeoutDuration) diff --git a/Sources/SemanticIndex/PreparationTaskDescription.swift b/Sources/SemanticIndex/PreparationTaskDescription.swift index b6f64162..c861a6dc 100644 --- a/Sources/SemanticIndex/PreparationTaskDescription.swift +++ b/Sources/SemanticIndex/PreparationTaskDescription.swift @@ -31,7 +31,7 @@ private let preparationIDForLogging = Atomic(1) package struct PreparationTaskDescription: IndexTaskDescription { package static let idPrefix = "prepare" - package let id = preparationIDForLogging.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue + package let id = preparationIDForLogging.wrappingAdd(1, ordering: .relaxed).oldValue /// The targets that should be prepared. package let targetsToPrepare: [BuildTargetIdentifier] diff --git a/Sources/SemanticIndex/TaskScheduler.swift b/Sources/SemanticIndex/TaskScheduler.swift index 87700850..0b66e283 100644 --- a/Sources/SemanticIndex/TaskScheduler.swift +++ b/Sources/SemanticIndex/TaskScheduler.swift @@ -136,10 +136,10 @@ package actor QueuedTask { /// it, the priority may get elevated. nonisolated var priority: TaskPriority { get { - TaskPriority(rawValue: _priority.load(ordering: .sequentiallyConsistent)) + TaskPriority(rawValue: _priority.load(ordering: .relaxed)) } set { - _priority.store(newValue.rawValue, ordering: .sequentiallyConsistent) + _priority.store(newValue.rawValue, ordering: .relaxed) } } @@ -155,7 +155,7 @@ package actor QueuedTask { /// Whether the task is currently executing or still queued to be executed later. package nonisolated var isExecuting: Bool { - return _isExecuting.load(ordering: .sequentiallyConsistent) + return _isExecuting.load(ordering: .relaxed) } package nonisolated func cancel() { @@ -222,7 +222,7 @@ package actor QueuedTask { taskPriorityChangedCallback(self.priority) } } onCancel: { - self.resultTaskCancelled.store(true, ordering: .sequentiallyConsistent) + self.resultTaskCancelled.store(true, ordering: .relaxed) } } } @@ -243,15 +243,15 @@ package actor QueuedTask { } precondition(executionTask == nil, "Task started twice") let task = Task.detached(priority: self.priority) { - if !Task.isCancelled && !self.resultTaskCancelled.load(ordering: .sequentiallyConsistent) { + if !Task.isCancelled && !self.resultTaskCancelled.load(ordering: .relaxed) { await self.description.execute() } return await self.finalizeExecution() } - _isExecuting.store(true, ordering: .sequentiallyConsistent) + _isExecuting.store(true, ordering: .relaxed) executionTask = task executionTaskCreatedContinuation.yield(task) - if self.resultTaskCancelled.load(ordering: .sequentiallyConsistent) { + if self.resultTaskCancelled.load(ordering: .relaxed) { // The queued task might have been cancelled after the execution ask was started but before the task was yielded // to `executionTaskCreatedContinuation`. In that case the result task will simply cancel the await on the // `executionTaskCreatedStream` and hence not call `valuePropagatingCancellation` on the execution task. This @@ -266,7 +266,7 @@ package actor QueuedTask { /// Implementation detail of `execute` that is called after `self.description.execute()` finishes. private func finalizeExecution() async -> ExecutionTaskFinishStatus { self.executionTask = nil - _isExecuting.store(false, ordering: .sequentiallyConsistent) + _isExecuting.store(false, ordering: .relaxed) if Task.isCancelled && self.cancelledToBeRescheduled { await executionStateChangedCallback?(self, .cancelledToBeRescheduled) self.cancelledToBeRescheduled = false diff --git a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift index af387d60..3d588416 100644 --- a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift +++ b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift @@ -115,7 +115,7 @@ private enum UpdateIndexStorePartition { /// This task description can be scheduled in a `TaskScheduler`. package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { package static let idPrefix = "update-indexstore" - package let id = updateIndexStoreIDForLogging.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue + package let id = updateIndexStoreIDForLogging.wrappingAdd(1, ordering: .relaxed).oldValue /// The files that should be indexed. package let filesToIndex: [FileAndOutputPath] diff --git a/Sources/SwiftLanguageService/CodeCompletionSession.swift b/Sources/SwiftLanguageService/CodeCompletionSession.swift index 0d4de3fa..ccb33e3f 100644 --- a/Sources/SwiftLanguageService/CodeCompletionSession.swift +++ b/Sources/SwiftLanguageService/CodeCompletionSession.swift @@ -39,7 +39,7 @@ struct CompletionSessionID: Equatable, Codable { } static func next() -> CompletionSessionID { - return CompletionSessionID(value: nextSessionID.wrappingAdd(1, ordering: .sequentiallyConsistent).oldValue) + return CompletionSessionID(value: nextSessionID.wrappingAdd(1, ordering: .relaxed).oldValue) } init(from decoder: any Decoder) throws {