[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
|
@interface DeallocLoadWeak : NSObject
|
||||||
-(void)checkWeak;
|
-(void)checkWeak;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
extern BOOL deallocRetainAndAccessDeallocated;
|
||||||
|
|
||||||
|
@interface DeallocRetainAndAccess : NSObject
|
||||||
|
@property void (^onDealloc)(id);
|
||||||
|
@end
|
||||||
|
|||||||
@@ -116,3 +116,44 @@ fun testKT41811WithGlobal() {
|
|||||||
|
|
||||||
assertTrue(deallocRetainReleaseDeallocated)
|
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;
|
deallocLoadWeakDeallocated = YES;
|
||||||
}
|
}
|
||||||
@end
|
@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 garbageCollect() = kotlin.native.internal.GC.collect()
|
||||||
|
|
||||||
fun createWeakReference(value: Any) = kotlin.native.ref.WeakReference(value)
|
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 {
|
class DeallocRetainTests : SimpleTestProvider {
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
#if !NOOP_GC
|
#if !NOOP_GC
|
||||||
test("Test1", test1)
|
test("Test1", test1)
|
||||||
|
if (DeallocRetainKt.isExperimentalMM()) {
|
||||||
|
test("Test2", test2)
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -481,6 +481,8 @@ __attribute__((swift_name("DeallocRetainKt")))
|
|||||||
@interface KtDeallocRetainKt : KtBase
|
@interface KtDeallocRetainKt : KtBase
|
||||||
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
||||||
+ (KtKotlinWeakReference<id> *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
+ (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
|
@end
|
||||||
|
|
||||||
__attribute__((objc_subclassing_restricted))
|
__attribute__((objc_subclassing_restricted))
|
||||||
|
|||||||
@@ -481,6 +481,8 @@ __attribute__((swift_name("DeallocRetainKt")))
|
|||||||
@interface KtDeallocRetainKt : KtBase
|
@interface KtDeallocRetainKt : KtBase
|
||||||
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
||||||
+ (KtKotlinWeakReference<id> *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
+ (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
|
@end
|
||||||
|
|
||||||
__attribute__((objc_subclassing_restricted))
|
__attribute__((objc_subclassing_restricted))
|
||||||
|
|||||||
@@ -481,6 +481,8 @@ __attribute__((swift_name("DeallocRetainKt")))
|
|||||||
@interface KtDeallocRetainKt : KtBase
|
@interface KtDeallocRetainKt : KtBase
|
||||||
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
+ (void)garbageCollect __attribute__((swift_name("garbageCollect()")));
|
||||||
+ (KtKotlinWeakReference *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
+ (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
|
@end
|
||||||
|
|
||||||
__attribute__((objc_subclassing_restricted))
|
__attribute__((objc_subclassing_restricted))
|
||||||
|
|||||||
Reference in New Issue
Block a user