swift-async-algorithms icon indicating copy to clipboard operation
swift-async-algorithms copied to clipboard

[WIP] Deadline algorithm

Open phausler opened this issue 3 years ago • 5 comments

This is a really common need; to create a deadline for given work and throw if that deadline passes.

phausler avatar Oct 14 '22 18:10 phausler

That's a bit of a misleading API... what people ask for and reinvent constantly currently is the unstructured task based one; this likely isn't what people expect by looking at the name.

We could tackle this by using a more descriptive name here, like withStructuredDeadline or something else to suggest that this is a structured concurrency thing, and thus WILL await anyway, but it'll cancel at least.

Perhaps we offer both... it is way easier to experiment in this package rather than in stdlib after all;

ktoso avatar Oct 17 '22 01:10 ktoso

Per the structured versus unstructured case: it only means that the deadline passing and the work child task being cancelled only becomes an issue if that work child task is unresponsive for cancellation. The cases where this is an issue is where that child task is actually a function that does not participate in cooperative cancellation; e.g. it does not fully participate in swift concurrency. That all being said; I am not sure that a withDeadline that is structured would really be that great in that it will likely end up orphaning tasks when used.

phausler avatar Oct 17 '22 15:10 phausler

Per the structured versus unstructured case: it only means that the deadline passing and the work child task being cancelled only becomes an issue if that work child task is unresponsive for cancellation. The cases where this is an issue is where that child task is actually a function that does not participate in cooperative cancellation; e.g. it does not fully participate in swift concurrency. That all being said; I am not sure that a withDeadline that is structured would really be that great in that it will likely end up orphaning tasks when used.

In general, I am with you here and I agree that we should offer this functionality; however, I think we really need to call out that this expects cooperative cancellation and might exceed the deadline. You never really know if some method that you call is cancelling at the first instance that cancellation is raised.

FranzBusch avatar Oct 19 '22 19:10 FranzBusch

This depends on iOS 16 and such, just because the dependency on the new clock feature. Would love to see a backport compatible version for iOS 13 and up. What do you think?

Fab1n avatar Dec 21 '22 14:12 Fab1n

Would it make sense to have a variant that does not throw when the deadline is reached, but instead cancels the operation and then returns whatever "best effort" value the operation returns?

Like, e.g.:

    try await withThrowingTaskGroup(of: T.self) { group in
      group.addTask(operation: operation)
      group.addTask {
        try? await Task.sleep(until: deadline, clock: clock)
        throw SomePrivateError()
      }
      do {
        defer { group.cancelAll() }
        return try await group.next()!
      } catch is SomePrivateError { // The timing child ended first (the outer task may have been cancelled)
        return try await group.next()! // Allow operation() to return a best effort result after cancellation
      }
    }

JJJensen avatar Dec 02 '23 13:12 JJJensen