Skip to content

Commit

Permalink
Merge pull request #37 from FabrizioBrancati/features/pause-resume-ca…
Browse files Browse the repository at this point in the history
…ncel

Add Support for Custom Pause, Resume, and Cancel Blocks
  • Loading branch information
FabrizioBrancati authored Dec 15, 2024
2 parents 91efa29 + 7785b31 commit 461d9fc
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 31 deletions.
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 @@ final class ConcurrentOperationTests: XCTestCase {
}
}

@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

0 comments on commit 461d9fc

Please sign in to comment.