mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
I apologize in advance to @jrose-apple, who is not a fan of this fix ;-) In unoptimized builds, the convenience initializers on DispatchQueue allocate and immediately deallocate an instance of OS_dispatch_queue prior to calling the C function that returns the "real" instance. This is because we don't have a way to write user-defined factory initializers yet; convenience initializers still have an 'initializing' entry point that takes an existing instance, which we have no choice but to throw away. Unfortunately, when we perform the fake allocation, we look up class metadata by calling the wrong Swift runtime function, causing a crash when we send +allocWithZone:. Fix this so that the metadata is accessed via a lookup from the Objective-C runtime, instead of making a totally fake 'foreign metadata' object -- it looks like there was code for this already, it just wasn't used in all cases. While getting metadata for a runtime-only class should be rare, this feels like a real bug fix, to me. Second, we would ultimately free the fake object by sending -release, however OS_dispatch_queue has an override of -dealloc which doesn't like to be called with a completely uninitialized instance. Here, I'm going to drop all pretense of sanity. The patch just changes IRGen to lower the dealloc_partial_ref instruction as a call to the object_dispose() Objective-C runtime function when the class in question is a runtime-only class. This frees the object without running -dealloc, which *happens* to work for OS_dispatch_queue. Fixes <rdar://problem/27226313>.
104 lines
2.2 KiB
Swift
104 lines
2.2 KiB
Swift
// RUN: %target-run-simple-swift
|
|
// REQUIRES: executable_test
|
|
|
|
// REQUIRES: objc_interop
|
|
|
|
import StdlibUnittest
|
|
import Dispatch
|
|
|
|
let iterations = 200_000
|
|
|
|
class Thing {}
|
|
|
|
class WBox<T: AnyObject> {
|
|
weak var wref: T?
|
|
init(_ ref: T) { self.wref = ref }
|
|
init() { self.wref = nil }
|
|
}
|
|
|
|
let WeakReferenceRaceTests = TestSuite("WeakReferenceRaceTests")
|
|
|
|
func forwardOptional<T>(_ t: T!) -> T {
|
|
return t!
|
|
}
|
|
|
|
WeakReferenceRaceTests.test("class instance property [SR-192] (copy)") {
|
|
let q = DispatchQueue(label: "", attributes: .concurrent)
|
|
|
|
// Capture a weak reference via its container object
|
|
// "https://bugs.swift.org/browse/SR-192"
|
|
for i in 1...iterations {
|
|
let box = WBox(Thing())
|
|
let closure = {
|
|
let nbox = WBox<Thing>()
|
|
nbox.wref = box.wref
|
|
_blackHole(nbox)
|
|
}
|
|
|
|
q.async(execute: closure)
|
|
q.async(execute: closure)
|
|
}
|
|
|
|
q.async(flags: .barrier) {}
|
|
}
|
|
|
|
WeakReferenceRaceTests.test("class instance property [SR-192] (load)") {
|
|
let q = DispatchQueue(label: "", attributes: .concurrent)
|
|
|
|
// Capture a weak reference via its container object
|
|
// "https://bugs.swift.org/browse/SR-192"
|
|
for i in 1...iterations {
|
|
let box = WBox(Thing())
|
|
let closure = {
|
|
if let ref = box.wref {
|
|
_blackHole(ref)
|
|
}
|
|
}
|
|
|
|
q.async(execute: closure)
|
|
q.async(execute: closure)
|
|
}
|
|
|
|
q.async(flags: .barrier) {}
|
|
}
|
|
|
|
WeakReferenceRaceTests.test("direct capture (copy)") {
|
|
let q = DispatchQueue(label: "", attributes: .concurrent)
|
|
|
|
// Capture a weak reference directly in multiple closures
|
|
for i in 1...iterations {
|
|
weak var wref = Thing()
|
|
let closure = {
|
|
let nbox = WBox<Thing>()
|
|
nbox.wref = wref
|
|
_blackHole(nbox)
|
|
}
|
|
|
|
q.async(execute: closure)
|
|
q.async(execute: closure)
|
|
}
|
|
|
|
q.async(flags: .barrier) {}
|
|
}
|
|
|
|
WeakReferenceRaceTests.test("direct capture (load)") {
|
|
let q = DispatchQueue(label: "", attributes: .concurrent)
|
|
|
|
// Capture a weak reference directly in multiple closures
|
|
for i in 1...iterations {
|
|
weak var wref = Thing()
|
|
let closure = {
|
|
if let ref = wref {
|
|
_blackHole(ref)
|
|
}
|
|
}
|
|
|
|
q.async(execute: closure)
|
|
q.async(execute: closure)
|
|
}
|
|
|
|
q.async(flags: .barrier) {}
|
|
}
|
|
|
|
runAllTests()
|