Skip to content

Basics/Concurrency: make thread-safe containers Sendable #6105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Sources/Basics/Concurrency/ThreadSafeArrayStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ public final class ThreadSafeArrayStore<Value> {
}
}
}

#if swift(<5.7)
extension ThreadSafeArrayStore: UnsafeSendable where Value: Sendable {}
#else
extension ThreadSafeArrayStore: @unchecked Sendable where Value: Sendable {}
#endif
6 changes: 6 additions & 0 deletions Sources/Basics/Concurrency/ThreadSafeBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,9 @@ extension ThreadSafeBox where Value == Int {
}
}
}

#if swift(<5.7)
extension ThreadSafeBox: UnsafeSendable where Value: Sendable {}
#else
extension ThreadSafeBox: @unchecked Sendable where Value: Sendable {}
#endif
34 changes: 18 additions & 16 deletions Sources/Basics/Observability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@ public class ObservabilitySystem {
)
}

public typealias HandlerClosure = @Sendable (ObservabilityScope, Diagnostic) -> Void

/// Create an ObservabilitySystem with a single diagnostics handler.
public convenience init(_ handler: @escaping (ObservabilityScope, Diagnostic) -> Void) {
public convenience init(_ handler: @escaping HandlerClosure) {
self.init(SingleDiagnosticsHandler(handler))
}

private struct SingleDiagnosticsHandler: ObservabilityHandlerProvider, DiagnosticsHandler {
var diagnosticsHandler: DiagnosticsHandler { self }

let underlying: (ObservabilityScope, Diagnostic) -> Void
let underlying: HandlerClosure

init(_ underlying: @escaping (ObservabilityScope, Diagnostic) -> Void) {
init(_ underlying: @escaping HandlerClosure) {
self.underlying = underlying
}

Expand All @@ -61,12 +63,12 @@ public protocol ObservabilityHandlerProvider {

// MARK: - ObservabilityScope

public final class ObservabilityScope: DiagnosticsEmitterProtocol, CustomStringConvertible {
public final class ObservabilityScope: DiagnosticsEmitterProtocol, CustomStringConvertible, Sendable {
public let description: String
private let parent: ObservabilityScope?
private let metadata: ObservabilityMetadata?

private var diagnosticsHandler: DiagnosticsHandlerWrapper
private let diagnosticsHandler: DiagnosticsHandlerWrapper

fileprivate init(
description: String,
Expand Down Expand Up @@ -150,7 +152,7 @@ public final class ObservabilityScope: DiagnosticsEmitterProtocol, CustomStringC

// MARK: - Diagnostics

public protocol DiagnosticsHandler {
public protocol DiagnosticsHandler: Sendable {
func handleDiagnostic(scope: ObservabilityScope, diagnostic: Diagnostic)
}

Expand Down Expand Up @@ -252,7 +254,7 @@ public struct DiagnosticsEmitter: DiagnosticsEmitterProtocol {
}
}

public struct Diagnostic: CustomStringConvertible {
public struct Diagnostic: CustomStringConvertible, Sendable {
public let severity: Severity
public let message: String
public internal (set) var metadata: ObservabilityMetadata?
Expand Down Expand Up @@ -322,7 +324,7 @@ public struct Diagnostic: CustomStringConvertible {
Self(severity: .debug, message: message.description, metadata: metadata)
}

public enum Severity: Comparable {
public enum Severity: Comparable, Sendable {
case error
case warning
case info
Expand Down Expand Up @@ -378,10 +380,10 @@ public struct Diagnostic: CustomStringConvertible {
// FIXME: we currently require that Value conforms to CustomStringConvertible which sucks
// ideally Value would conform to Equatable but that has generic requirement
// luckily, this is about to change so we can clean this up soon
public struct ObservabilityMetadata: CustomDebugStringConvertible {
public struct ObservabilityMetadata: CustomDebugStringConvertible, Sendable {
public typealias Key = ObservabilityMetadataKey

private var _storage = [AnyKey: Any]()
private var _storage = [AnyKey: Sendable]()

public init() {}

Expand Down Expand Up @@ -412,7 +414,7 @@ public struct ObservabilityMetadata: CustomDebugStringConvertible {
///
/// - Parameter body: The closure to be invoked for each item stored in this `ObservabilityMetadata`,
/// passing the type-erased key and the associated value.
public func forEach(_ body: (AnyKey, Any) throws -> Void) rethrows {
public func forEach(_ body: (AnyKey, Sendable) throws -> Void) rethrows {
try self._storage.forEach { key, value in
try body(key, value)
}
Expand Down Expand Up @@ -471,7 +473,7 @@ public struct ObservabilityMetadata: CustomDebugStringConvertible {
}

/// A type-erased `ObservabilityMetadataKey` used when iterating through the `ObservabilityMetadata` using its `forEach` method.
public struct AnyKey {
public struct AnyKey: Sendable {
/// The key's type represented erased to an `Any.Type`.
public let keyType: Any.Type

Expand All @@ -483,7 +485,7 @@ public struct ObservabilityMetadata: CustomDebugStringConvertible {

public protocol ObservabilityMetadataKey {
/// The type of value uniquely identified by this key.
associatedtype Value
associatedtype Value: Sendable
}

extension ObservabilityMetadata.AnyKey: Hashable {
Expand All @@ -510,7 +512,7 @@ extension ObservabilityMetadata {
typealias Value = Error
}

public struct UnderlyingError: CustomStringConvertible {
public struct UnderlyingError: CustomStringConvertible, Sendable {
let underlying: Error

public init (_ underlying: Error) {
Expand Down Expand Up @@ -571,7 +573,7 @@ extension ObservabilityMetadata {
typealias Value = DiagnosticLocationWrapper
}

public struct DiagnosticLocationWrapper: CustomStringConvertible {
public struct DiagnosticLocationWrapper: CustomStringConvertible, Sendable {
let underlying: DiagnosticLocation

public init (_ underlying: DiagnosticLocation) {
Expand Down Expand Up @@ -599,7 +601,7 @@ extension ObservabilityMetadata {
typealias Value = DiagnosticDataWrapper
}

struct DiagnosticDataWrapper: CustomStringConvertible {
struct DiagnosticDataWrapper: CustomStringConvertible, Sendable {
let underlying: DiagnosticData

public init (_ underlying: DiagnosticData) {
Expand Down
10 changes: 9 additions & 1 deletion Sources/CoreCommands/SwiftToolObservabilityHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public struct SwiftToolObservabilityHandler: ObservabilityHandlerProvider {
self.outputHandler.wait(timeout: timeout)
}

struct OutputHandler: DiagnosticsHandler {
struct OutputHandler {
private let logLevel: Diagnostic.Severity
internal let outputStream: ThreadSafeOutputByteStream
private let writer: InteractiveWriter
Expand Down Expand Up @@ -140,6 +140,14 @@ public struct SwiftToolObservabilityHandler: ObservabilityHandlerProvider {
}
}

#if swift(<5.7)
extension SwiftToolObservabilityHandler.OutputHandler: UnsafeSendable {}
#else
extension SwiftToolObservabilityHandler.OutputHandler: @unchecked Sendable {}
#endif

extension SwiftToolObservabilityHandler.OutputHandler: DiagnosticsHandler {}

/// This type is used to write on the underlying stream.
///
/// If underlying stream is a not tty, the string will be written in without any
Expand Down
1 change: 1 addition & 0 deletions Sources/PackageCollections/Model/PackageTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import struct Foundation.URL

import PackageModel
import SourceControl
import TSCUtility

extension PackageCollectionsModel {
/// Package metadata
Expand Down
1 change: 1 addition & 0 deletions Sources/PackageCollections/Model/TargetListResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Foundation

import PackageModel
import SourceControl
import TSCUtility

extension PackageCollectionsModel {
public typealias TargetListResult = [TargetListItem]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import TSCBasic
import Basics
import PackageCollectionsModel
import PackageModel
import TSCUtility

// MARK: - Model validations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import class Foundation.NSRegularExpression
import struct Foundation.URL
import PackageModel
import TSCBasic
import TSCUtility

struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable {
private static let apiHostPrefix = "api."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import PackageCollectionsSigning
import PackageModel
import SourceControl
import TSCBasic
import TSCUtility

private typealias JSONModel = PackageCollectionModel.V1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import struct Foundation.URL

import PackageModel
import TSCBasic
import TSCUtility

/// `PackageBasicMetadata` provider
protocol PackageMetadataProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Dispatch
import Foundation
import PackageModel
import TSCBasic
import struct TSCUtility.Version

public struct FilePackageFingerprintStorage: PackageFingerprintStorage {
let fileSystem: FileSystem
Expand Down
2 changes: 2 additions & 0 deletions Sources/PackageFingerprint/PackageFingerprintStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import Basics
import Dispatch
import PackageModel

import struct TSCUtility.Version

public protocol PackageFingerprintStorage {
func get(package: PackageIdentity,
version: Version,
Expand Down
2 changes: 2 additions & 0 deletions Sources/PackageGraph/DependencyResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import Dispatch
import PackageModel
import TSCBasic

import struct TSCUtility.Version

public protocol DependencyResolver {
typealias Binding = (package: PackageReference, binding: BoundVersion, products: ProductFilter)
typealias Delegate = DependencyResolverDelegate
Expand Down
2 changes: 1 addition & 1 deletion Sources/SPMBuildCore/PluginInvocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ extension ObservabilityMetadata {
}
}

public struct FileLocation: Equatable, CustomStringConvertible {
public struct FileLocation: Equatable, CustomStringConvertible, Sendable {
public let file: AbsolutePath
public let line: Int?

Expand Down