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

[WIP] Deadline algorithm #215

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions Sources/AsyncAlgorithms/Deadline.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public struct TimeoutError<C: Clock>: Error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency this likely should be a DeadlineExceededError, we don't speak in terms of timeouts; timeout is the convenience API

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that might be a better name.

public let deadline: C.Instant
public let clock: C

public init(_ deadline: C.Instant, _ clock: C) {
self.deadline = deadline
self.clock = clock
}
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public func withDeadline<C: Clock, T: Sendable>(
_ deadline: C.Instant,
clock: C,
_ operation: @Sendable () async throws -> T
) async throws -> T {
return try await withoutActuallyEscaping(operation) { operation in
try await withThrowingTaskGroup(of: T.self) { group in
group.addTask(operation: operation)
group.addTask {
try await Task.sleep(until: deadline, clock: clock)
throw TimeoutError(deadline, clock)
}
defer { group.cancelAll() }
return try await group.next()!
}
}
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public func withTimeout<C: Clock, T: Sendable>(
in duration: C.Duration,
Copy link
Member

@ktoso ktoso Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think such should be called "timeout", rather this still forms a deadline and should be called with...Deadline(in: .seconds...) because it is defining a deadline in some seconds, it is not a timeout "in". Specifically this is (in timeout: C.Duration where the now + timeout == deadline) wording wise.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't a deadline in 5 seconds just a timeout starting at now in 5 seconds?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree here with @ktoso that we should define this with the term deadline. The difference between a deadline and a timeout is quite important. A deadline can be reused and it will always be the same point in time w.r.t. the clock used. A timeout would need to recalculated against your current time. Furthermore, a deadline is less error prone since suspended tasks/threads won't influence the actual point in time calculation.

clock: C,
_ operation: @Sendable () async throws -> T
) async throws -> T {
try await withDeadline(
clock.now.advanced(by: duration),
clock: clock,
operation)
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public func withTimeout<T: Sendable>(
in duration: Duration,
_ operation: @Sendable () async throws -> T
) async throws -> T {
try await withTimeout(in: duration, clock: .continuous, operation)
}