From 7c99f4f0e785906d36c5055a6e8e4d385be51732 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 22 May 2026 23:39:14 -0700 Subject: [PATCH 1/2] Use swift-tools-protocols's ThreadSafeBox Delete sourcekit-lsp's `Sources/SwiftExtensions/ThreadSafeBox.swift` and the now-unused `NSLock+WithLock.swift`. Use the `Mutex`-backed `ThreadSafeBox` from swift-tools-protocols' Adjust call sites for the new API: e.g. `foo.value.mutate()` with `foo.withLock { $0.mutate() }` because it doesn't provide `_modify` accessor. --- .../DocCReferenceResolutionService.swift | 3 +- .../TestSourceKitLSPClient.swift | 8 +-- Sources/SemanticIndex/CheckedIndex.swift | 1 + Sources/SourceKitD/SourceKitD.swift | 2 +- Sources/SourceKitD/dlopen.swift | 1 + Sources/SourceKitLSP/DocumentManager.swift | 1 + Sources/SwiftExtensions/CMakeLists.txt | 2 - Sources/SwiftExtensions/NSLock+WithLock.swift | 21 ------ Sources/SwiftExtensions/ThreadSafeBox.swift | 65 ------------------- Sources/ToolchainRegistry/Toolchain.swift | 1 + .../BuildServerManagerTests.swift | 1 + .../BackgroundIndexingTests.swift | 7 +- .../CrashRecoveryTests.swift | 1 + .../ExecuteCommandTests.swift | 1 + .../SourceKitLSPTests/ExpandMacroTests.swift | 1 + Tests/SourceKitLSPTests/LocalSwiftTests.swift | 5 +- .../SourcekitdCoreInjectorTests.swift | 5 +- 17 files changed, 26 insertions(+), 100 deletions(-) delete mode 100644 Sources/SwiftExtensions/NSLock+WithLock.swift delete mode 100644 Sources/SwiftExtensions/ThreadSafeBox.swift diff --git a/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift b/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift index d3bbbce98..35d2f07c4 100644 --- a/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift +++ b/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift @@ -18,6 +18,7 @@ import IndexStoreDB import SemanticIndex @_spi(Linkcompletion) @preconcurrency import SwiftDocC import SwiftExtensions +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions final class DocCReferenceResolutionService: DocumentationService, Sendable { /// The message type that this service accepts. @@ -37,7 +38,7 @@ final class DocCReferenceResolutionService: DocumentationService, Sendable { } @discardableResult func removeContext(forKey key: String) -> DocCReferenceResolutionContext? { - contextMap.value.removeValue(forKey: key) + contextMap.withLock { $0.removeValue(forKey: key) } } func context(forKey key: String) -> DocCReferenceResolutionContext? { diff --git a/Sources/SKTestSupport/TestSourceKitLSPClient.swift b/Sources/SKTestSupport/TestSourceKitLSPClient.swift index bb1a9185a..cce45f5a0 100644 --- a/Sources/SKTestSupport/TestSourceKitLSPClient.swift +++ b/Sources/SKTestSupport/TestSourceKitLSPClient.swift @@ -70,12 +70,12 @@ final class PendingNotifications: Sendable { private let values = ThreadSafeBox<[any NotificationType]>(initialValue: []) nonisolated func add(_ value: any NotificationType) { - values.value.insert(value, at: 0) + values.withLock { $0.insert(value, at: 0) } } func next(timeout: Duration, pollingInterval: Duration = .milliseconds(10)) async throws -> any NotificationType { for _ in 0..(_ requestHandler: @escaping RequestHandler) { - requestHandlers.value.append((requestHandler: requestHandler, isOneShot: true)) + requestHandlers.withLock { $0.append((requestHandler: requestHandler, isOneShot: true)) } } /// Handle all requests of the given type that are sent to the client. package func handleMultipleRequests(_ requestHandler: @escaping RequestHandler) { - requestHandlers.value.append((requestHandler: requestHandler, isOneShot: false)) + requestHandlers.withLock { $0.append((requestHandler: requestHandler, isOneShot: false)) } } /// Executes `operation` and waits until a request of the specified type is received from the server. diff --git a/Sources/SemanticIndex/CheckedIndex.swift b/Sources/SemanticIndex/CheckedIndex.swift index d0e0fbb93..8c20d004f 100644 --- a/Sources/SemanticIndex/CheckedIndex.swift +++ b/Sources/SemanticIndex/CheckedIndex.swift @@ -16,6 +16,7 @@ package import Foundation @_spi(SourceKitLSP) package import LanguageServerProtocol @_spi(SourceKitLSP) import SKLogging import SwiftExtensions +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions /// Essentially a `DocumentManager` from the `SourceKitLSP` module. /// diff --git a/Sources/SourceKitD/SourceKitD.swift b/Sources/SourceKitD/SourceKitD.swift index a02ebbcf1..782d79d78 100644 --- a/Sources/SourceKitD/SourceKitD.swift +++ b/Sources/SourceKitD/SourceKitD.swift @@ -23,7 +23,7 @@ extension sourcekitd_api_values: @unchecked Sendable {} fileprivate extension ThreadSafeBox { /// If the wrapped value is `nil`, run `compute` and store the computed value. If it is not `nil`, return the stored /// value. - func computeIfNil(compute: () -> WrappedValue) -> WrappedValue where T == WrappedValue? { + func computeIfNil(compute: () -> WrappedValue) -> WrappedValue where Value == WrappedValue? { return withLock { (value: inout WrappedValue?) -> WrappedValue in if let value { return value diff --git a/Sources/SourceKitD/dlopen.swift b/Sources/SourceKitD/dlopen.swift index c97861116..17ace3422 100644 --- a/Sources/SourceKitD/dlopen.swift +++ b/Sources/SourceKitD/dlopen.swift @@ -12,6 +12,7 @@ @_spi(SourceKitLSP) import SKLogging import SwiftExtensions +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions #if os(Windows) import CRT diff --git a/Sources/SourceKitLSP/DocumentManager.swift b/Sources/SourceKitLSP/DocumentManager.swift index 13efb6e54..c436fea6b 100644 --- a/Sources/SourceKitLSP/DocumentManager.swift +++ b/Sources/SourceKitLSP/DocumentManager.swift @@ -17,6 +17,7 @@ package import SKUtilities import SemanticIndex import SwiftExtensions package import SwiftSyntax +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions /// An immutable snapshot of a document at a given time. /// diff --git a/Sources/SwiftExtensions/CMakeLists.txt b/Sources/SwiftExtensions/CMakeLists.txt index 893f257f1..c0292f829 100644 --- a/Sources/SwiftExtensions/CMakeLists.txt +++ b/Sources/SwiftExtensions/CMakeLists.txt @@ -11,7 +11,6 @@ set(sources Duration+Seconds.swift FileManagerExtensions.swift LazyValue.swift - NSLock+WithLock.swift PipeAsStringHandler.swift Platform.swift Process+terminate.swift @@ -19,7 +18,6 @@ set(sources RunWithCleanup.swift Sequence+AsyncMap.swift Sequence+ContainsAnyIn.swift - ThreadSafeBox.swift TransitiveClosure.swift URLExtensions.swift ) diff --git a/Sources/SwiftExtensions/NSLock+WithLock.swift b/Sources/SwiftExtensions/NSLock+WithLock.swift deleted file mode 100644 index 9c0d9bd6e..000000000 --- a/Sources/SwiftExtensions/NSLock+WithLock.swift +++ /dev/null @@ -1,21 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Foundation - -extension NSLock { - func withLock(_ body: () throws -> T) rethrows -> T { - lock() - defer { unlock() } - return try body() - } -} diff --git a/Sources/SwiftExtensions/ThreadSafeBox.swift b/Sources/SwiftExtensions/ThreadSafeBox.swift deleted file mode 100644 index c5b29ca7b..000000000 --- a/Sources/SwiftExtensions/ThreadSafeBox.swift +++ /dev/null @@ -1,65 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Foundation - -/// A thread safe container that contains a value of type `T`. -/// -/// - Note: Unchecked sendable conformance because value is guarded by a lock. -package final class ThreadSafeBox: Sendable { - /// Lock guarding `_value`. - private let lock = NSLock() - - private nonisolated(unsafe) var _value: T - - package init(initialValue: T) { - _value = initialValue - } - - /// Restrict the result of the body to `Sendable` so callers can safely use - /// the returned value outside the lock without requiring `T: Sendable`. - package func withLock(_ body: (inout T) throws -> Result) rethrows -> Result { - return try lock.withLock { - return try body(&_value) - } - } -} - -extension ThreadSafeBox where T: Sendable { - package var value: T { - get { - return lock.withLock { - return _value - } - } - set { - lock.withLock { - _value = newValue - } - } - _modify { - lock.lock() - defer { lock.unlock() } - yield &_value - } - } - - /// If the value in the box is an optional, return it and reset it to `nil` - /// in an atomic operation. - package func takeValue() -> T where U? == T { - lock.withLock { - guard let value = self._value else { return nil } - self._value = nil - return value - } - } -} diff --git a/Sources/ToolchainRegistry/Toolchain.swift b/Sources/ToolchainRegistry/Toolchain.swift index 5ad40cdb8..ef6818cad 100644 --- a/Sources/ToolchainRegistry/Toolchain.swift +++ b/Sources/ToolchainRegistry/Toolchain.swift @@ -15,6 +15,7 @@ import RegexBuilder @_spi(SourceKitLSP) import SKLogging import SwiftExtensions import TSCExtensions +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import class TSCBasic.Process diff --git a/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift b/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift index b569f0817..63196fc34 100644 --- a/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift +++ b/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift @@ -21,6 +21,7 @@ import SKTestSupport import SwiftExtensions import TSCBasic import ToolchainRegistry +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import XCTest fileprivate actor TestBuildServer: CustomBuildServer { diff --git a/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift b/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift index 92747c75e..251de87b2 100644 --- a/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift +++ b/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift @@ -2627,8 +2627,11 @@ final class BackgroundIndexingTests: SourceKitLSPTestCase { let testHooks = Hooks( buildServerHooks: BuildServerHooks(preHandleRequest: { request in if let request = request as? BuildTargetPrepareRequest { - preparationRequests.value.append(request) - if preparationRequests.value.count >= 2 { + let count = preparationRequests.withLock { (value: inout [BuildTargetPrepareRequest]) in + value.append(request) + return value.count + } + if count >= 2 { twoPreparationRequestsReceived.fulfill() } } diff --git a/Tests/SourceKitLSPTests/CrashRecoveryTests.swift b/Tests/SourceKitLSPTests/CrashRecoveryTests.swift index 89ff46267..9df454ecf 100644 --- a/Tests/SourceKitLSPTests/CrashRecoveryTests.swift +++ b/Tests/SourceKitLSPTests/CrashRecoveryTests.swift @@ -19,6 +19,7 @@ import SourceKitD import SourceKitLSP import SwiftExtensions @_spi(Testing) import SwiftLanguageService +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import XCTest fileprivate extension HoverResponse { diff --git a/Tests/SourceKitLSPTests/ExecuteCommandTests.swift b/Tests/SourceKitLSPTests/ExecuteCommandTests.swift index b61144a1d..e2f35f2c9 100644 --- a/Tests/SourceKitLSPTests/ExecuteCommandTests.swift +++ b/Tests/SourceKitLSPTests/ExecuteCommandTests.swift @@ -17,6 +17,7 @@ import SKTestSupport @_spi(Testing) import SourceKitLSP import SwiftExtensions import SwiftLanguageService +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import XCTest final class ExecuteCommandTests: SourceKitLSPTestCase { diff --git a/Tests/SourceKitLSPTests/ExpandMacroTests.swift b/Tests/SourceKitLSPTests/ExpandMacroTests.swift index c42d3f072..c85751556 100644 --- a/Tests/SourceKitLSPTests/ExpandMacroTests.swift +++ b/Tests/SourceKitLSPTests/ExpandMacroTests.swift @@ -17,6 +17,7 @@ import SKTestSupport @_spi(Testing) import SourceKitLSP import SwiftExtensions import SwiftLanguageService +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import XCTest final class ExpandMacroTests: SourceKitLSPTestCase { diff --git a/Tests/SourceKitLSPTests/LocalSwiftTests.swift b/Tests/SourceKitLSPTests/LocalSwiftTests.swift index 3cb027f9a..6a8b7c907 100644 --- a/Tests/SourceKitLSPTests/LocalSwiftTests.swift +++ b/Tests/SourceKitLSPTests/LocalSwiftTests.swift @@ -20,6 +20,7 @@ import SwiftExtensions @_spi(Testing) import SwiftLanguageService import SwiftParser import SwiftSyntax +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import XCTest final class LocalSwiftTests: SourceKitLSPTestCase { @@ -1369,8 +1370,8 @@ final class LocalSwiftTests: SourceKitLSPTestCase { uri: uri ) let swiftLanguageService = try await unwrap(testClient.primaryLanguageService(for: uri) as? SwiftLanguageService) - await swiftLanguageService.setReusedNodeCallback { - reusedNodes.value.append($0) + await swiftLanguageService.setReusedNodeCallback { node in + reusedNodes.withLock { $0.append(node) } reusedNodeCallback.fulfill() } diff --git a/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift b/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift index 6dc11f7fe..33a7362b6 100644 --- a/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift +++ b/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift @@ -19,6 +19,7 @@ import SourceKitLSP import SwiftExtensions @_spi(Testing) import SwiftLanguageService import ToolchainRegistry +@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions import XCTest final class SourcekitdCoreInjectorTests: SourceKitLSPTestCase { @@ -46,7 +47,7 @@ final class SourcekitdCoreInjectorTests: SourceKitLSPTestCase { let testClient = try await TestSourceKitLSPClient( hooks: Hooks(sourcekitdCoreInjector: { toolchainURL in - injectorCallCount.value += 1 + injectorCallCount.withLock { $0 += 1 } capturedToolchainURL.value = toolchainURL return injectedCore }) @@ -113,7 +114,7 @@ private final class InjectedSourceKitDCore: SourceKitDCore, Sendable { api: sourcekitd_api_functions_t, notificationCallback: @escaping @Sendable (sourcekitd_api_response_t) -> Void ) { - _initializeServiceCallCount.value += 1 + _initializeServiceCallCount.withLock { $0 += 1 } // Deliberately a no-op: we don't call sourcekitd_set_notification_handler here because // that would override the handler registered by the shared SourceKitDCoreImpl. For this // test only hover (a direct request/response) is exercised, so notifications are not needed. From 99cac8f91de04535d120433b8e8478a1bf045c85 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 27 May 2026 14:36:56 -0700 Subject: [PATCH 2/2] Update for the ThreadSafeBox.value setter removal --- .../BuildServerManager.swift | 3 ++- Sources/Diagnose/DiagnoseCommand.swift | 2 +- .../DocCReferenceResolutionService.swift | 2 +- .../InProcessSourceKitLSPClient.swift | 3 ++- .../CustomBuildServerTestProject.swift | 2 +- Sources/SKTestSupport/TestSourceKitLSPClient.swift | 2 +- Sources/SemanticIndex/CheckedIndex.swift | 2 +- Sources/SourceKitD/dlopen.swift | 2 +- Sources/SwiftExtensions/AsyncUtils.swift | 2 +- Sources/TSCExtensions/Process+Run.swift | 2 +- .../BuildServerManagerTests.swift | 2 +- .../SwiftPMBuildServerTests.swift | 8 ++++---- Tests/SemanticIndexTests/TaskSchedulerTests.swift | 2 +- .../BackgroundIndexingTests.swift | 8 ++++---- Tests/SourceKitLSPTests/ClangdTests.swift | 2 +- Tests/SourceKitLSPTests/ExecuteCommandTests.swift | 8 ++++---- Tests/SourceKitLSPTests/ExpandMacroTests.swift | 14 +++++++------- .../SourcekitdCoreInjectorTests.swift | 2 +- .../WorkspaceTestDiscoveryTests.swift | 2 +- Tests/SourceKitLSPTests/WorkspaceTests.swift | 4 ++-- 20 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Sources/BuildServerIntegration/BuildServerManager.swift b/Sources/BuildServerIntegration/BuildServerManager.swift index 680e44a5e..bc3040afc 100644 --- a/Sources/BuildServerIntegration/BuildServerManager.swift +++ b/Sources/BuildServerIntegration/BuildServerManager.swift @@ -181,9 +181,10 @@ private enum BuildServerAdapter { if Task.isCancelled { return continuation.resume(throwing: CancellationError()) } - requestID.value = messageHandler.send(request) { response in + let id = messageHandler.send(request) { response in continuation.resume(with: response) } + requestID.withLock { $0 = id } if Task.isCancelled { // The task might have been cancelled after we checked `Task.isCancelled` above but before `requestID.value` // is set, we won't send a `CancelRequestNotification` from the `onCancel` handler. Send it from here. diff --git a/Sources/Diagnose/DiagnoseCommand.swift b/Sources/Diagnose/DiagnoseCommand.swift index 1d66f0693..b1510acd8 100644 --- a/Sources/Diagnose/DiagnoseCommand.swift +++ b/Sources/Diagnose/DiagnoseCommand.swift @@ -261,7 +261,7 @@ package struct DiagnoseCommand: AsyncParsableCommand { ) try process.launch() try await process.waitUntilExit() - processExited.value = true + processExited.withLock { $0 = true } #endif } diff --git a/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift b/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift index 35d2f07c4..441466a20 100644 --- a/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift +++ b/Sources/DocumentationLanguageService/DocCReferenceResolutionService.swift @@ -34,7 +34,7 @@ final class DocCReferenceResolutionService: DocumentationService, Sendable { init() {} func addContext(_ context: DocCReferenceResolutionContext, withKey key: String) { - contextMap.value[key] = context + contextMap.withLock { $0[key] = context } } @discardableResult func removeContext(forKey key: String) -> DocCReferenceResolutionContext? { diff --git a/Sources/InProcessClient/InProcessSourceKitLSPClient.swift b/Sources/InProcessClient/InProcessSourceKitLSPClient.swift index ca0febb9f..e4ab4b03d 100644 --- a/Sources/InProcessClient/InProcessSourceKitLSPClient.swift +++ b/Sources/InProcessClient/InProcessSourceKitLSPClient.swift @@ -100,9 +100,10 @@ public final class InProcessSourceKitLSPClient: Sendable { // possible. return continuation.resume(throwing: CancellationError()) } - requestId.value = self.send(request) { + let id = self.send(request) { continuation.resume(with: $0) } + requestId.withLock { $0 = id } if Task.isCancelled, let requestId = requestId.takeValue() { // The task might have been cancelled after the above cancellation check but before `requestId` was assigned // a value. To cover that case, check for cancellation here again. Note that we won't cancel twice from here diff --git a/Sources/SKTestSupport/CustomBuildServerTestProject.swift b/Sources/SKTestSupport/CustomBuildServerTestProject.swift index 41494fec2..4982cfd7d 100644 --- a/Sources/SKTestSupport/CustomBuildServerTestProject.swift +++ b/Sources/SKTestSupport/CustomBuildServerTestProject.swift @@ -290,7 +290,7 @@ package final class CustomBuildServerTestProject XCTAssertNil(hooks.buildServerHooks.injectBuildServer) hooks.buildServerHooks.injectBuildServer = { [buildServerBox] projectRoot, connectionToSourceKitLSP in let buildServer = BuildServer(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP) - buildServerBox.value = buildServer + buildServerBox.withLock { $0 = buildServer } return LocalConnection(receiverName: "TestBuildServer", handler: buildServer) } try await super.init( diff --git a/Sources/SKTestSupport/TestSourceKitLSPClient.swift b/Sources/SKTestSupport/TestSourceKitLSPClient.swift index cce45f5a0..82031283a 100644 --- a/Sources/SKTestSupport/TestSourceKitLSPClient.swift +++ b/Sources/SKTestSupport/TestSourceKitLSPClient.swift @@ -407,7 +407,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable { // Register a one-shot handler that records when the request arrives. let received = ThreadSafeBox(initialValue: false) self.handleSingleRequest { (_: R) in - received.value = true + received.withLock { $0 = true } return VoidResponse() } diff --git a/Sources/SemanticIndex/CheckedIndex.swift b/Sources/SemanticIndex/CheckedIndex.swift index 8c20d004f..d223d5158 100644 --- a/Sources/SemanticIndex/CheckedIndex.swift +++ b/Sources/SemanticIndex/CheckedIndex.swift @@ -356,7 +356,7 @@ package final actor UncheckedIndex: Sendable { package func close() { // IndexStoreDB writes the index to disk when the retain count of the `IndexStoreDB` object hits zero. We hope that // nobody else still has a reference to `IndexStoreDB` here. - _underlyingIndexStoreDB.value = nil + _underlyingIndexStoreDB.withLock { $0 = nil } } /// Update the set of output paths that should be considered visible in the project. For example, if a source file is diff --git a/Sources/SourceKitD/dlopen.swift b/Sources/SourceKitD/dlopen.swift index 17ace3422..b0bc7aa4b 100644 --- a/Sources/SourceKitD/dlopen.swift +++ b/Sources/SourceKitD/dlopen.swift @@ -80,7 +80,7 @@ package final class DLHandle: Sendable { /// The handle must not be used anymore after calling `leak`. package func leak() { - rawValue.value = nil + rawValue.withLock { $0 = nil } } } diff --git a/Sources/SwiftExtensions/AsyncUtils.swift b/Sources/SwiftExtensions/AsyncUtils.swift index e468a61b5..11c01708a 100644 --- a/Sources/SwiftExtensions/AsyncUtils.swift +++ b/Sources/SwiftExtensions/AsyncUtils.swift @@ -99,7 +99,7 @@ package func withTimeoutResult( let stream = AsyncThrowingStream, any Error> { continuation in Task { try await Task.sleep(for: timeout) - didHitTimeout.value = true + didHitTimeout.withLock { $0 = true } continuation.yield(.timedOut) } diff --git a/Sources/TSCExtensions/Process+Run.swift b/Sources/TSCExtensions/Process+Run.swift index bac00b951..31eb973f9 100644 --- a/Sources/TSCExtensions/Process+Run.swift +++ b/Sources/TSCExtensions/Process+Run.swift @@ -35,7 +35,7 @@ extension Process { let hasExited = ThreadSafeBox(initialValue: false) return try await withTaskCancellationHandler { defer { - hasExited.value = true + hasExited.withLock { $0 = true } } return try await waitUntilExit() } onCancel: { diff --git a/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift b/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift index 63196fc34..4fc0a871a 100644 --- a/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift +++ b/Tests/BuildServerIntegrationTests/BuildServerManagerTests.swift @@ -64,7 +64,7 @@ private func createBuildServerManager( kind: .injected({ projectRoot, connectionToSourceKitLSP in assert(testBuildServer.value == nil, "Build server injector hook can only create a single TestBuildServer") let buildServer = TestBuildServer(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP) - testBuildServer.value = buildServer + testBuildServer.withLock { $0 = buildServer } return LocalConnection(receiverName: "TestBuildServer", handler: buildServer) }), projectRoot: dummyPath, diff --git a/Tests/BuildServerIntegrationTests/SwiftPMBuildServerTests.swift b/Tests/BuildServerIntegrationTests/SwiftPMBuildServerTests.swift index 721d4c1a3..3a3fadd9d 100644 --- a/Tests/BuildServerIntegrationTests/SwiftPMBuildServerTests.swift +++ b/Tests/BuildServerIntegrationTests/SwiftPMBuildServerTests.swift @@ -1334,11 +1334,11 @@ struct SwiftPMBuildServerTests { in: tempDir, reloadPackageDidStart: { if packageInitialized.value { - unexpectedReloadStarted.value = true + unexpectedReloadStarted.withLock { $0 = true } } } ) - packageInitialized.value = true + packageInitialized.withLock { $0 = true } // SwiftPM extracts the artifact bundle to: // /artifacts//// @@ -1388,11 +1388,11 @@ struct SwiftPMBuildServerTests { options: SourceKitLSPOptions(swiftPM: .init(scratchPath: try customScratch.filePath)), reloadPackageDidStart: { if packageInitialized.value { - unexpectedReloadStarted.value = true + unexpectedReloadStarted.withLock { $0 = true } } } ) - packageInitialized.value = true + packageInitialized.withLock { $0 = true } // With a custom scratch path, SwiftPM extracts to /artifacts/. // Simulate the delete-and-re-expand cycle for those paths. diff --git a/Tests/SemanticIndexTests/TaskSchedulerTests.swift b/Tests/SemanticIndexTests/TaskSchedulerTests.swift index a95852d69..da8f8191c 100644 --- a/Tests/SemanticIndexTests/TaskSchedulerTests.swift +++ b/Tests/SemanticIndexTests/TaskSchedulerTests.swift @@ -261,7 +261,7 @@ final class TaskSchedulerTests: SourceKitLSPTestCase { return } - taskExecutedBefore.value = true + taskExecutedBefore.withLock { $0 = true } taskStartedExecuting.fulfill() diff --git a/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift b/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift index 251de87b2..a3d54e6ad 100644 --- a/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift +++ b/Tests/SourceKitLSPTests/BackgroundIndexingTests.swift @@ -1392,7 +1392,7 @@ final class BackgroundIndexingTests: SourceKitLSPTestCase { hooks: testHooks, enableBackgroundIndexing: true ) - packageInitialized.value = true + packageInitialized.withLock { $0 = true } project.testClient.send( DidChangeWatchedFilesNotification(changes: [ FileEvent(uri: DocumentURI(project.scratchDirectory.appending(component: "random.swift")), type: .created) @@ -2186,7 +2186,7 @@ final class BackgroundIndexingTests: SourceKitLSPTestCase { return data.title == "Indexing" } ) - receivedReportProgressNotification.value = true + receivedReportProgressNotification.withLock { $0 = true } // Check that we receive an `end` notification _ = try await project.testClient.nextNotification( @@ -2255,7 +2255,7 @@ final class BackgroundIndexingTests: SourceKitLSPTestCase { // Ensure that changing `/private/tmp/.../test.c` only causes `/tmp/.../test.c` to be indexed, not // `/private/tmp/.../test.c`. - indexedFiles.value = [] + indexedFiles.withLock { $0 = [] } let testFileURL = try XCTUnwrap(project.uri(for: "test.c").fileURL?.realpath) try await "void y() {}".writeWithRetry(to: testFileURL) project.testClient.send( @@ -2603,7 +2603,7 @@ final class BackgroundIndexingTests: SourceKitLSPTestCase { let symbolsBeforeUpdate = try await project.testClient.send(WorkspaceSymbolsRequest(query: "myTestFu")) XCTAssertEqual(symbolsBeforeUpdate, []) - testSetupComplete.value = true + testSetupComplete.withLock { $0 = true } try await project.changeFileOnDisk( "Test.swift", newMarkedContents: """ diff --git a/Tests/SourceKitLSPTests/ClangdTests.swift b/Tests/SourceKitLSPTests/ClangdTests.swift index 239167c99..16e12d824 100644 --- a/Tests/SourceKitLSPTests/ClangdTests.swift +++ b/Tests/SourceKitLSPTests/ClangdTests.swift @@ -162,7 +162,7 @@ final class ClangdTests: SourceKitLSPTestCase { let clangdServer = try await unwrap(testClient.primaryLanguageService(for: uri)) await clangdServer.addStateChangeHandler { oldState, newState in if oldState == .connectionInterrupted, newState == .connected { - clangdRestarted.value = true + clangdRestarted.withLock { $0 = true } clangdRestartedExpectation.fulfill() } } diff --git a/Tests/SourceKitLSPTests/ExecuteCommandTests.swift b/Tests/SourceKitLSPTests/ExecuteCommandTests.swift index e2f35f2c9..d36b0a9cb 100644 --- a/Tests/SourceKitLSPTests/ExecuteCommandTests.swift +++ b/Tests/SourceKitLSPTests/ExecuteCommandTests.swift @@ -53,8 +53,8 @@ final class ExecuteCommandTests: SourceKitLSPTestCase { let applyEditWorkspaceEdit = ThreadSafeBox(initialValue: nil) testClient.handleSingleRequest { (req: ApplyEditRequest) -> ApplyEditResponse in - applyEditTitle.value = req.label - applyEditWorkspaceEdit.value = req.edit + applyEditTitle.withLock { $0 = req.label } + applyEditWorkspaceEdit.withLock { $0 = req.edit } expectation.fulfill() return ApplyEditResponse(applied: true, failureReason: nil) @@ -118,8 +118,8 @@ final class ExecuteCommandTests: SourceKitLSPTestCase { let applyEditWorkspaceEdit = ThreadSafeBox(initialValue: nil) testClient.handleSingleRequest { (req: ApplyEditRequest) -> ApplyEditResponse in - applyEditTitle.value = req.label - applyEditWorkspaceEdit.value = req.edit + applyEditTitle.withLock { $0 = req.label } + applyEditWorkspaceEdit.withLock { $0 = req.edit } expectation.fulfill() return ApplyEditResponse(applied: true, failureReason: nil) diff --git a/Tests/SourceKitLSPTests/ExpandMacroTests.swift b/Tests/SourceKitLSPTests/ExpandMacroTests.swift index c85751556..5b03814cf 100644 --- a/Tests/SourceKitLSPTests/ExpandMacroTests.swift +++ b/Tests/SourceKitLSPTests/ExpandMacroTests.swift @@ -91,7 +91,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let peekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) project.testClient.handleSingleRequest { (req: PeekDocumentsRequest) in - peekDocumentsRequestURIs.value = req.locations + peekDocumentsRequestURIs.withLock { $0 = req.locations } expectation.fulfill() return PeekDocumentsResponse(success: true) } @@ -130,7 +130,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let showDocumentRequestURI = ThreadSafeBox(initialValue: nil) project.testClient.handleSingleRequest { (req: ShowDocumentRequest) in - showDocumentRequestURI.value = req.uri + showDocumentRequestURI.withLock { $0 = req.uri } expectation.fulfill() return ShowDocumentResponse(success: true) } @@ -244,7 +244,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let peekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) project.testClient.handleSingleRequest { (req: PeekDocumentsRequest) in - peekDocumentsRequestURIs.value = req.locations + peekDocumentsRequestURIs.withLock { $0 = req.locations } expectation.fulfill() return PeekDocumentsResponse(success: true) } @@ -291,7 +291,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let showDocumentRequestURI = ThreadSafeBox(initialValue: nil) project.testClient.handleSingleRequest { (req: ShowDocumentRequest) in - showDocumentRequestURI.value = req.uri + showDocumentRequestURI.withLock { $0 = req.uri } expectation.fulfill() return ShowDocumentResponse(success: true) } @@ -388,7 +388,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let outerPeekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) project.testClient.handleSingleRequest { (req: PeekDocumentsRequest) in - outerPeekDocumentsRequestURIs.value = req.locations + outerPeekDocumentsRequestURIs.withLock { $0 = req.locations } outerPeekDocumentRequestReceived.fulfill() return PeekDocumentsResponse(success: true) } @@ -423,7 +423,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let intermediatePeekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) project.testClient.handleSingleRequest { (req: PeekDocumentsRequest) in - intermediatePeekDocumentsRequestURIs.value = req.locations + intermediatePeekDocumentsRequestURIs.withLock { $0 = req.locations } intermediatePeekDocumentRequestReceived.fulfill() return PeekDocumentsResponse(success: true) } @@ -460,7 +460,7 @@ final class ExpandMacroTests: SourceKitLSPTestCase { let innerPeekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) project.testClient.handleSingleRequest { (req: PeekDocumentsRequest) in - innerPeekDocumentsRequestURIs.value = req.locations + innerPeekDocumentsRequestURIs.withLock { $0 = req.locations } innerPeekDocumentRequestReceived.fulfill() return PeekDocumentsResponse(success: true) } diff --git a/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift b/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift index 33a7362b6..83a6b3086 100644 --- a/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift +++ b/Tests/SourceKitLSPTests/SourcekitdCoreInjectorTests.swift @@ -48,7 +48,7 @@ final class SourcekitdCoreInjectorTests: SourceKitLSPTestCase { let testClient = try await TestSourceKitLSPClient( hooks: Hooks(sourcekitdCoreInjector: { toolchainURL in injectorCallCount.withLock { $0 += 1 } - capturedToolchainURL.value = toolchainURL + capturedToolchainURL.withLock { $0 = toolchainURL } return injectedCore }) ) diff --git a/Tests/SourceKitLSPTests/WorkspaceTestDiscoveryTests.swift b/Tests/SourceKitLSPTests/WorkspaceTestDiscoveryTests.swift index 778af4b05..e83b9494e 100644 --- a/Tests/SourceKitLSPTests/WorkspaceTestDiscoveryTests.swift +++ b/Tests/SourceKitLSPTests/WorkspaceTestDiscoveryTests.swift @@ -136,7 +136,7 @@ final class WorkspaceTestDiscoveryTests: SourceKitLSPTestCase { enableBackgroundIndexing: true ) - initialIndexingFinished.value = true + initialIndexingFinished.withLock { $0 = true } let myTestsUri = try project.uri(for: "MyTests.swift") diff --git a/Tests/SourceKitLSPTests/WorkspaceTests.swift b/Tests/SourceKitLSPTests/WorkspaceTests.swift index 3ba036789..4f4115836 100644 --- a/Tests/SourceKitLSPTests/WorkspaceTests.swift +++ b/Tests/SourceKitLSPTests/WorkspaceTests.swift @@ -1161,7 +1161,7 @@ final class WorkspaceTests: SourceKitLSPTestCase { project.testClient.send(DidChangeWatchedFilesNotification(changes: [FileEvent(uri: baseLibUri, type: .changed)])) // Ensure that we handle the `DidChangeWatchedFilesNotification`. try await project.testClient.send(SynchronizeRequest()) - didChangeBaseLib.value = true + didChangeBaseLib.withLock { $0 = true } project.testClient.send( DidChangeActiveDocumentNotification(textDocument: TextDocumentIdentifier(libBUri)) @@ -1280,7 +1280,7 @@ final class WorkspaceTests: SourceKitLSPTestCase { project.testClient.send(DidChangeWatchedFilesNotification(changes: [FileEvent(uri: baseLibUri, type: .changed)])) // Ensure that we handle the `DidChangeWatchedFilesNotification`. try await project.testClient.send(SynchronizeRequest()) - didChangeBaseLib.value = true + didChangeBaseLib.withLock { $0 = true } let triggerPrepare = try await project.testClient.send( SourceKitOptionsRequest(