diff --git a/Package.swift b/Package.swift index 9aa83bf5..1e9bb2f0 100644 --- a/Package.swift +++ b/Package.swift @@ -52,13 +52,6 @@ var targets: [Target] = [ swiftSettings: globalSwiftSettings ), - // MARK: ToolsProtocolsCAtomics - - .target( - name: "ToolsProtocolsCAtomics", - dependencies: [] - ), - // MARK: LanguageServerProtocol .target( @@ -153,7 +146,7 @@ var targets: [Target] = [ .target( name: "ToolsProtocolsSwiftExtensions", - dependencies: ["ToolsProtocolsCAtomics"], + dependencies: [], exclude: ["CMakeLists.txt"], swiftSettings: globalSwiftSettings ), @@ -161,7 +154,7 @@ var targets: [Target] = [ // SourceKit-LSP SPI target. Builds ToolsProtocolsSwiftExtensions with an alternate module name to avoid runtime type collisions. .target( name: "_ToolsProtocolsSwiftExtensionsForPlugin", - dependencies: ["ToolsProtocolsCAtomics"], + dependencies: [], exclude: ["CMakeLists.txt"], swiftSettings: globalSwiftSettings ), diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index f1dab285..066d83a9 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -2,7 +2,6 @@ add_compile_options("$<$:SHELL:-package-name swift_langu add_compile_options("$<$:SHELL:-DRESILIENT_LIBRARIES>") add_compile_options("$<$:SHELL:-swift-version 6>") add_subdirectory(BuildServerProtocol) -add_subdirectory(ToolsProtocolsCAtomics) add_subdirectory(LanguageServerProtocol) add_subdirectory(LanguageServerProtocolTransport) add_subdirectory(SKLogging) diff --git a/Sources/LanguageServerProtocolTransport/JSONRPCConnection.swift b/Sources/LanguageServerProtocolTransport/JSONRPCConnection.swift index 16ced07e..fd4d3863 100644 --- a/Sources/LanguageServerProtocolTransport/JSONRPCConnection.swift +++ b/Sources/LanguageServerProtocolTransport/JSONRPCConnection.swift @@ -13,6 +13,7 @@ public import Foundation public import LanguageServerProtocol @_spi(SourceKitLSP) import SKLogging +import Synchronization @_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions #if canImport(Android) @@ -85,7 +86,7 @@ public final class JSONRPCConnection: Connection { private nonisolated(unsafe) var state: State = .created /// An integer that hasn't been used for a request ID yet. - let nextRequestIDStorage = AtomicUInt32(initialValue: 0) + let nextRequestIDStorage = Atomic(0) struct OutstandingRequest: Sendable { var responseType: ResponseType.Type @@ -597,7 +598,7 @@ public final class JSONRPCConnection: Connection { /// Request id for the next outgoing request. public func nextRequestID() -> RequestID { - return .string("sk-\(nextRequestIDStorage.fetchAndIncrement())") + return .string("sk-\(nextRequestIDStorage.wrappingAdd(1, ordering: .relaxed).oldValue)") } // MARK: Connection interface diff --git a/Sources/LanguageServerProtocolTransport/LocalConnection.swift b/Sources/LanguageServerProtocolTransport/LocalConnection.swift index f910ceb1..20ea28af 100644 --- a/Sources/LanguageServerProtocolTransport/LocalConnection.swift +++ b/Sources/LanguageServerProtocolTransport/LocalConnection.swift @@ -14,6 +14,7 @@ import Dispatch import Foundation public import LanguageServerProtocol @_spi(SourceKitLSP) import SKLogging +import Synchronization @_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions /// A connection between two message handlers in the same process. @@ -39,7 +40,7 @@ public final class LocalConnection: Connection, Sendable { /// The queue guarding `_nextRequestID`. private let queue: DispatchQueue = DispatchQueue(label: "local-connection-queue") - private let _nextRequestID = AtomicUInt32(initialValue: 0) + private let _nextRequestID = Atomic(0) /// - Important: Must only be accessed from `queue` nonisolated(unsafe) private var state: State = .ready @@ -87,7 +88,7 @@ public final class LocalConnection: Connection, Sendable { } public func nextRequestID() -> RequestID { - return .string("sk-\(_nextRequestID.fetchAndIncrement())") + return .string("sk-\(_nextRequestID.wrappingAdd(1, ordering: .relaxed).oldValue)") } public func send(_ notification: Notification) { diff --git a/Sources/LanguageServerProtocolTransport/QueueBasedMessageHandler.swift b/Sources/LanguageServerProtocolTransport/QueueBasedMessageHandler.swift index a2af2d62..42f19f76 100644 --- a/Sources/LanguageServerProtocolTransport/QueueBasedMessageHandler.swift +++ b/Sources/LanguageServerProtocolTransport/QueueBasedMessageHandler.swift @@ -13,6 +13,7 @@ import Foundation public import LanguageServerProtocol @_spi(SourceKitLSP) import SKLogging +import Synchronization @_spi(SourceKitLSP) public import ToolsProtocolsSwiftExtensions /// Side structure in which `QueueBasedMessageHandler` can keep track of active requests etc. @@ -42,7 +43,7 @@ public final class QueueBasedMessageHandlerHelper: Sendable { private let createLoggingScope: Bool /// Notifications don't have an ID. This represents the next ID we can use to identify a notification. - private let notificationIDForLogging = AtomicUInt32(initialValue: 1) + private let notificationIDForLogging = Atomic(1) private let state = ThreadSafeBox(initialValue: State()) @@ -107,7 +108,7 @@ public final class QueueBasedMessageHandlerHelper: Sendable { // Only use the last two digits of the notification ID for the logging scope to avoid creating too many scopes. // See comment in `withLoggingScope`. // The last 2 digits should be sufficient to differentiate between multiple concurrently running notifications. - let notificationID = notificationIDForLogging.fetchAndIncrement() + let notificationID = notificationIDForLogging.wrappingAdd(1, ordering: .relaxed).oldValue withLoggingScope("notification-\(notificationID % 100)") { body() } diff --git a/Sources/LanguageServerProtocolTransport/RequestAndReply.swift b/Sources/LanguageServerProtocolTransport/RequestAndReply.swift index bfdf1062..ab224da4 100644 --- a/Sources/LanguageServerProtocolTransport/RequestAndReply.swift +++ b/Sources/LanguageServerProtocolTransport/RequestAndReply.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// public import LanguageServerProtocol +import Synchronization @_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions /// A request and a callback that returns the request's reply @@ -22,7 +23,7 @@ public final class RequestAndReply: Sendable { private let reply: @Sendable (Result) -> Void /// Whether a reply has been made. Every request must reply exactly once. - private let replied: AtomicBool = AtomicBool(initialValue: false) + private let replied = Atomic(false) public init(_ request: Params, reply: @escaping @Sendable (Result) -> Void) { self.params = request @@ -30,12 +31,12 @@ public final class RequestAndReply: Sendable { } deinit { - precondition(replied.value, "request never received a reply") + precondition(replied.load(ordering: .acquiring), "request never received a reply") } /// Call the `replyBlock` with the result produced by the given closure. public func reply(_ body: () async throws -> Params.Response) async { - let didReply = replied.setAndGet(newValue: true) + let didReply = replied.exchange(true, ordering: .acquiringAndReleasing) precondition(!didReply, "replied to request more than once") do { reply(.success(try await body())) diff --git a/Sources/SKLogging/LoggingScope.swift b/Sources/SKLogging/LoggingScope.swift index 04df859a..923e19e9 100644 --- a/Sources/SKLogging/LoggingScope.swift +++ b/Sources/SKLogging/LoggingScope.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Foundation +import Synchronization @_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions public final class LoggingScope { @@ -20,7 +21,7 @@ public final class LoggingScope { /// Whether we have logged a fault that `subsystem` has been accessed without calling /// `configureDefaultLoggingSubsystem` first. - private static let hasLoggedNoSubsystemConfiguredFault = AtomicBool(initialValue: false) + private static let hasLoggedNoSubsystemConfiguredFault = Atomic(false) /// The name of the current logging subsystem or `nil` if no logging scope is set. @TaskLocal fileprivate static var _subsystem: String? @@ -35,7 +36,7 @@ public final class LoggingScope { } else if let defaultSubsystem = defaultSubsystem.value { return defaultSubsystem } else { - if !hasLoggedNoSubsystemConfiguredFault.setAndGet(newValue: true) { + if !hasLoggedNoSubsystemConfiguredFault.exchange(true, ordering: .relaxed) { Logger(subsystem: "default", category: "sklogging") .fault( """ diff --git a/Sources/SKLogging/NonDarwinLogging.swift b/Sources/SKLogging/NonDarwinLogging.swift index f56084ac..764bb8db 100644 --- a/Sources/SKLogging/NonDarwinLogging.swift +++ b/Sources/SKLogging/NonDarwinLogging.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +import Synchronization @_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions #if canImport(Darwin) @@ -434,7 +435,7 @@ public struct NonDarwinLogger: Sendable { fileprivate let id: NonDarwinSignpostID } -private let nextSignpostID = AtomicUInt32(initialValue: 0) +private let nextSignpostID = Atomic(0) /// A type that is API-compatible to `OSLogMessage` for all uses within sourcekit-lsp. /// @@ -447,7 +448,7 @@ private let nextSignpostID = AtomicUInt32(initialValue: 0) } @_spi(SourceKitLSP) public func makeSignpostID() -> NonDarwinSignpostID { - return NonDarwinSignpostID(id: nextSignpostID.fetchAndIncrement()) + return NonDarwinSignpostID(id: nextSignpostID.wrappingAdd(1, ordering: .relaxed).oldValue) } @_spi(SourceKitLSP) public func beginInterval( diff --git a/Sources/ToolsProtocolsCAtomics/CAtomics.c b/Sources/ToolsProtocolsCAtomics/CAtomics.c deleted file mode 100644 index f6d1c10c..00000000 --- a/Sources/ToolsProtocolsCAtomics/CAtomics.c +++ /dev/null @@ -1,11 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2025 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 -// -//===----------------------------------------------------------------------===// diff --git a/Sources/ToolsProtocolsCAtomics/CMakeLists.txt b/Sources/ToolsProtocolsCAtomics/CMakeLists.txt deleted file mode 100644 index 43ac4e4a..00000000 --- a/Sources/ToolsProtocolsCAtomics/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(ToolsProtocolsCAtomics INTERFACE) -target_include_directories(ToolsProtocolsCAtomics INTERFACE "include") - -set_property(GLOBAL APPEND PROPERTY SWIFTTOOLSPROTOCOLS_EXPORTS ToolsProtocolsCAtomics) diff --git a/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h b/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h deleted file mode 100644 index 2629f037..00000000 --- a/Sources/ToolsProtocolsCAtomics/include/ToolsProtocolsCAtomics.h +++ /dev/null @@ -1,78 +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 -// -//===----------------------------------------------------------------------===// - -#ifndef ToolsProtocolsCAtomics_H -#define ToolsProtocolsCAtomics_H - -#include -#include -#include -#include -#include - -typedef struct { - _Atomic(uint32_t) value; -} CAtomicUInt32; - -static inline CAtomicUInt32 *_Nonnull atomic_uint32_create(uint32_t initialValue) { - CAtomicUInt32 *atomic = malloc(sizeof(CAtomicUInt32)); - atomic->value = initialValue; - return atomic; -} - -static inline uint32_t atomic_uint32_get(CAtomicUInt32 *_Nonnull atomic) { - return atomic->value; -} - -static inline uint32_t atomic_uint32_get_and_set(CAtomicUInt32 *_Nonnull atomic, uint32_t newValue) { - return __c11_atomic_exchange(&atomic->value, newValue, __ATOMIC_SEQ_CST); -} - -static inline void atomic_uint32_set(CAtomicUInt32 *_Nonnull atomic, uint32_t newValue) { - atomic->value = newValue; -} - -static inline uint32_t atomic_uint32_fetch_and_increment(CAtomicUInt32 *_Nonnull atomic) { - return atomic->value++; -} - -static inline void atomic_uint32_destroy(CAtomicUInt32 *_Nonnull atomic) { - free(atomic); -} - -typedef struct { - _Atomic(int32_t) value; -} CAtomicInt32; - -static inline CAtomicInt32 *_Nonnull atomic_int32_create(int32_t initialValue) { - CAtomicInt32 *atomic = malloc(sizeof(CAtomicInt32)); - atomic->value = initialValue; - return atomic; -} - -static inline int32_t atomic_int32_get(CAtomicInt32 *_Nonnull atomic) { - return atomic->value; -} - -static inline void atomic_int32_set(CAtomicInt32 *_Nonnull atomic, int32_t newValue) { - atomic->value = newValue; -} - -static inline int32_t atomic_int32_fetch_and_increment(CAtomicInt32 *_Nonnull atomic) { - return atomic->value++; -} - -static inline void atomic_int32_destroy(CAtomicInt32 *_Nonnull atomic) { - free(atomic); -} - -#endif // ToolsProtocolsCAtomics_H diff --git a/Sources/ToolsProtocolsCAtomics/include/module.modulemap b/Sources/ToolsProtocolsCAtomics/include/module.modulemap deleted file mode 100644 index a64c49e7..00000000 --- a/Sources/ToolsProtocolsCAtomics/include/module.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -module ToolsProtocolsCAtomics { - header "ToolsProtocolsCAtomics.h" - export * -} diff --git a/Sources/ToolsProtocolsSwiftExtensions/AsyncUtils.swift b/Sources/ToolsProtocolsSwiftExtensions/AsyncUtils.swift index aecfbafe..b7f30e07 100644 --- a/Sources/ToolsProtocolsSwiftExtensions/AsyncUtils.swift +++ b/Sources/ToolsProtocolsSwiftExtensions/AsyncUtils.swift @@ -280,7 +280,7 @@ package func withTimeout( body: @escaping @Sendable () async throws -> T, resultReceivedAfterTimeout: @escaping @Sendable (_ result: T) async -> Void ) async throws -> T? { - let didHitTimeout = AtomicBool(initialValue: false) + let didHitTimeout = ThreadSafeBox(initialValue: false) let stream = AsyncThrowingStream { continuation in Task { diff --git a/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift b/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift deleted file mode 100644 index 302e6e7a..00000000 --- a/Sources/ToolsProtocolsSwiftExtensions/Atomics.swift +++ /dev/null @@ -1,111 +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 ToolsProtocolsCAtomics - -// TODO: Use atomic types from the standard library (https://github.com/swiftlang/sourcekit-lsp/issues/1949) -@_spi(SourceKitLSP) public final class AtomicBool: Sendable { - private nonisolated(unsafe) let atomic: UnsafeMutablePointer - - @_spi(SourceKitLSP) public init(initialValue: Bool) { - self.atomic = atomic_uint32_create(initialValue ? 1 : 0) - } - - deinit { - atomic_uint32_destroy(atomic) - } - - @_spi(SourceKitLSP) public var value: Bool { - get { - atomic_uint32_get(atomic) != 0 - } - set { - atomic_uint32_set(atomic, newValue ? 1 : 0) - } - } - - /// Sets the boolean to the new value and returns the previous value. - @_spi(SourceKitLSP) public func setAndGet(newValue: Bool) -> Bool { - return atomic_uint32_get_and_set(atomic, newValue ? 1 : 0) != 0 - } -} - -@_spi(SourceKitLSP) public final class AtomicUInt8: Sendable { - private nonisolated(unsafe) let atomic: UnsafeMutablePointer - - @_spi(SourceKitLSP) public init(initialValue: UInt8) { - self.atomic = atomic_uint32_create(UInt32(initialValue)) - } - - deinit { - atomic_uint32_destroy(atomic) - } - - @_spi(SourceKitLSP) public var value: UInt8 { - get { - UInt8(atomic_uint32_get(atomic)) - } - set { - atomic_uint32_set(atomic, UInt32(newValue)) - } - } -} - -@_spi(SourceKitLSP) public final class AtomicUInt32: Sendable { - private nonisolated(unsafe) let atomic: UnsafeMutablePointer - - @_spi(SourceKitLSP) public init(initialValue: UInt32) { - self.atomic = atomic_uint32_create(initialValue) - } - - @_spi(SourceKitLSP) public var value: UInt32 { - get { - atomic_uint32_get(atomic) - } - set { - atomic_uint32_set(atomic, newValue) - } - } - - deinit { - atomic_uint32_destroy(atomic) - } - - @_spi(SourceKitLSP) public func fetchAndIncrement() -> UInt32 { - return atomic_uint32_fetch_and_increment(atomic) - } -} - -@_spi(SourceKitLSP) public final class AtomicInt32: Sendable { - private nonisolated(unsafe) let atomic: UnsafeMutablePointer - - @_spi(SourceKitLSP) public init(initialValue: Int32) { - self.atomic = atomic_int32_create(initialValue) - } - - @_spi(SourceKitLSP) public var value: Int32 { - get { - atomic_int32_get(atomic) - } - set { - atomic_int32_set(atomic, newValue) - } - } - - deinit { - atomic_int32_destroy(atomic) - } - - @_spi(SourceKitLSP) public func fetchAndIncrement() -> Int32 { - return atomic_int32_fetch_and_increment(atomic) - } -} diff --git a/Sources/ToolsProtocolsSwiftExtensions/CMakeLists.txt b/Sources/ToolsProtocolsSwiftExtensions/CMakeLists.txt index e3963999..a89ce654 100644 --- a/Sources/ToolsProtocolsSwiftExtensions/CMakeLists.txt +++ b/Sources/ToolsProtocolsSwiftExtensions/CMakeLists.txt @@ -1,7 +1,6 @@ set(sources AsyncQueue.swift AsyncUtils.swift - Atomics.swift Collection+Only.swift Duration+Seconds.swift FileManagerExtensions.swift @@ -15,16 +14,12 @@ set(sources add_library(ToolsProtocolsSwiftExtensions ${sources}) set_target_properties(ToolsProtocolsSwiftExtensions PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) -target_link_libraries(ToolsProtocolsSwiftExtensions PUBLIC - ToolsProtocolsCAtomics) target_link_libraries(ToolsProtocolsSwiftExtensions PRIVATE $<$>:Foundation>) add_library(_ToolsProtocolsSwiftExtensionsForPlugin ${sources}) set_target_properties(_ToolsProtocolsSwiftExtensionsForPlugin PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) -target_link_libraries(_ToolsProtocolsSwiftExtensionsForPlugin PUBLIC - ToolsProtocolsCAtomics) target_link_libraries(_ToolsProtocolsSwiftExtensionsForPlugin PRIVATE $<$>:Foundation>)