Skip to content
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

Add Support for Custom Pause, Resume, and Cancel Blocks #37

Merged
merged 10 commits into from
Dec 15, 2024
23 changes: 18 additions & 5 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,28 @@ The easiest way to test this package on Linux is to use Docker. You can use the
docker run --rm --privileged --interactive --tty \
--volume "$(pwd):/src" \
--workdir "/src" \
swift:5.10
swift:6.0
```

Also, you can use the following tags:

- Use `swift:6.0` to use the Swift 6.0 version.
- Use `swift:6.0-noble` to use the Swift 6.0 version with Ubuntu 24.04.
- Use `swift:6.0-jammy` to use the Swift 6.0 version with Ubuntu 22.04.
- Use `swift:6.0-focal` to use the Swift 6.0 version with Ubuntu 20.04.
- Use `swift:5.10` to use the Swift 5.10 version.
- Use `swift:5.10-noble` to use the Swift 5.10 version with Ubuntu 24.04.
- Use `swift:5.10-jammy` to use the Swift 5.10 version with Ubuntu 22.04.
- Use `swift:5.10-focal` to use the Swift 5.10 version with Ubuntu 20.04.
- Use `swift:5.9` to use the Swift 5.9 version.
- Use `swift:5.9-noble` to use the Swift 5.9 version with Ubuntu 24.04.
- Use `swift:5.9-jammy` to use the Swift 5.9 version with Ubuntu 22.04.
- Use `swift:5.9-focal` to use the Swift 5.9 version with Ubuntu 20.04.

> [!TIP]
> Use `swift:5.10` to use a specific Swift version. If you want to use the latest version, you can use `swift:latest`.
>
> Use `swift:5.10-jammy` to use the Swift 5.10 version with Ubuntu 22.04 and `swift:5.10-focal` to use the Swift 5.10 version with Ubuntu 20.04.
> If you want to use the latest version, you can use `swift:latest`.

1. Run the following command to run the test suite:
3. Run the following command to run the test suite:

```bash
swift test
Expand Down
8 changes: 4 additions & 4 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ _Describe the platforms that your PR supports. If your PR does not support a pla

_Make sure you check off the following items. If they cannot be completed, provide a reason._

- [ ] Added tests
- [ ] Added documentation
- [ ] Added a changelog entry
- [ ] Added to the README the new functionality description
- [ ] Added Unit Tests to cover the new changes
- [ ] Added code documentation with `///` to reflect the new changes
- [ ] Added the new changes to the CHANGELOG.md
- [ ] Added the new functionality description to the README.md
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
### Added

- Added CIHelper to run test on Linux but not on CI - [#33](https://github.com/FabrizioBrancati/Queuer/pull/33)
- Added `onPause`, `onResume`, and `onCancel` closures to `ConcurrentOperation` class - [#37](https://github.com/FabrizioBrancati/Queuer/pull/37)

### Improved

- Improved usage section in README.md file
- Updated swift-docc-plugin to 1.4.3

## [3.0.1](https://github.com/FabrizioBrancati/Queuer/releases/tag/3.0.1) - No Loop No Party

Expand Down
60 changes: 44 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,31 @@ Add the dependency to any targets you've declared in your manifest:

## Usage

- [Shared Queuer](https://github.com/FabrizioBrancati/Queuer#shared-queuer)
- [Custom Queue](https://github.com/FabrizioBrancati/Queuer#custom-queue)
- [Using the Shared Queuer](https://github.com/FabrizioBrancati/Queuer#using-the-shared-queuer)
- [Create a Custom Queue](https://github.com/FabrizioBrancati/Queuer#create-a-custom-queue)
- [Create an Operation Block](https://github.com/FabrizioBrancati/Queuer#create-an-operation-block)
- [Chained Operations](https://github.com/FabrizioBrancati/Queuer#chained-operations)
- [Group Operations](https://github.com/FabrizioBrancati/Queuer#group-operations)
- [Queue States](https://github.com/FabrizioBrancati/Queuer#queue-states)
- [Synchronous Queue](https://github.com/FabrizioBrancati/Queuer#synchronous-queue)
- [Use Chained Operations](https://github.com/FabrizioBrancati/Queuer#use-chained-operations)
- [Use Group Operations](https://github.com/FabrizioBrancati/Queuer#use-group-operations)
- [Available Queue States](https://github.com/FabrizioBrancati/Queuer#available-queue-states)
- [Control Operation States](https://github.com/FabrizioBrancati/Queuer#control-operation-states)
- [Use a Synchronous Queue](https://github.com/FabrizioBrancati/Queuer#use-a-synchronous-queue)
- [Create a Custom Operation](https://github.com/FabrizioBrancati/Queuer#create-a-custom-operation)
- [Automatically Retry an Operation](https://github.com/FabrizioBrancati/Queuer#automatically-retry-an-operation)
- [Manually Retry an Operation](https://github.com/FabrizioBrancati/Queuer#manually-retry-an-operation)
- [Manually Finish an Operation](https://github.com/FabrizioBrancati/Queuer#manually-finish-an-operation)
- [Async Task in an Operation](https://github.com/FabrizioBrancati/Queuer#async-task-in-an-operation)
- [Scheduler](https://github.com/FabrizioBrancati/Queuer#scheduler)
- [Semaphore](https://github.com/FabrizioBrancati/Queuer#semaphore)
- [Set Up a Scheduler](https://github.com/FabrizioBrancati/Queuer#set-up-a-scheduler)
- [Use a Semaphore](https://github.com/FabrizioBrancati/Queuer#use-a-semaphore)

### Shared Queuer
### Using the Shared Queuer

Queuer offers a shared instance that you can use to add operations to a centralized queue:

```swift
Queuer.shared.addOperation(operation)
```

### Custom Queue
### Create a Custom Queue

You can also create a custom queue:

Expand Down Expand Up @@ -118,7 +119,7 @@ You have three methods to add an `Operation` block.
> [!NOTE]
> We will see how `ConcurrentOperation` works later.

### Chained Operations
### Use Chained Operations

Chained Operations are `Operation`s that add a dependency each other.

Expand All @@ -144,7 +145,7 @@ queue.addCompletionHandler {
}
```

### Group Operations
### Use Group Operations

Group Operations are `Operation`s that handles a group of `Operation`s with a completion handler.

Expand Down Expand Up @@ -174,7 +175,7 @@ queue.addChainedOperations([groupOperationA, concurrentOperationC]) {

In this case the output will be the following one: `[[A & B -> completionHandler] -> C] -> completionHandler`.

### Queue States
### Available Queue States

There are a few method to handle the queue states.

Expand Down Expand Up @@ -211,7 +212,34 @@ There are a few method to handle the queue states.
> [!IMPORTANT]
> This function means that the queue will blocks the current thread until all `Operation`s are finished.

### Synchronous Queue
### Control Operation States

You can control the `ConcurrentOperation` states by calling `pause()`, `resume()`, `cancel()`.

> [!NOTE]
> If you use a `Queuer` object with all `ConcurrentOperation` objects inside to manage your queue, you should avoid calling them directly on the `ConcurrentOperation` object, but you should call them on the `Queuer` one. The `Queuer` object will handle the `ConcurrentOperation` states automatically.

1. Pause a `ConcurrentOperation`:

```swift
concurrentOperation.pause()
```

2. Resume a `ConcurrentOperation`:

```swift
concurrentOperation.resume()
```

3. Cancel a `ConcurrentOperation`:

```swift
concurrentOperation.cancel()
```

For convenience, you can set closures to the `ConcurrentOperation` object to handle the states with `onPause`, `onResume`, and `onCancel` properties. They will be called when the `ConcurrentOperation` is paused, resumed, or canceled.

### Use a Synchronous Queue

Setting the `maxConcurrentOperationCount` property of a queue to `1` will make you sure that only one task at a time will be executed.

Expand Down Expand Up @@ -315,7 +343,7 @@ concurrentOperation.manualFinish = true
> [!CAUTION]
> If you don't set `manualFinish` to `true`, your `Operation` will finish before the async task is completed.

### Scheduler
### Set Up a Scheduler

A `Scheduler` is a struct that uses the GDC's `DispatchSourceTimer` to create a timer that can execute functions with a specified interval and quality of service.

Expand All @@ -340,7 +368,7 @@ With `timer` property you can access to all `DispatchSourceTimer` properties and
schedule.timer.cancel()
```

### Semaphore
### Use a Semaphore

A `Semaphore` is a struct that uses the GCD's `DispatchSemaphore` to create a semaphore on the function and wait until it finish its job.

Expand Down
31 changes: 27 additions & 4 deletions Sources/Queuer/ConcurrentOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ open class ConcurrentOperation: Operation {
/// `Operation`'s execution block.
public var executionBlock: ((_ operation: ConcurrentOperation) -> Void)?

/// `Operation`'s pause block.
/// This block is called when the `Operation` is paused.
public var onPause: ((_ operation: ConcurrentOperation) -> Void)?

/// `Operation`'s resume block.
/// This block is called when the `Operation` is resumed.
public var onResume: ((_ operation: ConcurrentOperation) -> Void)?

/// `Operation`'s resume block.
/// This block is called when the `Operation` is canceled.
public var onCancel: ((_ operation: ConcurrentOperation) -> Void)?

/// Set if the `Operation` is executing.
private var _executing = false {
willSet {
Expand Down Expand Up @@ -159,12 +171,23 @@ open class ConcurrentOperation: Operation {
}

/// Pause the current `Operation`, if it's supported.
/// Must be overridden by a subclass to get a custom pause action.
open func pause() {}
/// It can be overridden to add custom behavior.
open func pause() {
onPause?(self)
}

/// Resume the current `Operation`, if it's supported.
/// Must be overridden by a subclass to get a custom resume action.
open func resume() {}
/// It can be overridden to add custom behavior.
open func resume() {
onResume?(self)
}

/// Cancel the current `Operation`, if it's supported.
/// It can be overridden to add custom behavior.
override open func cancel() {
super.cancel()
onCancel?(self)
}
}

/// `ConcurrentOperation` extension with queue handling.
Expand Down
10 changes: 8 additions & 2 deletions Sources/Queuer/Queuer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,17 @@ public class Queuer {
}

/// Cancel all `Operation`s in queue.
@available(*, deprecated, message: "Use `cancel()` instead.")
public func cancelAll() {
cancel()
}

/// Cancel all `Operation`s in queue.
public func cancel() {
queue.cancelAllOperations()
}

/// Pause the queue.
/// Pause all `Operation`s in queue.
public func pause() {
queue.isSuspended = true

Expand All @@ -99,7 +105,7 @@ public class Queuer {
}
}

/// Resume the queue.
/// Resume all `Operation`s in queue.
public func resume() {
queue.isSuspended = false

Expand Down
1 change: 1 addition & 0 deletions Tests/QueuerTests/ConcurrentOperationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
}
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
func testAsyncChainedRetry() async {
if CIHelper.isNotRunningOnCI() {
let queue = Queuer(name: "ConcurrentOperationTestChainedRetry")
Expand Down Expand Up @@ -318,7 +319,7 @@

let deadline = DispatchTime.now() + .seconds(2)
DispatchQueue.global(qos: .background).asyncAfter(deadline: deadline) {
queue.cancelAll()

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.3)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.3)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.3)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.2)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.2)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.2)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.1)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.1)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.1)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.0.1)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.0.1)

'cancelAll()' is deprecated: Use `cancel()` instead.

Check warning on line 322 in Tests/QueuerTests/ConcurrentOperationTests.swift

View workflow job for this annotation

GitHub Actions / macOS (15.0.1)

'cancelAll()' is deprecated: Use `cancel()` instead.
testExpectation.fulfill()
}

Expand Down
Loading