https://github.com/ph1ps/swift-concurrency-deadline
A deadline algorithm for Swift Concurrency
https://github.com/ph1ps/swift-concurrency-deadline
concurrency deadline swift swift-concurrency timeout
Last synced: 6 months ago
JSON representation
A deadline algorithm for Swift Concurrency
- Host: GitHub
- URL: https://github.com/ph1ps/swift-concurrency-deadline
- Owner: ph1ps
- License: mit
- Created: 2024-08-14T18:38:13.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2024-11-06T20:06:57.000Z (6 months ago)
- Last Synced: 2024-11-06T21:20:17.827Z (6 months ago)
- Topics: concurrency, deadline, swift, swift-concurrency, timeout
- Language: Swift
- Homepage:
- Size: 40 KB
- Stars: 19
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Deadline
A deadline algorithm for Swift Concurrency.[](https://swiftpackageindex.com/ph1ps/swift-concurrency-deadline)
[](https://swiftpackageindex.com/ph1ps/swift-concurrency-deadline)## Rationale
As I've previously stated on the [Swift forums](https://forums.swift.org/t/my-experience-with-concurrency/73197): in my opinion deadlines or timeouts are a missing piece in Swift's Concurrency system. Since this algorithm is not easy to get right I decided to open-source my implementation.## Details
The library comes with two free functions, one with a generic clock and another one which uses the `ContinuousClock` as default.
```swift
public func deadline(
until instant: C.Instant,
tolerance: C.Instant.Duration? = nil,
clock: C,
isolation: isolated (any Actor)? = #isolation,
operation: @Sendable () async throws -> R
) async throws -> R where C: Clock, R: Sendable { ... }public func deadline(
until instant: ContinuousClock.Instant,
tolerance: ContinuousClock.Instant.Duration? = nil,
isolation: isolated (any Actor)? = #isolation,
operation: @Sendable () async throws -> R
) async throws -> R where R: Sendable { ... }
```This function provides a mechanism for enforcing timeouts on asynchronous operations that lack native deadline support. It creates a `TaskGroup` with two concurrent tasks: the provided operation and a sleep task.
- Parameters:
- `instant`: The absolute deadline for the operation to complete.
- `tolerance`: The allowed tolerance for the deadline.
- `clock`: The clock used for timing the operation.
- `isolation`: The isolation passed on to the task group.
- `operation`: The asynchronous operation to be executed.- Returns: The result of the operation if it completes before the deadline.
- Throws: `DeadlineExceededError`, if the operation fails to complete before the deadline and errors thrown by the operation or clock.> [!CAUTION]
> The operation closure must support cooperative cancellation. Otherwise, the deadline will not be respected.### Examples
To fully understand this, let's illustrate the 3 outcomes of this function:#### Outcome 1
The operation finishes in time:
```swift
let result = try await deadline(until: .now + .seconds(5)) {
// Simulate long running task
try await Task.sleep(for: .seconds(1))
return "success"
}
```
As you'd expect, result will be "success". The same applies when your operation fails in time:
```swift
let result = try await deadline(until: .now + .seconds(5)) {
// Simulate long running task
try await Task.sleep(for: .seconds(1))
throw CustomError()
}
```
This will throw `CustomError`.#### Outcome 2
The operation does not finish in time:
```swift
let result = try await deadline(until: .now + .seconds(1)) {
// Simulate even longer running task
try await Task.sleep(for: .seconds(5))
return "success"
}
```
This will throw `DeadlineExceededError` because the operation will not finish in time.#### Outcome 3
The parent task was cancelled:
```swift
let task = Task {
do {
try await deadline(until: .now + .seconds(5)) {
try await URLSession.shared.data(from: url)
}
} catch {
print(error)
}
}task.cancel()
```
The print is guaranteed to print `URLError(.cancelled)`.## Improvements
- Only have one free function with a default expression of `ContinuousClock` for the `clock` parameter.
- Blocked by: https://github.com/swiftlang/swift/issues/72199
- Use `sending` instead of `@Sendable` for region based isolation support.
- Blocked by: https://github.com/swiftlang/swift/issues/76242
- Use `@isolated(any)` for synchronous task enqueueing support.
- Blocked by: https://github.com/swiftlang/swift/issues/76604