The functionality introduced in SE-0472 is key to avoiding initial delays with async sequences in UI logic, but the current availability will delay [pun intended] its adoption in apps by years. With community back-ports like swift-perception (of Observation), we get to benefit from the new work in older OS versions already, but extensions to task scheduling seem harder if not impossible to back-deploy by 3rd-party libraries.
I understand that back deploying the original _Concurrency library all the way to macOS 10.15, iOS 13 etc. was a big undertaking. Wouldnât this seem like a logical extension to that?
As far as I can tell, this isnât possible due to the use of a new runtime function:
// Determine if we're switching isolation dynamically.
// If not, we can run the task synchronously and therefore MUST NOT "enqueue" it.
let flagsMustNotCrash: UInt64 = 0
let canRunSynchronously: Bool =
if let builtinSerialExecutor {
_taskIsCurrentExecutor(executor: builtinSerialExecutor, flags: flagsMustNotCrash)
} else {
true // if there is no target executor, we can run synchronously
}
_taskIsCurrentExecutor(executor:flags:) is limited to the same new platforms and looks like a way to use a new version of the function that doesnât have a runtime crash issue, even though they donât do anything with the flags.
Jon is right that this is a runtime feature. Backdeploying runtime features is extremely difficult from a process perspective and the bar to clear is therefore equally extremely high. Not having any concurrency features backdeployed did clear this bar, but it is rather unlikely individual improvements like this would make it.
In this case, is there more in the runtime than this non-crashingtaskIsCurrentExecutor check? Wasnât there always a non-crashing variant of it available?
But Swift API to actually return true/false wasnât â so this feature would have âsometimesâ worked because any involved custom task executor (including dispatch queues), would fail and crash the check. So⌠weâd have to pessimize those executors and not use the check. This would require even more special handling in backdeployment for Dispatch but would not work for other custom executors⌠Personally I think the more we have those âsometimes worksâ behaviors the worse off we are and developers would end up not being able to reliably use these..
The bottom line here is that backdeploying runtime features extremely difficult and the âwhat the backdeployed runtime should doâ isnât the problem, the core of the problem is rolling backdeploy libraries to begin with.
But can we consider a narrower use case? How much work would it be to introduce a Task.immediateOnMain counterpart, which would be @MainActor isolated on the (synchronous) call site and only promised to be immediate if the task to be started was either nonisolated or MainActor-isolated?
Would that be something a 3rd-party library could somehow define, potentially by accessing runtime symbols?
That alone would tackle 99% of todayâs UI needs.
That actually exists as SPI that Task.immediate aimed to generalize and replace. I donât think weâd want to âbring backâ the workaround that this was and Task.immediate replaced, however you could have a look at it.
@ktoso does Objective C use the same API for starting Swift async function in the same context? In my testing, Objective C invocations of Swift async functions work on custom executor as well.