TaskGroup Crash

I’m seeing a crash that looks to be related to TaskGroup from Swift Concurrency. I can’t catch it in the debugger, only when running on device. It looks like the crash is actually in the Swift Concurrency library, and I’m not sure how to trace from there back to something in my own code...

Some digging so far suggests the issue only happens:

  • on iOS 15.0->15.3.x (ie. it looks to not happen on 15.4)
  • on older iPhones such as 6s, 6s plus, 7, 8, X

Where do I start trying to figure out the root cause of this?

So if I'm ready this correctly the TooltipView view's observableObject Model is placing a call to a generic Store find method using a dynamic key path lookup. What's going on in Store.find<A>(:) ?

Here’s the only code in my codebase that uses TaskGroup.

// Function that processes a bunch of 'action triggers' and produces a stream of actions
func run(action: Action, find: @escaping (Model.Type) -> Model?) -> AsyncStream<Action> {
    AsyncStream { continuation in
        Task {
            await withTaskGroup(of: Void.self) { group in
                for trigger in triggers {
                    group.addTask {
                        for await result in trigger(action, find) {
                            if result is VoidAction { continue }
                            continuation.yield(result)
                        }
                    }
                }

                await group.waitForAll()
                continuation.finish()
            }
        }
    }
}

// Later, from an async context. Process an action and dispatch its output.
Task {
    for await output in run(action: action, find: { store.find($0) }) {
        try await store.dispatch(output)
    }
}

I was able to solve the crash by explicitly marking both the task group’s closures as @MainActor. But would love to get a deeper understanding of why this doesn’t work.

            await withTaskGroup(of: Void.self) { @MainActor group in
                for trigger in triggers {
                    group.addTask { @MainActor in
                        for await result in trigger(action, find) {
                            if result is VoidAction { continue }
                            continuation.yield(result)
                        }
                    }
                }

                await group.waitForAll()
                continuation.finish()
            }

Just means something meant to only work on the main thread was being called on the background since it no longer crashes after bringing the task group back onto the main thread.

I'm also seeing this issue on the same OS versions and devices. I think the bug may be that TaskGroups aren't properly inheriting the execution context of their parent tasks. For example, in:

Task { @MainActor in
  await withTaskGroup(of: Void.self) { taskGroup in
    taskGroup.addTask {
      foo()
    }
    bar()
  }
}

I'd expect foo() and bar() to run on @MainActor since the task is run there, but it seems that on these specific OS versions that's not always the case.

TaskGroup Crash
 
 
Q