[Review] SF-0023: Progress Reporting in Swift Concurrency

This is a reminder from the pitch discussion [1] of something not mentioned or addressed in the proposal: the completion states of tasks cancelled or in error.

Concurrency being structured means progress is composite, and thus that some sub-tasks may be done while others are incomplete; of those incomplete, some may have terminated via cancel or error.

The proposal does not address how these incomplete but terminal states are represented as progress values.

Granting that progress is not responsible for implementing or handling cancelled or errors, progress still needs to represent those states accurately to avoid creating more problems.

Because the proposal collapses all progress into a single numeric digest, any cancelled/failed task makes the entire progress digest invalid. In the current/max number scheme, the maximum would imply termination is completion, and using sub-maximal values will make digests incorrect and fail to represent a terminal state.

Unless the proposal represents these states, it will be impossible for developers to do so, unless a developer controls progress reporting for the entire forest of possible tasks and can somehow create and tunnel magic values via progress counters. But we hope and expect developers to use tasks from each other and from libraries, so the proposal should address this.

Since error handling and cancellation are a cooperative effort and code can be complex, it's common to get systems that are work normally but balk strangely on failure -- "strangely" because it can be hard to isolate what's happening or wrong. This is one of the biggest hurdles to concurrency in Swift: not understanding when it doesn't work as intended. If progress reporting could represent these states accurately, then it would really help.

Solutions depend on the current implementation flexibility:

  • Do nothing, but document as out-of-scope
    • -> developers at least on notice
  • Write sample code showing how developers could report this
    • -> developers agree, if diligent about following documentation
  • Create magic constants, known to all
    • -> compile-time agreement but without type-safety
  • Write static utilities or factories to enforce and detect magic values
    • -> Compatible for well-behaved developers
  • Use a binary-compatible struct wrapping the current Int
    • -> type-safe, ergonomic, and possibly interoperable?
  • Use an enum with associated values?
    • -> but the enum is complicated, difficult to extend, and not interoperable

If there are constraints against changing the format, the static utilities seem like the best solution. They also have the benefit of signaling whether source code is possibly cooperative or doesn't support it.

Thanks for your patience...

[1] [Pitch] Progress Reporting in Swift Concurrency - #47 by wes1

1 Like