Back-deployment of SE-0472 `Task.immediate`?

Is there any chance of back-deploying the new Task.immediate(name:priority:operation:)functions to older Apple platform versions than 26?

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?

5 Likes

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-crashing taskIsCurrentExecutor 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.

1 Like

Thank you, that clarifies things. And that’s why I asked about it under Development > Standard Library and not Evolution.

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.

1 Like

@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.