mirror of
https://github.com/apple/container.git
synced 2026-09-12 18:55:43 +00:00
Transition to actors (#225)
Due to the reduced use of the macro, we can now fully transition to `Mutex`.
This commit is contained in:
+1
-10
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "de8e92cc53d3475bc48033bb3a62c1be5fff6cfaa8277de1d89f19524d834833",
|
||||
"originHash" : "ac3ef791bf32cf99904bb681e6465fd729983add7f517f1c9342249121799bef",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
@@ -208,15 +208,6 @@
|
||||
"version" : "2.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-syntax",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-syntax.git",
|
||||
"state" : {
|
||||
"revision" : "0dff260d3d1bb99c382d8dfcd6bb093e5e9cbd36",
|
||||
"version" : "602.0.0-prerelease-2025-05-29"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-system",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
@@ -33,7 +33,6 @@ let package = Package(
|
||||
.library(name: "ContainerizationOS", targets: ["ContainerizationOS"]),
|
||||
.library(name: "ContainerizationExtras", targets: ["ContainerizationExtras"]),
|
||||
.library(name: "ContainerizationArchive", targets: ["ContainerizationArchive"]),
|
||||
.library(name: "SendableProperty", targets: ["SendableProperty"]),
|
||||
.executable(name: "cctl", targets: ["cctl"]),
|
||||
],
|
||||
dependencies: [
|
||||
@@ -46,7 +45,6 @@ let package = Package(
|
||||
.package(url: "https://github.com/apple/swift-nio.git", from: "2.80.0"),
|
||||
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.1"),
|
||||
.package(url: "https://github.com/apple/swift-system.git", from: "1.4.0"),
|
||||
.package(url: "https://github.com/swiftlang/swift-syntax.git", "602.0.0-latest"..<"603.0.0"),
|
||||
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"),
|
||||
],
|
||||
targets: [
|
||||
@@ -64,7 +62,6 @@ let package = Package(
|
||||
"ContainerizationOS",
|
||||
"ContainerizationIO",
|
||||
"ContainerizationExtras",
|
||||
"SendableProperty",
|
||||
.target(name: "ContainerizationEXT4", condition: .when(platforms: [.macOS])),
|
||||
],
|
||||
exclude: [
|
||||
@@ -204,7 +201,6 @@ let package = Package(
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
"CShim",
|
||||
"ContainerizationError",
|
||||
"SendableProperty",
|
||||
],
|
||||
exclude: [
|
||||
"../ContainerizationOS/README.md"
|
||||
@@ -244,25 +240,5 @@ let package = Package(
|
||||
.target(
|
||||
name: "CShim"
|
||||
),
|
||||
// Library that exposes a macro as part of its API, which is used in client programs.
|
||||
.target(name: "SendableProperty", dependencies: ["SendablePropertyMacros"]),
|
||||
// Macro implementation that performs the source transformation of a macro.
|
||||
.macro(
|
||||
name: "SendablePropertyMacros",
|
||||
dependencies: [
|
||||
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
|
||||
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
|
||||
]
|
||||
),
|
||||
// A test target used to develop the macro implementation.
|
||||
.testTarget(
|
||||
name: "SendablePropertyMacrosTests",
|
||||
dependencies: [
|
||||
"SendablePropertyMacros",
|
||||
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
|
||||
]
|
||||
),
|
||||
// A test target for the macro implementation.
|
||||
.testTarget(name: "SendablePropertyTests", dependencies: ["SendableProperty"]),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ package final class IndexedAddressAllocator<AddressType: CustomStringConvertible
|
||||
}
|
||||
}
|
||||
|
||||
private let stateGuard: Mutex<State>
|
||||
private let state: Mutex<State>
|
||||
|
||||
/// Create an allocator with specified size and index mappings.
|
||||
package init(
|
||||
@@ -57,11 +57,11 @@ package final class IndexedAddressAllocator<AddressType: CustomStringConvertible
|
||||
addressToIndex: addressToIndex,
|
||||
indexToAddress: indexToAddress
|
||||
)
|
||||
self.stateGuard = Mutex(state)
|
||||
self.state = Mutex(state)
|
||||
}
|
||||
|
||||
public func allocate() throws -> AddressType {
|
||||
try self.stateGuard.withLock { state in
|
||||
try self.state.withLock { state in
|
||||
guard state.enabled else {
|
||||
throw AllocatorError.allocatorDisabled
|
||||
}
|
||||
@@ -81,7 +81,7 @@ package final class IndexedAddressAllocator<AddressType: CustomStringConvertible
|
||||
}
|
||||
|
||||
package func reserve(_ address: AddressType) throws {
|
||||
try self.stateGuard.withLock { state in
|
||||
try self.state.withLock { state in
|
||||
guard state.enabled else {
|
||||
throw AllocatorError.allocatorDisabled
|
||||
}
|
||||
@@ -101,7 +101,7 @@ package final class IndexedAddressAllocator<AddressType: CustomStringConvertible
|
||||
}
|
||||
|
||||
package func release(_ address: AddressType) throws {
|
||||
try self.stateGuard.withLock { state in
|
||||
try self.state.withLock { state in
|
||||
guard let index = state.addressToIndex(address) else {
|
||||
throw AllocatorError.invalidAddress(address.description)
|
||||
}
|
||||
@@ -116,7 +116,7 @@ package final class IndexedAddressAllocator<AddressType: CustomStringConvertible
|
||||
}
|
||||
|
||||
package func disableAllocator() -> Bool {
|
||||
self.stateGuard.withLock { state in
|
||||
self.state.withLock { state in
|
||||
guard state.allocationCount == 0 else {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ package final class RotatingAddressAllocator: AddressAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
private let stateGuard: Mutex<State>
|
||||
private let state: Mutex<State>
|
||||
|
||||
/// Create an allocator with specified size and index mappings.
|
||||
package init(
|
||||
@@ -52,11 +52,11 @@ package final class RotatingAddressAllocator: AddressAllocator {
|
||||
addressToIndex: addressToIndex,
|
||||
indexToAddress: indexToAddress
|
||||
)
|
||||
self.stateGuard = Mutex(state)
|
||||
self.state = Mutex(state)
|
||||
}
|
||||
|
||||
public func allocate() throws -> AddressType {
|
||||
try self.stateGuard.withLock { state in
|
||||
try self.state.withLock { state in
|
||||
guard state.enabled else {
|
||||
throw AllocatorError.allocatorDisabled
|
||||
}
|
||||
@@ -77,7 +77,7 @@ package final class RotatingAddressAllocator: AddressAllocator {
|
||||
}
|
||||
|
||||
package func reserve(_ address: AddressType) throws {
|
||||
try self.stateGuard.withLock { state in
|
||||
try self.state.withLock { state in
|
||||
guard state.enabled else {
|
||||
throw AllocatorError.allocatorDisabled
|
||||
}
|
||||
@@ -97,7 +97,7 @@ package final class RotatingAddressAllocator: AddressAllocator {
|
||||
}
|
||||
|
||||
package func release(_ address: AddressType) throws {
|
||||
try self.stateGuard.withLock { state in
|
||||
try self.state.withLock { state in
|
||||
guard let index = (state.addressToIndex(address)) else {
|
||||
throw AllocatorError.invalidAddress(address.description)
|
||||
}
|
||||
@@ -113,7 +113,7 @@ package final class RotatingAddressAllocator: AddressAllocator {
|
||||
}
|
||||
|
||||
package func disableAllocator() -> Bool {
|
||||
self.stateGuard.withLock { state in
|
||||
self.state.withLock { state in
|
||||
guard state.allocationCount == 0 else {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// A declaration of the `@SendableProperty` macro.
|
||||
@attached(peer, names: arbitrary)
|
||||
@attached(accessor)
|
||||
public macro SendableProperty() = #externalMacro(module: "SendablePropertyMacros", type: "SendablePropertyMacro")
|
||||
@@ -1,20 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// A declaration of the `@SendablePropertyUnchecked` macro.
|
||||
@attached(peer, names: arbitrary)
|
||||
@attached(accessor)
|
||||
public macro SendablePropertyUnchecked() = #externalMacro(module: "SendablePropertyMacros", type: "SendablePropertyMacroUnchecked")
|
||||
@@ -1,41 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// `Synchronization` will be automatically imported with `SendableProperty`.
|
||||
@_exported import Synchronization
|
||||
|
||||
/// A synchronization primitive that protects shared mutable state via mutual exclusion.
|
||||
public final class Synchronized<T>: Sendable {
|
||||
private let lock: Mutex<State>
|
||||
|
||||
private struct State: @unchecked Sendable {
|
||||
var value: T
|
||||
}
|
||||
|
||||
/// Creates a new instance.
|
||||
/// - Parameter value: The initial value.
|
||||
public init(_ value: T) {
|
||||
self.lock = Mutex(State(value: value))
|
||||
}
|
||||
|
||||
/// Calls the given closure after acquiring the lock and returns its value.
|
||||
/// - Parameter body: The body of code to execute while the lock is held.
|
||||
public func withLock<R>(_ body: (inout T) throws -> R) rethrows -> R {
|
||||
try lock.withLock { state in
|
||||
try body(&state.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Errors that can be thrown by `@SendableProperty`.
|
||||
enum SendablePropertyError: CustomStringConvertible, Error {
|
||||
case unexpectedError
|
||||
case onlyApplicableToVar
|
||||
case notApplicableToType
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .unexpectedError: return "The macro encountered an unexpected error"
|
||||
case .onlyApplicableToVar: return "The macro can only be applied to a variable"
|
||||
case .notApplicableToType: return "The macro can't be applied to a variable of this type"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import SwiftCompilerPlugin
|
||||
import SwiftParser
|
||||
import SwiftSyntax
|
||||
import SwiftSyntaxBuilder
|
||||
import SwiftSyntaxMacros
|
||||
|
||||
/// A macro that allows to make a property of a supported type thread-safe keeping the `Sendable` conformance of the type.
|
||||
public struct SendablePropertyMacro: PeerMacro {
|
||||
private static let allowedTypes: Set<String> = [
|
||||
"Int", "UInt", "Int16", "UInt16", "Int32", "UInt32", "Int64", "UInt64", "Float", "Double", "Bool", "UnsafeRawPointer", "UnsafeMutableRawPointer", "UnsafePointer",
|
||||
"UnsafeMutablePointer",
|
||||
]
|
||||
|
||||
private static func checkPropertyType(in declaration: some DeclSyntaxProtocol) throws {
|
||||
guard let varDecl = declaration.as(VariableDeclSyntax.self),
|
||||
let binding = varDecl.bindings.first,
|
||||
let typeAnnotation = binding.typeAnnotation,
|
||||
let id = typeAnnotation.type.as(IdentifierTypeSyntax.self)
|
||||
else {
|
||||
// Nothing to check.
|
||||
return
|
||||
}
|
||||
|
||||
var typeName = id.name.text
|
||||
// Allow optionals of the allowed types.
|
||||
if typeName.prefix(9) == "Optional<" && typeName.suffix(1) == ">" {
|
||||
typeName = String(typeName.dropFirst(9).dropLast(1))
|
||||
}
|
||||
// Allow generics of the allowed types.
|
||||
if typeName.contains("<") {
|
||||
typeName = String(typeName.prefix { $0 != "<" })
|
||||
}
|
||||
|
||||
guard allowedTypes.contains(typeName) else {
|
||||
throw SendablePropertyError.notApplicableToType
|
||||
}
|
||||
}
|
||||
|
||||
/// The macro expansion that introduces a `Sendable`-conforming "peer" declaration for a thread-safe storage for the value of the given declaration of a variable.
|
||||
/// - Parameters:
|
||||
/// - node: The given attribute node.
|
||||
/// - declaration: The given declaration.
|
||||
/// - context: The macro expansion context.
|
||||
public static func expansion(
|
||||
of node: SwiftSyntax.AttributeSyntax, providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext
|
||||
) throws -> [SwiftSyntax.DeclSyntax] {
|
||||
try checkPropertyType(in: declaration)
|
||||
return try SendablePropertyMacroUnchecked.expansion(of: node, providingPeersOf: declaration, in: context)
|
||||
}
|
||||
}
|
||||
|
||||
extension SendablePropertyMacro: AccessorMacro {
|
||||
/// The macro expansion that adds `Sendable`-conforming accessors to the given declaration of a variable.
|
||||
/// - Parameters:
|
||||
/// - node: The given attribute node.
|
||||
/// - declaration: The given declaration.
|
||||
/// - context: The macro expansion context.
|
||||
public static func expansion(
|
||||
of node: SwiftSyntax.AttributeSyntax, providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext
|
||||
) throws -> [SwiftSyntax.AccessorDeclSyntax] {
|
||||
try checkPropertyType(in: declaration)
|
||||
return try SendablePropertyMacroUnchecked.expansion(of: node, providingAccessorsOf: declaration, in: context)
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import SwiftCompilerPlugin
|
||||
import SwiftParser
|
||||
import SwiftSyntax
|
||||
import SwiftSyntaxBuilder
|
||||
import SwiftSyntaxMacros
|
||||
|
||||
/// A macro that allows to make a property of a custom type thread-safe keeping the `Sendable` conformance of the type. This macro can be used with classes and enums. Avoid using it with structs, arrays, and dictionaries.
|
||||
public struct SendablePropertyMacroUnchecked: PeerMacro {
|
||||
private static func peerPropertyName(for propertyName: String) -> String {
|
||||
"_" + propertyName
|
||||
}
|
||||
|
||||
/// The macro expansion that introduces a `Sendable`-conforming "peer" declaration for a thread-safe storage for the value of the given declaration of a variable.
|
||||
/// - Parameters:
|
||||
/// - node: The given attribute node.
|
||||
/// - declaration: The given declaration.
|
||||
/// - context: The macro expansion context.
|
||||
public static func expansion(
|
||||
of node: SwiftSyntax.AttributeSyntax, providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext
|
||||
) throws -> [SwiftSyntax.DeclSyntax] {
|
||||
guard let varDecl = declaration.as(VariableDeclSyntax.self),
|
||||
let binding = varDecl.bindings.first,
|
||||
let pattern = binding.pattern.as(IdentifierPatternSyntax.self)
|
||||
else {
|
||||
throw SendablePropertyError.onlyApplicableToVar
|
||||
}
|
||||
|
||||
let propertyName = pattern.identifier.text
|
||||
let hasInitializer = binding.initializer != nil
|
||||
let initializerValue = binding.initializer?.value.description ?? "nil"
|
||||
|
||||
var genericTypeAnnotation = ""
|
||||
if let typeAnnotation = binding.typeAnnotation {
|
||||
let typeName = typeAnnotation.type.description.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
genericTypeAnnotation = "<\(typeName)\(hasInitializer ? "" : "?")>"
|
||||
}
|
||||
|
||||
let accessLevel = varDecl.modifiers.first(where: { ["open", "public", "internal", "fileprivate", "private"].contains($0.name.text) })?.name.text ?? "internal"
|
||||
|
||||
// Create a peer property
|
||||
let peerPropertyName = self.peerPropertyName(for: propertyName)
|
||||
let peerProperty: DeclSyntax =
|
||||
"""
|
||||
\(raw: accessLevel) let \(raw: peerPropertyName) = Synchronized\(raw: genericTypeAnnotation)(\(raw: initializerValue))
|
||||
"""
|
||||
return [peerProperty]
|
||||
}
|
||||
}
|
||||
|
||||
extension SendablePropertyMacroUnchecked: AccessorMacro {
|
||||
/// The macro expansion that adds `Sendable`-conforming accessors to the given declaration of a variable.
|
||||
/// - Parameters:
|
||||
/// - node: The given attribute node.
|
||||
/// - declaration: The given declaration.
|
||||
/// - context: The macro expansion context.
|
||||
public static func expansion(
|
||||
of node: SwiftSyntax.AttributeSyntax, providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext
|
||||
) throws -> [SwiftSyntax.AccessorDeclSyntax] {
|
||||
guard let varDecl = declaration.as(VariableDeclSyntax.self),
|
||||
let binding = varDecl.bindings.first,
|
||||
let pattern = binding.pattern.as(IdentifierPatternSyntax.self)
|
||||
else {
|
||||
throw SendablePropertyError.onlyApplicableToVar
|
||||
}
|
||||
|
||||
let propertyName = pattern.identifier.text
|
||||
let hasInitializer = binding.initializer != nil
|
||||
|
||||
// Replace the property with an accessor
|
||||
let peerPropertyName = Self.peerPropertyName(for: propertyName)
|
||||
|
||||
let accessorGetter: AccessorDeclSyntax =
|
||||
"""
|
||||
get {
|
||||
\(raw: peerPropertyName).withLock { $0\(raw: hasInitializer ? "" : "!") }
|
||||
}
|
||||
"""
|
||||
let accessorSetter: AccessorDeclSyntax =
|
||||
"""
|
||||
set {
|
||||
\(raw: peerPropertyName).withLock { $0 = newValue }
|
||||
}
|
||||
"""
|
||||
|
||||
return [accessorGetter, accessorSetter]
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import SwiftCompilerPlugin
|
||||
import SwiftSyntaxMacros
|
||||
|
||||
/// A plugin that registers the `SendablePropertyMacroUnchecked` and `SendablePropertyMacro`.
|
||||
@main
|
||||
struct SendablePropertyPlugin: CompilerPlugin {
|
||||
let providingMacros: [Macro.Type] = [
|
||||
SendablePropertyMacroUnchecked.self,
|
||||
SendablePropertyMacro.self,
|
||||
]
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import SwiftSyntax
|
||||
import SwiftSyntaxBuilder
|
||||
import SwiftSyntaxMacros
|
||||
import SwiftSyntaxMacrosTestSupport
|
||||
import XCTest
|
||||
|
||||
// Macro implementations build for the host, so the corresponding module is not available when cross-compiling. Cross-compiled tests may still make use of the macro itself in end-to-end tests.
|
||||
#if canImport(SendablePropertyMacros)
|
||||
import SendablePropertyMacros
|
||||
|
||||
let testMacros: [String: Macro.Type] = [
|
||||
"SendableProperty": SendablePropertyMacro.self
|
||||
]
|
||||
#endif
|
||||
|
||||
final class SendablePropertyMacrosTests: XCTestCase {
|
||||
func testMacroExpansionWithTypeAnnotation() throws {
|
||||
#if canImport(SendablePropertyMacros)
|
||||
assertMacroExpansion(
|
||||
"""
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value: Int
|
||||
}
|
||||
""",
|
||||
expandedSource:
|
||||
"""
|
||||
final class TestMacro: Sendable {
|
||||
var value: Int {
|
||||
get {
|
||||
_value.withLock {
|
||||
$0!
|
||||
}
|
||||
}
|
||||
set {
|
||||
_value.withLock {
|
||||
$0 = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal let _value = Synchronized<Int?>(nil)
|
||||
}
|
||||
""",
|
||||
macros: testMacros
|
||||
)
|
||||
#else
|
||||
throw XCTSkip("macros are only supported when running tests for the host platform")
|
||||
#endif
|
||||
}
|
||||
|
||||
func testMacroExpansionWithInitialValue() throws {
|
||||
#if canImport(SendablePropertyMacros)
|
||||
assertMacroExpansion(
|
||||
"""
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value = 0
|
||||
}
|
||||
""",
|
||||
expandedSource:
|
||||
"""
|
||||
final class TestMacro: Sendable {
|
||||
var value {
|
||||
get {
|
||||
_value.withLock {
|
||||
$0
|
||||
}
|
||||
}
|
||||
set {
|
||||
_value.withLock {
|
||||
$0 = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal let _value = Synchronized(0)
|
||||
}
|
||||
""",
|
||||
macros: testMacros
|
||||
)
|
||||
#else
|
||||
throw XCTSkip("macros are only supported when running tests for the host platform")
|
||||
#endif
|
||||
}
|
||||
|
||||
func testMacroExpansionWithTypeAnnotationAndInitialValue() throws {
|
||||
#if canImport(SendablePropertyMacros)
|
||||
assertMacroExpansion(
|
||||
"""
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value: Int = 0
|
||||
}
|
||||
""",
|
||||
expandedSource:
|
||||
"""
|
||||
final class TestMacro: Sendable {
|
||||
var value: Int {
|
||||
get {
|
||||
_value.withLock {
|
||||
$0
|
||||
}
|
||||
}
|
||||
set {
|
||||
_value.withLock {
|
||||
$0 = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal let _value = Synchronized<Int>(0)
|
||||
}
|
||||
""",
|
||||
macros: testMacros
|
||||
)
|
||||
#else
|
||||
throw XCTSkip("macros are only supported when running tests for the host platform")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the Containerization project authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import SendableProperty
|
||||
import XCTest
|
||||
|
||||
final class SendablePropertyTests: XCTestCase {
|
||||
func testMacroWithTypeAnnotation() throws {
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value: Int
|
||||
}
|
||||
|
||||
let testMacro = TestMacro()
|
||||
testMacro.value = 42
|
||||
XCTAssertTrue(testMacro.value == 42)
|
||||
}
|
||||
|
||||
func testMacroWithInitialValue() throws {
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value = 0
|
||||
}
|
||||
|
||||
let testMacro = TestMacro()
|
||||
XCTAssertTrue(type(of: testMacro.value) == Int.self)
|
||||
XCTAssertTrue(testMacro.value == 0)
|
||||
testMacro.value = 42
|
||||
XCTAssertTrue(testMacro.value == 42)
|
||||
}
|
||||
|
||||
func testMacroWithTypeAnnotationAndInitialValue() throws {
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value: Int = 0
|
||||
}
|
||||
|
||||
let testMacro = TestMacro()
|
||||
testMacro.value = 42
|
||||
XCTAssertTrue(testMacro.value == 42)
|
||||
}
|
||||
|
||||
func testMacroInConcurrentThreads() throws {
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var value = ""
|
||||
}
|
||||
|
||||
let testMacro = TestMacro()
|
||||
let loremIpsum =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
|
||||
let numberOfIterations = 100_000
|
||||
let queue = DispatchQueue(label: "com.apple.sendable-property-tests", attributes: .concurrent)
|
||||
let dispatchGroup = DispatchGroup()
|
||||
for i in 0..<numberOfIterations {
|
||||
dispatchGroup.enter()
|
||||
queue.async {
|
||||
testMacro.value = "\(loremIpsum) (\(i))"
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
}
|
||||
dispatchGroup.wait()
|
||||
}
|
||||
|
||||
func testMacroWithSupportedTypes() throws {
|
||||
final class TestMacro: Sendable {
|
||||
@SendableProperty
|
||||
var int: Int
|
||||
@SendableProperty
|
||||
var uint: UInt
|
||||
@SendableProperty
|
||||
var int16: Int16
|
||||
@SendableProperty
|
||||
var uint16: UInt16
|
||||
@SendableProperty
|
||||
var int32: Int32
|
||||
@SendableProperty
|
||||
var uint32: UInt32
|
||||
@SendableProperty
|
||||
var int64: Int64
|
||||
@SendableProperty
|
||||
var uint64: UInt64
|
||||
@SendableProperty
|
||||
var float: Float
|
||||
@SendableProperty
|
||||
var double: Double
|
||||
@SendableProperty
|
||||
var bool: Bool
|
||||
@SendableProperty
|
||||
var unsafeRawPoiner: UnsafeRawPointer
|
||||
@SendableProperty
|
||||
var unsafeMutableRawPointer: UnsafeMutableRawPointer
|
||||
@SendableProperty
|
||||
var unsafePoiner: UnsafePointer<Int>
|
||||
@SendableProperty
|
||||
var unsafeMutablePointer: UnsafeMutablePointer<Int>
|
||||
|
||||
@SendableProperty
|
||||
var intOpt: Int?
|
||||
@SendableProperty
|
||||
var uintOpt: UInt?
|
||||
@SendableProperty
|
||||
var int16Opt: Int16?
|
||||
@SendableProperty
|
||||
var uint16Opt: UInt16?
|
||||
@SendableProperty
|
||||
var int32Opt: Int32?
|
||||
@SendableProperty
|
||||
var uint32Opt: UInt32?
|
||||
@SendableProperty
|
||||
var int64Opt: Int64?
|
||||
@SendableProperty
|
||||
var uint64Opt: UInt64?
|
||||
@SendableProperty
|
||||
var floaOptt: Float?
|
||||
@SendableProperty
|
||||
var doubleOpt: Double?
|
||||
@SendableProperty
|
||||
var boolOpt: Bool?
|
||||
@SendableProperty
|
||||
var unsafeRawPoinerOpt: UnsafeRawPointer?
|
||||
@SendableProperty
|
||||
var unsafeMutableRawPointerOpt: UnsafeMutableRawPointer?
|
||||
@SendableProperty
|
||||
var unsafePoinerOpt: UnsafePointer<Int>?
|
||||
@SendableProperty
|
||||
var unsafeMutablePointerOpt: UnsafeMutablePointer<Int>?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ final class ManagedProcess: Sendable {
|
||||
|
||||
private let log: Logger
|
||||
private let process: Command
|
||||
private let lock: Mutex<State>
|
||||
private let state: Mutex<State>
|
||||
private let syncfd: Pipe
|
||||
private let owningPid: Int32?
|
||||
|
||||
@@ -44,7 +44,7 @@ final class ManagedProcess: Sendable {
|
||||
}
|
||||
|
||||
var pid: Int32 {
|
||||
self.lock.withLock {
|
||||
self.state.withLock {
|
||||
$0.pid
|
||||
}
|
||||
}
|
||||
@@ -121,13 +121,13 @@ final class ManagedProcess: Sendable {
|
||||
try io.start(process: &process)
|
||||
|
||||
self.process = process
|
||||
self.lock = Mutex(State(io: io))
|
||||
self.state = Mutex(State(io: io))
|
||||
}
|
||||
}
|
||||
|
||||
extension ManagedProcess {
|
||||
func start() throws -> Int32 {
|
||||
try self.lock.withLock {
|
||||
try self.state.withLock {
|
||||
log.info(
|
||||
"starting managed process",
|
||||
metadata: [
|
||||
@@ -164,7 +164,7 @@ extension ManagedProcess {
|
||||
}
|
||||
|
||||
func setExit(_ status: Int32) {
|
||||
self.lock.withLock {
|
||||
self.state.withLock {
|
||||
self.log.info(
|
||||
"managed process exit",
|
||||
metadata: [
|
||||
@@ -185,7 +185,7 @@ extension ManagedProcess {
|
||||
/// Wait on the process to exit
|
||||
func wait() async -> Int32 {
|
||||
await withCheckedContinuation { cont in
|
||||
self.lock.withLock {
|
||||
self.state.withLock {
|
||||
if let status = $0.exitStatus {
|
||||
cont.resume(returning: status)
|
||||
return
|
||||
@@ -196,7 +196,7 @@ extension ManagedProcess {
|
||||
}
|
||||
|
||||
func kill(_ signal: Int32) throws {
|
||||
try self.lock.withLock {
|
||||
try self.state.withLock {
|
||||
guard $0.exitStatus == nil else {
|
||||
return
|
||||
}
|
||||
@@ -209,7 +209,7 @@ extension ManagedProcess {
|
||||
}
|
||||
|
||||
func resize(size: Terminal.Size) throws {
|
||||
try self.lock.withLock {
|
||||
try self.state.withLock {
|
||||
guard $0.exitStatus == nil else {
|
||||
return
|
||||
}
|
||||
@@ -218,7 +218,7 @@ extension ManagedProcess {
|
||||
}
|
||||
|
||||
func closeStdin() throws {
|
||||
try self.lock.withLock {
|
||||
try self.state.withLock {
|
||||
try $0.io.closeStdin()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid
|
||||
log: log
|
||||
)
|
||||
|
||||
try proxy.start()
|
||||
try await proxy.start()
|
||||
try await state.add(proxy: proxy)
|
||||
} catch {
|
||||
log.error(
|
||||
@@ -202,7 +202,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid
|
||||
|
||||
do {
|
||||
let proxy = try await state.remove(proxy: request.id)
|
||||
try proxy.close()
|
||||
try await proxy.close()
|
||||
} catch {
|
||||
log.error(
|
||||
"stopVsockProxy",
|
||||
|
||||
@@ -18,9 +18,8 @@ import ContainerizationIO
|
||||
import ContainerizationOS
|
||||
import Foundation
|
||||
import Logging
|
||||
import SendableProperty
|
||||
|
||||
final class VsockProxy: Sendable {
|
||||
actor VsockProxy {
|
||||
enum Action {
|
||||
case listen
|
||||
case dial
|
||||
@@ -54,9 +53,8 @@ final class VsockProxy: Sendable {
|
||||
private let udsPerms: UInt32?
|
||||
private let log: Logger?
|
||||
|
||||
@SendableProperty
|
||||
private var listener: Socket?
|
||||
private let task = Mutex<Task<(), Never>?>(nil)
|
||||
private var task: Task<(), Never>?
|
||||
}
|
||||
|
||||
extension VsockProxy {
|
||||
@@ -70,11 +68,14 @@ extension VsockProxy {
|
||||
if fm.fileExists(atPath: self.path.path) {
|
||||
try FileManager.default.removeItem(at: self.path)
|
||||
}
|
||||
let task = task.withLock { $0 }
|
||||
task?.cancel()
|
||||
self.listener = nil
|
||||
}
|
||||
|
||||
func start() throws {
|
||||
guard listener == nil else {
|
||||
return
|
||||
}
|
||||
switch self.action {
|
||||
case .dial:
|
||||
try dialHost()
|
||||
@@ -140,7 +141,7 @@ extension VsockProxy {
|
||||
self.log?.error("failed to accept connection: \(error)")
|
||||
}
|
||||
}
|
||||
self.task.withLock { $0 = task }
|
||||
self.task = task
|
||||
}
|
||||
|
||||
private func handleConn(
|
||||
|
||||
Reference in New Issue
Block a user