Proposal to add cancellation abilities for Async/Await

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.

2 Likes