Async, Rethrows, and Defer

In Swift, unlike e.g. C#, it’s not the task itself that’s awaited. I’m pretty sure this would have to be await Task { … }.value.

1 Like

Ignoring the missing .value this does work and is the current way to have an enforced clean-up, but we have to resort to unstructured Concurrency which indicates that we are lacking a feature here. We ought to be able to express everything with structured Concurrency so that the compiler can reason about this.

Having a defer where we can opt-out of cancellation would be great.

4 Likes

Fun gotcha: In my experience, Task.sleep(nanoseconds:) returns immediately if you do that. Sleeping the current task next-to-forever seems to work with .max / 2 nanoseconds however. Maybe it's internally reinterpreting the UInt64 as signed?

(Also you probably mean to try? await that so it handles and ignores CancellationError.)

4 Likes

This would check out: setting only the MSb (i.e. (.max >> 1) + 1 or ~UInt64(Int64.max)) returns just as immediately. That should either be fixed or documented.

The source of that bug is described here: Fix bug in Dispatch executor preventing long sleeps by rauhul · Pull Request #64304 · apple/swift · GitHub but I haven't found the time to follow up on my patch.

2 Likes

It doesn't look like someone can take the helm on implementing async defer, so in the meantime I have changed all these occasions to spawning a task and waiting for it w/ semaphores. Ugly, but does the job.

I, too, would like this, and I'd like to also throw out of defer:

try await defer { try await something() }
1 Like

+1 for a try-able await-able defer.

I really value the straight-line code where the deallocation is shown next to the allocation that the defer mechanism promotes. It's unfortunate that if the method (e.g. close()) is fallible and/or async that you can't use the defer mechanism at all.

1 Like

Hm, isn't try in a defer a bit weird? What if the function already threw and then throws again in the defer? Would it take the first error thrown or the last? I think there was probably a good reason why trying in defer was not introduced in the first place.

1 Like

I think that this case can be handled in a variety of ways. For example, here is what Go does for panics (their version of throwable errors) during defer execution, which is to wrap them all up.