From 613d07edbb45eaa0fbc3fb1665cad6142be867fd Mon Sep 17 00:00:00 2001 From: Hiroshi Kimura Date: Tue, 24 Sep 2024 17:42:35 +0900 Subject: [PATCH] Swift6 (#6) --- .github/workflows/Build.yml | 8 ++-- Package.swift | 5 ++- .../TaskManagerActor.swift | 4 +- .../TaskQueueActor.swift | 45 +++++++++---------- .../TaskStackActor.swift | 7 ++- .../UnfairLockAtomic.swift | 2 +- 6 files changed, 35 insertions(+), 36 deletions(-) diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 0d5a074..fb8961a 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -4,12 +4,12 @@ on: [push, pull_request] jobs: test: - runs-on: macos-13 + runs-on: macos-14 steps: - - uses: maxim-lobanov/setup-xcode@v1.1 + - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "14.3" + xcode-version: "16.0" - uses: actions/checkout@v2 - name: Run Test - run: xcodebuild -scheme swift-concurrency-task-manager test -destination 'platform=iOS Simulator,name=iPhone 14 Pro,OS=16.4' | xcpretty + run: xcodebuild -scheme swift-concurrency-task-manager test -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=18.0' | xcpretty diff --git a/Package.swift b/Package.swift index 77d2ead..2142a5e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.7 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -34,5 +34,6 @@ let package = Package( name: "ConcurrencyTaskManagerTests", dependencies: ["ConcurrencyTaskManager"] ), - ] + ], + swiftLanguageModes: [.v5, .v6] ) diff --git a/Sources/ConcurrencyTaskManager/TaskManagerActor.swift b/Sources/ConcurrencyTaskManager/TaskManagerActor.swift index 487c77f..c2dc084 100644 --- a/Sources/ConcurrencyTaskManager/TaskManagerActor.swift +++ b/Sources/ConcurrencyTaskManager/TaskManagerActor.swift @@ -233,11 +233,11 @@ public actor TaskManagerActor { task(label: label, key: key, mode: mode, priority: priority, action) } - public func batch(_ closure: (isolated TaskManagerActor) -> Void) { + public func batch(_ closure: sending @Sendable (isolated TaskManagerActor) -> Void) { closure(self) } - public func batch(_ closure: (isolated TaskManagerActor) async -> Void) async { + public func batch(_ closure: sending @Sendable (isolated TaskManagerActor) async -> Void) async { await closure(self) } diff --git a/Sources/ConcurrencyTaskManager/TaskQueueActor.swift b/Sources/ConcurrencyTaskManager/TaskQueueActor.swift index 8f8a568..e3e9cfb 100644 --- a/Sources/ConcurrencyTaskManager/TaskQueueActor.swift +++ b/Sources/ConcurrencyTaskManager/TaskQueueActor.swift @@ -1,16 +1,17 @@ import Foundation +@preconcurrency import Combine /** structure of linked-list it has a node for the next task after this node. */ -final class TaskNode: CustomStringConvertible { +final class TaskNode: CustomStringConvertible, @unchecked Sendable { struct WeakBox { weak var value: T? } - private struct State { + private struct State: Sendable { var isActivated: Bool = false var isFinished: Bool = false @@ -20,7 +21,7 @@ final class TaskNode: CustomStringConvertible { private var anyTask: _Verge_TaskType? - let taskFactory: (WeakBox) async -> Void + let taskFactory: (sending WeakBox) async -> Void private(set) var next: TaskNode? let label: String @@ -29,7 +30,7 @@ final class TaskNode: CustomStringConvertible { init( label: String = "", - @_inheritActorContext taskFactory: @escaping @Sendable (WeakBox) async -> Void + @_inheritActorContext taskFactory: @escaping @Sendable (sending WeakBox) async -> Void ) { self.label = label self.taskFactory = taskFactory @@ -179,18 +180,8 @@ public actor TaskQueueActor { } // connecting to the next if presents - - await self?.batch { - guard let node = box.value else { return } - if let next = node.next { - $0.head = next - next.activate() - } else { - if $0.head === node { - $0.head = nil - } - } - } + + await self?.advance(box: box) } @@ -240,13 +231,17 @@ public actor TaskQueueActor { } } - - /** - Performs given closure in a critical session - */ - @discardableResult - public func batch(_ perform: (isolated TaskQueueActor) -> Return) -> Return { - return perform(self) + + func advance(box: sending TaskNode.WeakBox) { + guard let node = box.value else { return } + if let next = node.next { + self.head = next + next.activate() + } else { + if self.head === node { + self.head = nil + } + } } } @@ -275,7 +270,7 @@ final class AutoReleaseContinuationBox: @unchecked Sendable { self.continuation = continuation } - func resume(throwing error: Error) { + func resume(throwing error: sending Error) { lock.lock() defer { lock.unlock() @@ -287,7 +282,7 @@ final class AutoReleaseContinuationBox: @unchecked Sendable { continuation?.resume(throwing: error) } - func resume(returning value: T) { + func resume(returning value: sending T) { lock.lock() defer { lock.unlock() diff --git a/Sources/ConcurrencyTaskManager/TaskStackActor.swift b/Sources/ConcurrencyTaskManager/TaskStackActor.swift index da6b631..b8f82db 100644 --- a/Sources/ConcurrencyTaskManager/TaskStackActor.swift +++ b/Sources/ConcurrencyTaskManager/TaskStackActor.swift @@ -1,5 +1,6 @@ import DequeModule import Foundation +@preconcurrency import Combine public actor TaskStackActor { @@ -105,7 +106,7 @@ public actor TaskStackActor { continuation.yield(state) } - continuation.onTermination = { _ in + continuation.onTermination = { _ in cancellable.cancel() } @@ -129,7 +130,9 @@ public actor TaskStackActor { state.update(waitingCount: stack.count, executingCount: currentExecutingTaskCount) } - private func processForCompletion(taskNode: TaskNode) { + private func processForCompletion( + taskNode: sending TaskNode + ) { executings.removeAll { $0 === taskNode } } diff --git a/Tests/ConcurrencyTaskManagerTests/UnfairLockAtomic.swift b/Tests/ConcurrencyTaskManagerTests/UnfairLockAtomic.swift index bac2afe..80c771e 100644 --- a/Tests/ConcurrencyTaskManagerTests/UnfairLockAtomic.swift +++ b/Tests/ConcurrencyTaskManagerTests/UnfairLockAtomic.swift @@ -1,6 +1,6 @@ import os.atomic -public final class UnfairLock: Sendable { +public final class UnfairLock: @unchecked Sendable { private let _lock: os_unfair_lock_t public init() {