[K/N] Add more tests on associated object handling ^KT-56233

This commit is contained in:
Alexander Shabalin
2023-03-31 14:20:20 +02:00
committed by Space Team
parent dbe14a0a90
commit 87da670319
13 changed files with 227 additions and 0 deletions
@@ -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))