or "code I had to write on my own but I'd hoped for smarter people to already have implemented".
I have been using Concurrency religiously since its very beginning in several iOS projects with often very niche system frameworks or third party frameworks and I wanted to share my learnings and my wishes to further progression in Concurrency.
While I appreciate the (I'd call them) advanced Concurrency features recently proposed (region based isolation, custom actor executors, inheritance of actor isolation, ...) I'd also like to see some movement for more basic functionalities, which I will elaborate.
I'd also be interested in how other teams or developers solve these kind of problems. Maybe I am just missing some points.
Deadline (or timeouts)
I have seen many implementations in this forum. Some good, some which are missing a couple of points. Some relying on cooperative cancellation, some relying on multiple Task { }
.
I think this is a necessary tool when working with APIs that work with different kinds of communication channels. Fortunately a lot of frameworks already support this out of the box, like URLSession
or even NetService
. One framework which does not have such behavior is CoreBluetooth
. One could argue that this is a missing feature of this framework, but I am pretty sure there are a lot of other use cases (based on the number of posts about this in the forums), which would make it a good candidate to include a maintained implementation somewhere in the Swift ecosystem. Another reason would be that I think it is not easy to get right, and a lot of people don't get it quite right.
There has been some drafting on this in AsyncAlgorithms but there was no real movement for almost 2 years. One of the more prominent thread with implementations is this one.
My temporary solution was to copy the implementation of AsyncAlgorithms
.
Auto cancelling continuations
For a long time I felt like I am doing something wrong for wanting this, but this post gave me confidence that this is actually something missing.
A lot of APIs do not support cancellation on their own, so there is nothing to put in the onCancel
block. However in a some cases you actually still want the continuation to resume (with eg. CancellationError
) in order to make other concepts like TaskGroup
s work like you (or maybe just me, naively) expect because it will wait for every child to be cancelled and not just resume work.
As pointed out in the other thread it again is not easy to implement. You need locks or an actor, and locking in onCancel
as pointed out in the documentation could be problematic.
My temporary solution was to write my own (potentially deadlocking) auto cancelling continuation.
Broadcasting async sequences
I think this is also wished for quite frequently on here. Either I am misusing some APIs or this is a very useful tool when bridging frameworks into either reactive, or in general the concurrent world. I am thinking of a manager type class or actor that conforms to some delegate which you want to subscribe to in multiple points in your app.
This is currently not really possible without either building your own array of Continuation
s, like creating one AsyncSequence
at each call point and storing the continuations in an array and yield on all elements once you receive mentioned delegate events, or relying on Combine features like PassthroughSubject
or CurrentValueSubject
and using their .values
. Both of these solutions feel kind of wrong. The first solution probably even wronger, because I am probably missing some conditions where this will not work.
This has also already been proposed in AsyncAlgorithms back in 2022. No movement here. (Why?)
Queueing
I've had to work with APIs or hardware which require queueing but don't implement it themselves. This is rather easily implemented in non-Concurrency code with GCD. However there is no official way on how to implement this with Concurrency.
There are many open source projects out there which implement this (also in posts in this forum). Some of them miss edge cases or are just not ergonomic. The best solution (and also my go-to for now) is using a small wrapper around AsyncSemaphore
.
I do get that this problem might not be something stdlib or AsyncAlgorithms should solve. But this kind of relates to the problem that there are no non-reentrant types in the current Concurrency system. Also recently mentioned in this post. I think with non-reentrant actors you could implement a queue rather easily yourself.
Final words
Please don't get me wrong, I don't want to complain about missing progress or missing features, or hijack the other forum posts. I just wanted to collect some pain points which I've had the last couple of years and maybe get some more insights or ultimately progress on these topics.
Thanks.