It seems like async let is not working well with isolation checking.
I'm trying to make backport feature of DiscardableTaskGroup.
Thanks to generalized async-sequence evolution with backport of TaskGroup isolation.
I was able to constrain all the mutation of TaskGroup to happen in the same isolation while, enqueueing multiple task to mutate TaskGroup
Reproduction
extension Actor {
func withDiscardingGroup<TaskResult>(
body: (isolated Self, inout TaskGroup<Void>) async -> sending TaskResult
) async -> sending TaskResult {
let holder: SafetyRegion = self as? SafetyRegion ?? .init()
if await holder.isFinished {
preconditionFailure("SafetyRegion is already used!")
}
return await withTaskGroup(of: Void.self, returning: TaskResult.self) { group in
group.addTask(priority: .background) {
/// keep at least one child task alive
/// so that subTask won't return
await holder.hold()
}
/// drain all the finished or failed Task
async let subTask:Void = {
// Error: 'self'-isolated 'group' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
while let _ = await group.next(isolation: self) {
if await holder.isFinished {
break
}
}
}()
// passing isolated self to extend isolation
// Error:'self'-isolated 'body' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
let value = await body(self, &group)
await subTask
return value
}
}
}
@usableFromInline
package actor SafetyRegion {
@usableFromInline
internal(set) package var isFinished = false
@usableFromInline
internal var continuation: UnsafeContinuation<Void,Never>? = nil
@inlinable
package init() {
}
@usableFromInline
package func markDone() {
// guard !isFinished else { return }
isFinished = true
continuation?.resume()
continuation = nil
}
@usableFromInline
internal func hold() async {
return await withUnsafeContinuation {
if isFinished {
$0.resume()
} else {
if let old = self.continuation {
assertionFailure("received suspend more than once!")
old.resume()
}
self.continuation = $0
}
}
}
}
compiler is saying additonal message:
Pattern that the region based isolation checker does not understand how to check. Please file a bug
Expected behavior
since everything is in the same isolation, and I'm just extending the isolation.
there is three un-sendable data. closure, taskGroup, and closure return.
In this case it is pretty obvious that every thing is captured by the same actor.
Environment
xcode 16.03 beta toolchain
m1 macbook air
Additional information
For now, using unchecked Sendable and nonisolated annotation can bypass compiler check as an workaround.
Description
It seems like
async let
is not working well with isolation checking.I'm trying to make backport feature of
DiscardableTaskGroup
. Thanks to generalized async-sequence evolution with backport ofTaskGroup
isolation. I was able to constrain all the mutation ofTaskGroup
to happen in the same isolation while, enqueueing multiple task to mutateTaskGroup
Reproduction
compiler is saying additonal message:
Pattern that the region based isolation checker does not understand how to check. Please file a bug
Expected behavior
since everything is in the same isolation, and I'm just extending the isolation.
there is three un-sendable data. closure, taskGroup, and closure return. In this case it is pretty obvious that every thing is captured by the same actor.
Environment
xcode 16.03 beta toolchain m1 macbook air
Additional information
For now, using unchecked Sendable and nonisolated annotation can bypass compiler check as an workaround.
Will this fix on next release, because of this?