Skip to content

Instantly share code, notes, and snippets.

@sidepelican
Created October 25, 2025 12:58
Show Gist options
  • Save sidepelican/b55902f8504969ec204088804a4d5e11 to your computer and use it in GitHub Desktop.
Save sidepelican/b55902f8504969ec204088804a4d5e11 to your computer and use it in GitHub Desktop.
TaskScope.swift
import Synchronization
public final class TaskScope: Sendable {
public typealias Job = @Sendable () async -> ()
private let stream: AsyncStream<Job>
private let continuation: AsyncStream<Job>.Continuation
private let isRunning = Atomic<Bool>(false)
public init() {
(stream, continuation) = AsyncStream.makeStream(bufferingPolicy: .unbounded)
}
public func launch(
@_inheritActorContext _ task: @escaping Job
) {
assert(
isRunning.load(ordering: .relaxed) == true,
"TaskScope is not runnig. Did you forget `.compose(viewModel)` or `.task { await taskScope.run() }` ?"
)
continuation.yield(task)
}
public func run() async {
let oldIsRunning = isRunning.exchange(true, ordering: .relaxed)
precondition(oldIsRunning == false, "run() is being called twice simultaneously.")
await withDiscardingTaskGroup { group in
for await task in stream {
group.addTask(operation: task)
}
}
isRunning.store(false, ordering: .relaxed)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment