I want to share my idea to you.
First, without any support about cancellation for async/await,
user can use pattern as like below.
// cancel notification sender interface
class CancelContext {
var token: CancelToken
func cancel()
}
// cancel notification receiver interface
class CancelToken {
func register(handler: @escaping () -> Void)
}
// cancellable async func take cancel token
func download(url: URL, cancelBy cancelToken: CancelToken) async -> Data
func decodeImage(data: Data, cancelBy cancelToken: CancelToken) async -> UIImage
// task driver create CancelContext
func onStartButton() async {
let cancelContext = CancelContext()
self.cancelContext = cancelContext
let cancelToken = cancelContext.token
// user needs to pass same cancelToken for async functions
// which are wanted to compose same cancellation group.
let data = await download(url: urlField.text, cancelBy: cancelToken)
let image = await decodeImage(data: data, cancelBy: cancelToken)
self.imageView.image = image
}
func onCancelButton() {
self.cancelContext?.cancel()
}
To generalize, almost composition of await call are become to below.
func start() async {
let cancelContext = CancelContext()
self.cancelContext = cancelContext
let cancelToken = cancelContext.token
let a = await f(cancelBy: cancelToken)
let b = await g(a, cancelBy: cancelToken)
let c = await h(b, cancelBy: cancelToken)
let d = await k(c, cancelBy: cancelToken)
...
}
There is a problem.
Repeating explicit cancelBy: cancelToken
is boring and too expressive.
User want to simply say "they are same group".
So I imagine code like blow.
func start() async {
let cancelContext = CancelContext()
self.cancelContext = cancelContext
cancelContext.grouping {
let a = await f()
let b = await g(a)
let c = await h(b)
let d = await k(c)
...
}
}
If we can write this and it run as same explicit pattern,
Its best solution about async/await with cancellation.
Unfortunately I can not implement this.
The thing seems to this, autoreleasepool
.
https://developer.apple.com/documentation/objectivec/2299644-autoreleasepool
This higher function is can be considered as
it applies implicit hidden argument to all autorelease
function invoking in it.
And in implementation detail, pool is stored at thread local storage.
Same technique can not apply to this cancellation grouping.
Because async function may switch thread.
So thread local storage is useless for it.
In this point, I got a one conclusion.
We need a some abstraction about single consecutive invocation of await.
In thread programming, this is Thread.current.threadDictionary
.
So for example, we need such like CoroutineContext.current.contextDictionary
.
If it is, each cancellable function take CancelToken
from this implicitly.