[K/N] Add more tests on associated object handling ^KT-56233
This commit is contained in:
committed by
Space Team
parent
dbe14a0a90
commit
87da670319
@@ -5960,6 +5960,23 @@ if (isAppleTarget(project)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frameworkTest("testKt56233Framework") {
|
||||
enabled = isExperimentalMM // Fails with legacy MM. It's deprecated, so won't fix.
|
||||
framework("Kt56233") {
|
||||
sources = ['framework/kt56233']
|
||||
}
|
||||
swiftSources = ['framework/kt56233']
|
||||
}
|
||||
|
||||
frameworkTest("testPermanentObjectsFramework") {
|
||||
enabled = isExperimentalMM
|
||||
framework("PermanentObjects") {
|
||||
sources = ['framework/permanentObjects']
|
||||
opts = ['-opt-in=kotlin.native.internal.InternalForKotlinNative']
|
||||
}
|
||||
swiftSources = ['framework/permanentObjects']
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
enum class SimpleEnum {
|
||||
ONE,
|
||||
TWO
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import Kt56233
|
||||
|
||||
func threadRoutine(pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
|
||||
autoreleasepool {
|
||||
let f = pointer.bindMemory(to: (() -> ()).self, capacity: 1).pointee
|
||||
f()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func launchThreads(
|
||||
_ f: @convention(c) () -> (),
|
||||
threadCount: Int = 4
|
||||
) throws {
|
||||
var threads: [pthread_t] = []
|
||||
for _ in 0..<threadCount {
|
||||
let fPtr = UnsafeMutablePointer<() -> ()>.allocate(capacity: 1)
|
||||
fPtr.initialize(to: f)
|
||||
var thread: pthread_t? = nil
|
||||
let result = pthread_create(&thread, nil, threadRoutine, fPtr)
|
||||
try assertEquals(actual: result, expected: 0)
|
||||
threads.append(thread!)
|
||||
}
|
||||
for thread in threads {
|
||||
pthread_join(thread, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func kt56233() {
|
||||
// Stress testing for race conditions.
|
||||
for _ in 0..<50000000 {
|
||||
_ = Kt56233.SimpleEnum.two.ordinal
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Execution of the test --------
|
||||
|
||||
class TestTests : SimpleTestProvider {
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
test("Kt56233", { try launchThreads(kt56233) })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@file:OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
|
||||
import kotlin.native.internal.GC
|
||||
import kotlin.native.internal.gc.GCInfo
|
||||
import kotlin.native.internal.isPermanent
|
||||
import kotlin.test.*
|
||||
|
||||
private var _counter = 0
|
||||
|
||||
object Permanent {
|
||||
var counter
|
||||
get() = _counter
|
||||
set(value) {
|
||||
_counter = value
|
||||
}
|
||||
}
|
||||
|
||||
fun assertIsPermanent() {
|
||||
assertTrue(Permanent.isPermanent())
|
||||
}
|
||||
|
||||
fun stableRefsCount(): Long {
|
||||
GC.collect()
|
||||
return GC.lastGCInfo!!.rootSet.stableReferences
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import PermanentObjects
|
||||
|
||||
func testPermanentObjects() throws {
|
||||
PermanentObjects.KnlibraryKt.assertIsPermanent()
|
||||
let stableRefsBefore = PermanentObjects.KnlibraryKt.stableRefsCount()
|
||||
autoreleasepool {
|
||||
for i in 0..<1000 {
|
||||
PermanentObjects.Permanent().counter += 1
|
||||
}
|
||||
}
|
||||
let stableRefsAfter = PermanentObjects.KnlibraryKt.stableRefsCount()
|
||||
try assertEquals(actual: PermanentObjects.Permanent().counter, expected: 1000)
|
||||
try assertEquals(actual: stableRefsAfter, expected: stableRefsBefore)
|
||||
}
|
||||
|
||||
// -------- Execution of the test --------
|
||||
|
||||
class TestTests : SimpleTestProvider {
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
test("permanentObjects", testPermanentObjects)
|
||||
}
|
||||
}
|
||||
@@ -22,3 +22,9 @@ extern BOOL deallocLoadWeakDeallocated;
|
||||
@interface DeallocLoadWeak : NSObject
|
||||
-(void)checkWeak;
|
||||
@end
|
||||
|
||||
extern BOOL deallocRetainAndAccessDeallocated;
|
||||
|
||||
@interface DeallocRetainAndAccess : NSObject
|
||||
@property void (^onDealloc)(id);
|
||||
@end
|
||||
|
||||
@@ -116,3 +116,44 @@ fun testKT41811WithGlobal() {
|
||||
|
||||
assertTrue(deallocRetainReleaseDeallocated)
|
||||
}
|
||||
|
||||
var localDeallocRetainAndAccessDeallocated = false
|
||||
|
||||
@Test
|
||||
fun testKT41811WithAccess() {
|
||||
// Legacy MM crashes with an assertion failure.
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
if (!isExperimentalMM())
|
||||
return
|
||||
|
||||
// Attempt to make the state predictable:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
deallocRetainAndAccessDeallocated = false
|
||||
localDeallocRetainAndAccessDeallocated = false
|
||||
|
||||
createGarbageDeallocRetainAndAccess()
|
||||
|
||||
// Runs [DeallocRetainAndAccess dealloc]:
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertTrue(deallocRetainAndAccessDeallocated)
|
||||
assertTrue(localDeallocRetainAndAccessDeallocated)
|
||||
|
||||
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
|
||||
private fun createGarbageDeallocRetainAndAccess() {
|
||||
autoreleasepool {
|
||||
object : DeallocRetainAndAccess() {
|
||||
init {
|
||||
onDealloc = {
|
||||
assertNull(it)
|
||||
assertFalse(localDeallocRetainAndAccessDeallocated)
|
||||
localDeallocRetainAndAccessDeallocated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,3 +37,20 @@ BOOL deallocLoadWeakDeallocated = NO;
|
||||
deallocLoadWeakDeallocated = YES;
|
||||
}
|
||||
@end
|
||||
|
||||
id retainObject2 = nil;
|
||||
BOOL deallocRetainAndAccessDeallocated = NO;
|
||||
|
||||
@implementation DeallocRetainAndAccess
|
||||
|
||||
- (void)dealloc {
|
||||
retainObject2 = self;
|
||||
assert(_onDealloc != nil);
|
||||
_onDealloc(retainObject2);
|
||||
retainObject2 = nil;
|
||||
|
||||
assert(!deallocRetainAndAccessDeallocated);
|
||||
deallocRetainAndAccessDeallocated = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,3 +10,10 @@ open class DeallocRetainBase
|
||||
fun garbageCollect() = kotlin.native.internal.GC.collect()
|
||||
|
||||
fun createWeakReference(value: Any) = kotlin.native.ref.WeakReference(value)
|
||||
|
||||
fun assertNull(value: Any?) {
|
||||
kotlin.test.assertNull(value)
|
||||
}
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
fun isExperimentalMM() = kotlin.native.isExperimentalMM()
|
||||
|
||||
@@ -62,12 +62,48 @@ private class DeallocRetain : DeallocRetainBase {
|
||||
}
|
||||
}
|
||||
|
||||
private class DeallocRetainAndAccess : DeallocRetainBase {
|
||||
static var deallocated = false
|
||||
static var retainObject: DeallocRetainAndAccess? = nil
|
||||
|
||||
deinit {
|
||||
DeallocRetainAndAccess.retainObject = self
|
||||
DeallocRetainKt.assertNull(value: DeallocRetainAndAccess.retainObject)
|
||||
DeallocRetainAndAccess.retainObject = nil
|
||||
|
||||
try! assertFalse(DeallocRetainAndAccess.deallocated)
|
||||
DeallocRetainAndAccess.deallocated = true
|
||||
}
|
||||
}
|
||||
|
||||
private func test2() throws {
|
||||
// Attempt to make the state predictable:
|
||||
DeallocRetainKt.garbageCollect()
|
||||
|
||||
DeallocRetainAndAccess.deallocated = false
|
||||
|
||||
autoreleasepool {
|
||||
DeallocRetainAndAccess()
|
||||
}
|
||||
|
||||
// Runs DeallocRetainAndAccess.deinit:
|
||||
DeallocRetainKt.garbageCollect()
|
||||
|
||||
try assertTrue(DeallocRetain.deallocated)
|
||||
|
||||
// Might crash due to double-dispose if the dealloc applied addRef/releaseRef to reclaimed Kotlin object:
|
||||
DeallocRetainKt.garbageCollect()
|
||||
}
|
||||
|
||||
class DeallocRetainTests : SimpleTestProvider {
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
#if !NOOP_GC
|
||||
test("Test1", test1)
|
||||
if (DeallocRetainKt.isExperimentalMM()) {
|
||||
test("Test2", test2)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +481,8 @@ __attribute__((swift_name("DeallocRetainKt")))
|
||||
@interface KtDeallocRetainKt : KtBase
|
||||
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
||||
+ (KtKotlinWeakReference<id> *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
||||
+ (void)assertNullValue:(id _Nullable)value __attribute__((swift_name("assertNull(value:)")));
|
||||
+ (BOOL)isExperimentalMM __attribute__((swift_name("isExperimentalMM()")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
|
||||
@@ -481,6 +481,8 @@ __attribute__((swift_name("DeallocRetainKt")))
|
||||
@interface KtDeallocRetainKt : KtBase
|
||||
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
||||
+ (KtKotlinWeakReference<id> *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
||||
+ (void)assertNullValue:(id _Nullable)value __attribute__((swift_name("assertNull(value:)")));
|
||||
+ (BOOL)isExperimentalMM __attribute__((swift_name("isExperimentalMM()")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
|
||||
@@ -481,6 +481,8 @@ __attribute__((swift_name("DeallocRetainKt")))
|
||||
@interface KtDeallocRetainKt : KtBase
|
||||
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
||||
+ (KtKotlinWeakReference *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
||||
+ (void)assertNullValue:(id _Nullable)value __attribute__((swift_name("assertNull(value:)")));
|
||||
+ (BOOL)isExperimentalMM __attribute__((swift_name("isExperimentalMM()")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
|
||||
Reference in New Issue
Block a user