Skip to main content

detached_with_priority

Function detached_with_priority 

Source
pub fn detached_with_priority(
    priority: TaskPriority,
    body: impl FnOnce() + Send + 'static,
) -> Task
Expand description

Run body as a detached Swift Task at a chosen priority.

The closure runs on Swift’s global concurrent executor, so it must be Send and must not touch anything that wants the main thread — which is most of SwiftUI. It is FnOnce, because a task runs its body exactly once and owns it: values can be moved in, and the closure is dropped as soon as it returns.

A panic inside it is caught and reported rather than unwound into the Swift async frame, where there is no landing pad; the task still completes.

As many at once as you like. swift_task_create’s fifth argument is the closure context and the entry receives it in x20, so each task carries its own closure — unlike the .task modifier, whose async closure is handed back the context SwiftUI was given and so has to live in one of three fixed slots. crate::asyncfn describes that ceiling; this is the same machinery without it.

let work = runtime::detached(move || {
    // …on a cooperative thread…
});
work.wait();

§Two things this got wrong, both worth knowing

swift_task_create faults inside itself when either is wrong, which is a long way from the mistake. Both were found by breaking on the real call in a Swift binary and reading the registers.

  • The flags are not zero. Task.detached passes 1 << 12 — Task_EnqueueJob — which makes swift_task_create schedule the task itself. Passing zero and calling swift_task_enqueue afterwards is not the same thing.

  • $sytN is not ()’s metadata. It is the full metadata record, whose first word is the value witness table; the metadata address point is eight bytes further on. The runtime reads the result type’s value witnesses to size the future, so an address eight bytes low reads a function pointer as a size. crate::abi::resolve_type_by_mangled_name returns the address point, which is what Swift passes.

    The empty tuple is special in this, and it is worth saying so before someone generalises the lesson: $sSiN, $sSSN, $sSbN and $sSdN were checked against the same lookup and are exactly the address points, which is why the rest of this crate uses them directly. A tuple has no nominal descriptor, so its metadata symbol names a different thing.