Skip to content

Commit

Permalink
swift concurrency
Browse files Browse the repository at this point in the history
  • Loading branch information
alemar11 committed Apr 23, 2024
1 parent 8e5f3d7 commit bd2c2e2
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 17 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

### 7.0.0

- Xcode 15
- Swift 5.10
- Swift Concurrency support

### 6.3.0

- Xcode 13
Expand Down
8 changes: 6 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

import PackageDescription

let swiftSettings: [SwiftSetting] = [
.enableExperimentalFeature("StrictConcurrency")
]

let package = Package(
name: "AdvancedOperation",
platforms: [.macOS(.v13), .iOS(.v16), .tvOS(.v16), .watchOS(.v5), .visionOS(.v1)],
products: [
.library(name: "AdvancedOperation", targets: ["AdvancedOperation"])
],
targets: [
.target(name: "AdvancedOperation", path: "Sources"),
.testTarget(name: "AdvancedOperationTests", dependencies: ["AdvancedOperation"])
.target(name: "AdvancedOperation", path: "Sources", swiftSettings: swiftSettings),
.testTarget(name: "AdvancedOperationTests", dependencies: ["AdvancedOperation"], swiftSettings: swiftSettings)
],
swiftLanguageVersions: [.v5]
)
2 changes: 1 addition & 1 deletion Sources/Operations/AsynchronousBlockOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public typealias AsyncBlockOperation = AsynchronousBlockOperation
/// A sublcass of `AsynchronousOperation` to execute a closure.
public final class AsynchronousBlockOperation: AsynchronousOperation {
/// A closure type that takes a closure as its parameter.
public typealias Block = (@escaping () -> Void) -> Void
public typealias Block = (@Sendable @escaping () -> Void) -> Void

// MARK: - Private Properties

Expand Down
2 changes: 1 addition & 1 deletion Sources/Operations/ResultOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import os.lock
/// An `AsynchronousOperation` that produces a `result` once finished.
///
/// If a `ResultOperation` gets cancelled before being executed, no result will be produced by default.
open class ResultOperation<Success, Failure>: AsynchronousOperation where Failure: Error {
open class ResultOperation<Success, Failure>: AsynchronousOperation where Success: Sendable, Failure: Error {
/// Block executed after the operation is finished providing the final result (if any).
/// - Note: `onFinish` and `completionBlock` call order is not guaranteed in any way.
public var onFinish: ((Result<Success, Failure>?) -> Void)?
Expand Down
25 changes: 17 additions & 8 deletions Tests/AdvancedOperationTests/Core/AsynchronousOperationTests.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// AdvancedOperation

import XCTest

import os.lock
@testable import AdvancedOperation

final class AsynchronousOperationTests: XCTestCase {
Expand Down Expand Up @@ -296,6 +296,7 @@ final class AsynchronousOperationTests: XCTestCase {
XCTAssertTrue(operation1.isFinished)
}

@MainActor
func testAllOperationCancelled() {
let queue = OperationQueue()
let expectation1 = expectation(description: "\(#function)\(#line)")
Expand Down Expand Up @@ -398,30 +399,38 @@ final class AsynchronousOperationTests: XCTestCase {
let expectation = XCTKVOExpectation(
keyPath: #keyPath(Operation.isFinished), object: operation2, expectedValue: true)

var eventsForOperation = [Event]()
var eventsForOperation2 = [Event]()
let eventsForOperation = OSAllocatedUnfairLock(initialState: [Event]())
let eventsForOperation2 = OSAllocatedUnfairLock(initialState: [Event]())

let tokenExecuting = operation.observe(\.isExecuting, options: [.prior, .old, .new]) { (op, change) in
eventsForOperation.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
eventsForOperation.withLock {
$0.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
}
}

let tokenFinishing = operation.observe(\.isFinished, options: [.prior, .old, .new]) { (op, change) in
eventsForOperation.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
eventsForOperation.withLock {
$0.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
}
}

let tokenExecuting2 = operation.observe(\.isExecuting, options: [.prior, .old, .new]) { (op, change) in
eventsForOperation2.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
eventsForOperation2.withLock {
$0.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
}
}

let tokenFinishing2 = operation.observe(\.isFinished, options: [.prior, .old, .new]) { (op, change) in
eventsForOperation2.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
eventsForOperation2.withLock {
$0.append(Event(isPrior: change.isPrior, oldValue: change.oldValue, newValue: change.newValue))
}
}

operation.start()
operation2.start()

wait(for: [expectation], timeout: 2)
XCTAssertEqual(eventsForOperation, eventsForOperation2)
XCTAssertEqual(eventsForOperation.withLock({ $0 }), eventsForOperation2.withLock({ $0 }))
tokenExecuting.invalidate()
tokenExecuting2.invalidate()
tokenFinishing.invalidate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class ProgressReportingTests: XCTestCase {

currentProgress.addChild(groupOperation.progress, withPendingUnitCount: 1)

var logs = [String]()
nonisolated(unsafe) var logs = [String]()
let token = currentProgress.observe(\.fractionCompleted, options: [.initial, .old, .new]) { progress, _ in
// gather some progress data to be used if the test fails
let log =
Expand Down Expand Up @@ -358,7 +358,7 @@ final class ProgressReportingTests: XCTestCase {

currentProgress.addChild(queue.progress, withPendingUnitCount: 1)

var fractions = [Double]()
nonisolated(unsafe) var fractions = [Double]()
let expectedFractions = [0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0]
let token = currentProgress.observe(\.fractionCompleted, options: [.initial, .old, .new]) { (progress, change) in
print("fraction: \(progress.fractionCompleted)", "-", progress.localizedAdditionalDescription ?? "")
Expand Down
2 changes: 1 addition & 1 deletion Tests/AdvancedOperationTests/Helpers/Mocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ internal final class DummyFailableOperation: FailableAsyncOperation<DummyFailabl
// MARK: - GroupOperation

internal final class ProducerGroupOperation: GroupOperation {
init(operation: @escaping () -> Operation) {
init(operation: @Sendable @escaping () -> Operation) {
super.init(operations: [])
let producer = BlockOperation { [unowned self] in
// operation can be "produced" and added to the GroupOperation only if the producer is still running
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ final class AsynchronousBlockOperationTests: XCTestCase {
wait(for: [expectation1, expectation2], timeout: 10, enforceOrder: true)
}

@MainActor
func testComposition() {
let expectation3 = expectation(description: "\(#function)\(#line)")
let operation1 = SleepyAsyncOperation()
Expand All @@ -76,8 +77,10 @@ final class AsynchronousBlockOperationTests: XCTestCase {
XCTAssertTrue(adapterOperation.isFinished)
}

@MainActor
func testMemoryLeak() {
var object = NSObject()
class Dummy: @unchecked Sendable { }
var object = Dummy()
weak var weakObject = object

autoreleasepool {
Expand All @@ -96,7 +99,7 @@ final class AsynchronousBlockOperationTests: XCTestCase {

// Memory leaks test: once the operation is released, the captured object (by reference) should be nil (weakObject)
operation = AsynchronousBlockOperation { _ in }
object = NSObject()
object = Dummy()
}

XCTAssertNil(weakObject, "Memory leak: the object should have been deallocated at this point.")
Expand Down

0 comments on commit bd2c2e2

Please sign in to comment.