swift
swift copied to clipboard
[SR-12479] Inconsistent behavior when using DispatchQueue.sync(flags:execute:) in property getter.
Previous ID | SR-12479 |
Radar | rdar://problem/62201587 |
Original Reporter | kuanfajardo (JIRA User) |
Type | Bug |
Additional Detail from JIRA
Votes | 0 |
Component/s | |
Labels | Bug |
Assignee | None |
Priority | Medium |
md5: a00f1c1f960a76d1c8611f9999e59b4e
Issue Description:
When performing a `sync(flags:execute: )` operation in a property getter, I get the following error __only if I actually pass in any flags:
Thread 1: closure argument passed as @noescape to Objective-C has escaped: file , line 56, column 56
@propertyWrapper
public final class GCDSynchronized<Value: Any> {
private var value: Value
private let queue: DispatchQueue
/* Various init methods */
public var wrappedValue: Value {
get {
-> queue.sync(flags: ***) { self.value } // If *** is not [], crash!
}
/* Setter */
}
}
It works just fine if I pass an empty array to the `flags` param, which to me is dumbfounding since it's the same method either way. Only thing I could think of is that the implementation of `sync(flags:execute)` calls `sync(execute: )` if `flags.isEmpty`, in which case my beef is with the former not handling my escaping closure 🙁
P.S. The line numbers from the error (56, 56) do not match my line numbers (which is 140).
@swift-ci create
This is still an issue with Xcode 14.3 (14E222b), though it might be restricted to .assignCurrentContext
:
import Foundation
let queue = DispatchQueue(label: "I'm a train choo choo", attributes: .concurrent)
queue.sync(flags: .barrier) { print("eenie") }
queue.async(flags: .barrier) { print("meenie") }
queue.async(flags: .assignCurrentContext) { print("minie") }
queue.sync(flags: .assignCurrentContext) { print("no soup for you") }
Confirmed on both x86 and M1 with both serial and concurrent queues: closure argument passed as @noescape to Objective-C has escaped: file , line 61, column 57
For what it's worth, my behavioral expectations for queue.sync(flags: .assignCurrentContext, execute: callback)
are:
- add intention to queue
- block current thread until all previously enqueued barriers are lifted
- resume thread to run
return try callback()
, potentially concurrently with other non-barrier threads (depending on queue attributes and the presence of the.barrier
flag) - job done, remove from queue and off to the pub