[K/N][Tests] Migrate dylib-interop-exe tests

^KT-61259
This commit is contained in:
Vladimir Sukharev
2024-02-15 13:57:30 +01:00
committed by Space Team
parent 83d851dd61
commit 2b2c685827
56 changed files with 278 additions and 397 deletions
@@ -0,0 +1,3 @@
language = Objective-C
headerFilter = **/kt56402.h
linkerOpts = -lkt56402
@@ -0,0 +1,21 @@
#include <inttypes.h>
#include <objc/NSObject.h>
@interface OnDestroyHook: NSObject
-(instancetype)init:(void(^)(uintptr_t))onDestroy;
-(uintptr_t)identity;
@end
@protocol OnDestroyHook
-(uintptr_t)identity;
@end
void startApp(void(^task)());
uint64_t currentThreadId();
BOOL isMainThread();
void spin();
OnDestroyHook* newGlobal(void(^onDestroy)(uintptr_t));
void clearGlobal();
id<OnDestroyHook> newOnDestroyHook(void(^onDestroy)(uintptr_t));
@@ -0,0 +1,193 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kt56402.*
import kotlin.concurrent.AtomicInt
import kotlin.native.concurrent.*
import kotlin.native.internal.test.testLauncherEntryPoint
import kotlin.system.exitProcess
import kotlin.test.*
import kotlinx.cinterop.*
class LockedSet<T> {
private val lock = AtomicInt(0)
private val impl = mutableSetOf<T>()
private inline fun <R> locked(f: () -> R): R {
while (!lock.compareAndSet(0, 1)) {}
try {
return f()
} finally {
lock.value = 0
}
}
fun add(id: T) = locked {
assertFalse(id in impl)
impl.add(id)
}
fun remove(id: T) = locked {
assertTrue(id in impl)
impl.remove(id)
}
operator fun contains(id: T) = locked {
id in impl
}
}
class OnDestroyHookSub(onDestroy: (ULong) -> Unit) : OnDestroyHook(onDestroy)
val aliveObjectIds = LockedSet<ULong>()
fun alloc(ctor: ((ULong) -> Unit) -> ULong): ULong = autoreleasepool {
val id = ctor {
aliveObjectIds.remove(it)
}
aliveObjectIds.add(id)
id
}
fun waitDestruction(id: ULong) {
assertTrue(isMainThread())
// Make sure the finalizers are not run on the main thread even for STMS.
withWorker {
execute(TransferMode.SAFE, {}) {
kotlin.native.internal.GC.collect()
}.result
}
while (true) {
spin()
if (!(id in aliveObjectIds)) {
break
}
}
}
@Test
fun testOnMainThread() {
assertTrue(isMainThread())
val id = alloc { onDestroy ->
OnDestroyHook {
assertTrue(isMainThread())
onDestroy(it)
}.identity()
}
waitDestruction(id)
}
@Test
fun testOnSecondaryThread() {
val id = withWorker {
execute(TransferMode.SAFE, {}) {
assertFalse(isMainThread())
alloc { onDestroy ->
OnDestroyHook {
assertFalse(isMainThread())
onDestroy(it)
}.identity()
}
}.result
}
waitDestruction(id)
}
@Test
fun testSubOnMainThread() {
assertTrue(isMainThread())
val id = alloc { onDestroy ->
OnDestroyHookSub {
assertTrue(isMainThread())
onDestroy(it)
}.identity()
}
waitDestruction(id)
}
@Test
fun testSubOnSecondaryThread() {
val id = withWorker {
execute(TransferMode.SAFE, {}) {
assertFalse(isMainThread())
alloc { onDestroy ->
OnDestroyHookSub {
assertFalse(isMainThread())
onDestroy(it)
}.identity()
}
}.result
}
waitDestruction(id)
}
@Test
fun testGlobalOnMainThread() {
assertTrue(isMainThread())
val id = alloc { onDestroy ->
val obj = newGlobal {
assertTrue(isMainThread())
onDestroy(it)
}!!
clearGlobal()
obj.identity()
}
waitDestruction(id)
}
@Test
fun testGlobalOnSecondaryThread() {
val id = withWorker {
execute(TransferMode.SAFE, {}) {
assertFalse(isMainThread())
alloc { onDestroy ->
val obj = newGlobal {
assertFalse(isMainThread())
onDestroy(it)
}!!
clearGlobal()
obj.identity()
}
}.result
}
waitDestruction(id)
}
@Test
fun testProtocolOnMainThread() {
assertTrue(isMainThread())
val id = alloc { onDestroy ->
newOnDestroyHook {
assertTrue(isMainThread())
onDestroy(it)
}!!.identity()
}
waitDestruction(id)
}
@Test
fun testProtocolOnSecondaryThread() {
val id = withWorker {
execute(TransferMode.SAFE, {}) {
assertFalse(isMainThread())
alloc { onDestroy ->
newOnDestroyHook {
assertFalse(isMainThread())
onDestroy(it)
}!!.identity()
}
}.result
}
waitDestruction(id)
}
fun main(args: Array<String>) = startApp {
val exitCode = testLauncherEntryPoint(args)
if (exitCode != 0) {
exitProcess(exitCode)
}
}
@@ -0,0 +1,101 @@
#include "kt56402.h"
#include <assert.h>
#include <dispatch/dispatch.h>
#include <pthread.h>
#import <AppKit/NSApplication.h>
#import <Foundation/NSRunLoop.h>
#import <Foundation/NSThread.h>
@implementation OnDestroyHook {
void (^onDestroy_)(uintptr_t);
}
- (uintptr_t)identity {
return (uintptr_t)self;
}
- (instancetype)init:(void (^)(uintptr_t))onDestroy {
if (self = [super init]) {
onDestroy_ = onDestroy;
}
return self;
}
- (void)dealloc {
onDestroy_([self identity]);
}
@end
@interface OnDestroyHookPrivate : NSObject<OnDestroyHook>
@end
@implementation OnDestroyHookPrivate {
void (^onDestroy_)(uintptr_t);
}
- (uintptr_t)identity {
return (uintptr_t)self;
}
- (instancetype)init:(void (^)(uintptr_t))onDestroy {
if (self = [super init]) {
onDestroy_ = onDestroy;
}
return self;
}
- (void)dealloc {
onDestroy_([self identity]);
}
@end
void startApp(void (^task)()) {
dispatch_async(dispatch_get_main_queue(), ^{
// At this point all other scheduled main queue tasks were already executed.
// Executing via performBlock to allow a recursive run loop in `spin()`.
[[NSRunLoop currentRunLoop] performBlock:^{
task();
[NSApp terminate:NULL];
}];
});
[[NSApplication sharedApplication] run];
}
uint64_t currentThreadId() {
uint64_t result;
int ret = pthread_threadid_np(NULL, &result);
assert(ret == 0);
return result;
}
BOOL isMainThread() {
return [NSThread isMainThread];
}
void spin() {
if ([NSRunLoop currentRunLoop] != [NSRunLoop mainRunLoop]) {
fprintf(stderr, "Must spin main run loop\n");
exit(1);
}
[[NSRunLoop currentRunLoop]
runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
OnDestroyHook* global = NULL;
OnDestroyHook* newGlobal(void(^onDestroy)(uintptr_t)) {
global = [[OnDestroyHook alloc] init:onDestroy];
return global;
}
void clearGlobal() {
global = NULL;
}
id<OnDestroyHook> newOnDestroyHook(void(^onDestroy)(uintptr_t)) {
return [[OnDestroyHookPrivate alloc] init:onDestroy];
}