Transition to actors (#225)

Due to the reduced use of the macro, we can now fully transition to
`Mutex`.
This commit is contained in:
Dmitry Kovba
2025-07-29 08:10:48 -04:00
committed by GitHub
parent 95978185fd
commit d512fcb7cc
16 changed files with 31 additions and 669 deletions
+1 -10
View File
@@ -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",
-24
View File
@@ -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>?
}
}
}
+9 -9
View File
@@ -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()
}
}
+2 -2
View File
@@ -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",
+7 -6
View File
@@ -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(