From a5abaae70f4b4b783e5319f306ad3638120d8e4a Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Wed, 18 Mar 2020 12:39:33 +0300 Subject: [PATCH 1/8] Add a test --- backend.native/tests/build.gradle | 5 ++ .../tests/runtime/basic/initializers6.kt | 75 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 backend.native/tests/runtime/basic/initializers6.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index ae42d5f30e5..c6a231429b3 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2675,6 +2675,11 @@ task initializers5(type: KonanLocalTest) { source = "runtime/basic/initializers5.kt" } +task initializers6(type: KonanLocalTest) { + disabled = project.testTarget == 'wasm32' // Needs workers. + source = "runtime/basic/initializers6.kt" +} + task expression_as_statement(type: KonanLocalTest) { expectedFail = (project.testTarget == 'wasm32') // uses exceptions. goldValue = "Ok\n" diff --git a/backend.native/tests/runtime/basic/initializers6.kt b/backend.native/tests/runtime/basic/initializers6.kt new file mode 100644 index 00000000000..4fc1af5c530 --- /dev/null +++ b/backend.native/tests/runtime/basic/initializers6.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package runtime.basic.initializers6 + +import kotlin.test.* + +import kotlin.native.concurrent.* + +val aWorkerId = 2 +val bWorkersCount = 3 + +val aWorkerUnlocker = AtomicInt(0) +val bWorkerUnlocker = AtomicInt(0) + +object A { + init { + // Must be called by aWorker only. + assertEquals(aWorkerId, Worker.current.id) + // Only allow b workers to run, when a worker has started initialization. + bWorkerUnlocker.increment() + // Only proceed with initialization, when all b workers have started executing. + while (aWorkerUnlocker.value < bWorkersCount) {} + // And now wait a bit, to increase probability of races. + Worker.current.park(1000 * 1000L) + } + val a = produceA() + val b = produceB() +} + +fun produceA(): String { + // Must've been called by aWorker only. + assertEquals(aWorkerId, Worker.current.id) + return "A" +} + +fun produceB(): String { + // Must've been called by aWorker only. + assertEquals(aWorkerId, Worker.current.id) + // Also check that it's ok to get A.a while initializing A.b. + return "B+${A.a}" +} + +@Test fun runTest() { + val aWorker = Worker.start() + // This test relies on aWorkerId value. + assertEquals(aWorkerId, aWorker.id) + val bWorkers = Array(bWorkersCount, { _ -> Worker.start() }) + + val aFuture = aWorker.execute(TransferMode.SAFE, {}, { + A.b + }) + val bFutures = Array(bWorkers.size, { + bWorkers[it].execute(TransferMode.SAFE, {}, { + // Wait until A has started to initialize. + while (bWorkerUnlocker.value < 1) {} + // Now allow A initialization to continue. + aWorkerUnlocker.increment() + // And this should not've tried to init A itself. + A.a + A.b + }) + }) + + for (future in bFutures) { + assertEquals("AB+A", future.result) + } + assertEquals("B+A", aFuture.result) + + for (worker in bWorkers) { + worker.requestTermination().result + } + aWorker.requestTermination().result +} From d28f122328aa30587abc08f8b6b6b2c12d088b00 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Mon, 23 Mar 2020 11:06:31 +0300 Subject: [PATCH 2/8] Add another test --- backend.native/tests/build.gradle | 4 +++ .../tests/runtime/basic/initializers7.kt | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 backend.native/tests/runtime/basic/initializers7.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index c6a231429b3..3fe2a4160a1 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2680,6 +2680,10 @@ task initializers6(type: KonanLocalTest) { source = "runtime/basic/initializers6.kt" } +task initializers7(type: KonanLocalTest) { + source = "runtime/basic/initializers7.kt" +} + task expression_as_statement(type: KonanLocalTest) { expectedFail = (project.testTarget == 'wasm32') // uses exceptions. goldValue = "Ok\n" diff --git a/backend.native/tests/runtime/basic/initializers7.kt b/backend.native/tests/runtime/basic/initializers7.kt new file mode 100644 index 00000000000..2e4971830ba --- /dev/null +++ b/backend.native/tests/runtime/basic/initializers7.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package runtime.basic.initializers7 + +import kotlin.test.* + +import kotlin.random.Random + +object A { + val a1 = Random.nextInt(100) + val a2 = Random.nextInt(100) +} + +object B { + val b1 = A.a2 + val b2 = C.c1 +} + +object C { + val c1 = Random.nextInt(100) + val c2 = A.a1 + val c3 = B.b1 + val c4 = B.b2 +} + +@Test fun runTest() { + assertEquals(A.a1, C.c2) + assertEquals(A.a2, C.c3) + assertEquals(C.c1, C.c4) +} From 39d678b629e559ee7051213a591258022a2b20b6 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Fri, 20 Mar 2020 13:43:22 +0300 Subject: [PATCH 3/8] Use a specialized storage for shared shadow --- .../backend/konan/llvm/CodeGenerator.kt | 9 +++--- .../backend/konan/llvm/LlvmDeclarations.kt | 9 ++---- runtime/src/main/cpp/Memory.cpp | 30 +++++++++++++------ runtime/src/main/cpp/Memory.h | 6 ++-- runtime/src/relaxed/cpp/MemoryImpl.cpp | 4 +-- runtime/src/strict/cpp/MemoryImpl.cpp | 4 +-- 6 files changed, 34 insertions(+), 28 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 152c95ddb7b..3dcf71366aa 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -921,7 +921,6 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } val singleton = context.llvmDeclarations.forSingleton(irClass) val instanceAddress = singleton.instanceStorage - val instanceShadowAddress = singleton.instanceShadowStorage if (storageKind == ObjectStorageKind.PERMANENT) { return loadSlot(instanceAddress.getAddress(this), false) @@ -938,13 +937,13 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, val typeInfo = codegen.typeInfoForAllocation(irClass) val defaultConstructor = irClass.constructors.single { it.valueParameters.size == 0 } val ctor = codegen.llvmFunction(defaultConstructor) - val (initFunction, args) = + val initFunction = if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) { - val shadowObjectPtr = instanceShadowAddress!!.getAddress(this) - context.llvm.initSharedInstanceFunction to listOf(objectPtr, shadowObjectPtr, typeInfo, ctor) + context.llvm.initSharedInstanceFunction } else { - context.llvm.initInstanceFunction to listOf(objectPtr, typeInfo, ctor) + context.llvm.initInstanceFunction } + val args = listOf(objectPtr, typeInfo, ctor) val newValue = call(initFunction, args, Lifetime.GLOBAL, exceptionHandler) val bbInitResult = currentBlock br(bbExit) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 60da5877af1..1893901b1df 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -67,7 +67,7 @@ internal class ClassLlvmDeclarations( val singletonDeclarations: SingletonLlvmDeclarations?, val objCDeclarations: KotlinObjCClassLlvmDeclarations?) -internal class SingletonLlvmDeclarations(val instanceStorage: AddressAccess, val instanceShadowStorage: AddressAccess?) +internal class SingletonLlvmDeclarations(val instanceStorage: AddressAccess) internal class KotlinObjCClassLlvmDeclarations( val classPointerGlobal: StaticData.Global, @@ -279,12 +279,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : val symbolName = "kobjref:" + qualifyInternalName(irClass) val instanceAddress = addKotlinGlobal(symbolName, getLLVMType(irClass.defaultType), threadLocal = threadLocal) - val instanceShadowAddress = if (threadLocal || storageKind == ObjectStorageKind.PERMANENT) null else { - val shadowSymbolName = "kshadowobjref:" + qualifyInternalName(irClass) - addKotlinGlobal(shadowSymbolName, getLLVMType(irClass.defaultType), threadLocal = true) - } - - return SingletonLlvmDeclarations(instanceAddress, instanceShadowAddress) + return SingletonLlvmDeclarations(instanceAddress) } private fun createKotlinObjCClassDeclarations(irClass: IrClass): KotlinObjCClassLlvmDeclarations { diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 86fc53b1ebd..a51ad342add 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -451,6 +451,8 @@ struct MemoryState { uint64_t allocSinceLastGcThreshold; #endif // USE_GC + KStdUnorderedMap initializingSingletons; + #if COLLECT_STATISTIC #define CONTAINER_ALLOC_STAT(state, size, container) state->statistic.incAlloc(size, container); #define CONTAINER_DESTROY_STAT(state, container) \ @@ -1999,7 +2001,7 @@ OBJ_GETTER(initInstance, template OBJ_GETTER(initSharedInstance, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { #if KONAN_NO_THREADS ObjHeader* value = *location; if (value != nullptr) { @@ -2025,25 +2027,32 @@ OBJ_GETTER(initSharedInstance, } #endif // KONAN_NO_EXCEPTIONS #else // KONAN_NO_THREADS - ObjHeader* value = *localLocation; - if (value != nullptr) RETURN_OBJ(value); + auto it = memoryState->initializingSingletons.find(location); + if (it != memoryState->initializingSingletons.end()) { + RETURN_OBJ(it->second); + } ObjHeader* initializing = reinterpret_cast(1); // Spin lock. + ObjHeader* value = nullptr; while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing); if (value != nullptr) { // OK'ish, inited by someone else. RETURN_OBJ(value); } ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT); - UpdateHeapRef(localLocation, object); + auto insertIt = memoryState->initializingSingletons.insert({location, object}); + RuntimeCheck(insertIt.second, "object cannot be assigned twice into initializingSingletons"); + addHeapRef(object); #if KONAN_NO_EXCEPTIONS ctor(object); if (Strict) FreezeSubgraph(object); UpdateHeapRef(location, object); synchronize(); + memoryState->initializingSingletons.erase(location); + releaseHeapRef(object); return object; #else // KONAN_NO_EXCEPTIONS try { @@ -2052,11 +2061,14 @@ OBJ_GETTER(initSharedInstance, FreezeSubgraph(object); UpdateHeapRef(location, object); synchronize(); + memoryState->initializingSingletons.erase(location); + releaseHeapRef(object); return object; } catch (...) { UpdateReturnRef(OBJ_RESULT, nullptr); zeroHeapRef(location); - zeroHeapRef(localLocation); + memoryState->initializingSingletons.erase(location); + releaseHeapRef(object); synchronize(); throw; } @@ -2803,12 +2815,12 @@ OBJ_GETTER(InitInstanceRelaxed, } OBJ_GETTER(InitSharedInstanceStrict, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(initSharedInstance, location, localLocation, typeInfo, ctor); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { + RETURN_RESULT_OF(initSharedInstance, location, typeInfo, ctor); } OBJ_GETTER(InitSharedInstanceRelaxed, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(initSharedInstance, location, localLocation, typeInfo, ctor); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { + RETURN_RESULT_OF(initSharedInstance, location, typeInfo, ctor); } void SetStackRefStrict(ObjHeader** location, const ObjHeader* object) { diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 8fdb5803d7b..9768b5a1840 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -461,11 +461,11 @@ OBJ_GETTER(InitInstance, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); OBJ_GETTER(InitSharedInstanceStrict, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); OBJ_GETTER(InitSharedInstanceRelaxed, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); OBJ_GETTER(InitSharedInstance, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)); // Weak reference operations. // Atomically clears counter object reference. diff --git a/runtime/src/relaxed/cpp/MemoryImpl.cpp b/runtime/src/relaxed/cpp/MemoryImpl.cpp index 0763899a16c..4cdbda5e3b9 100644 --- a/runtime/src/relaxed/cpp/MemoryImpl.cpp +++ b/runtime/src/relaxed/cpp/MemoryImpl.cpp @@ -25,8 +25,8 @@ OBJ_GETTER(InitInstance, } OBJ_GETTER(InitSharedInstance, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(InitSharedInstanceRelaxed, location, localLocation, typeInfo, ctor); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { + RETURN_RESULT_OF(InitSharedInstanceRelaxed, location, typeInfo, ctor); } void ReleaseHeapRef(const ObjHeader* object) { diff --git a/runtime/src/strict/cpp/MemoryImpl.cpp b/runtime/src/strict/cpp/MemoryImpl.cpp index 27f47b7e0c2..0a5a7afd0a8 100644 --- a/runtime/src/strict/cpp/MemoryImpl.cpp +++ b/runtime/src/strict/cpp/MemoryImpl.cpp @@ -25,8 +25,8 @@ OBJ_GETTER(InitInstance, } OBJ_GETTER(InitSharedInstance, - ObjHeader** location, ObjHeader** localLocation, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - RETURN_RESULT_OF(InitSharedInstanceStrict, location, localLocation, typeInfo, ctor); + ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { + RETURN_RESULT_OF(InitSharedInstanceStrict, location, typeInfo, ctor); } void ReleaseHeapRef(const ObjHeader* object) { From 783e0524bcadb97d47d60b60e5d31e646325e84f Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Mon, 23 Mar 2020 10:09:27 +0300 Subject: [PATCH 4/8] Switch to std::map --- runtime/src/main/cpp/Memory.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index a51ad342add..f19ef114efd 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -451,7 +451,9 @@ struct MemoryState { uint64_t allocSinceLastGcThreshold; #endif // USE_GC - KStdUnorderedMap initializingSingletons; + // This map is expected to be small, std::map consumes less memory than + // std::unordered_map and is just as efficient. + KStdOrderedMap initializingSingletons; #if COLLECT_STATISTIC #define CONTAINER_ALLOC_STAT(state, size, container) state->statistic.incAlloc(size, container); From b52658eb382a5706069b78fdbc4228654218751d Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Tue, 24 Mar 2020 18:44:30 +0300 Subject: [PATCH 5/8] Try using stack --- runtime/src/main/cpp/Memory.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index f19ef114efd..c0ca081afd1 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -451,9 +451,8 @@ struct MemoryState { uint64_t allocSinceLastGcThreshold; #endif // USE_GC - // This map is expected to be small, std::map consumes less memory than - // std::unordered_map and is just as efficient. - KStdOrderedMap initializingSingletons; + // A stack of initializing singletons. + KStdVector> initializingSingletons; #if COLLECT_STATISTIC #define CONTAINER_ALLOC_STAT(state, size, container) state->statistic.incAlloc(size, container); @@ -2029,9 +2028,11 @@ OBJ_GETTER(initSharedInstance, } #endif // KONAN_NO_EXCEPTIONS #else // KONAN_NO_THREADS - auto it = memoryState->initializingSingletons.find(location); - if (it != memoryState->initializingSingletons.end()) { - RETURN_OBJ(it->second); + // Search from the top of the stack. + for (auto it = memoryState->initializingSingletons.rbegin(); it != memoryState->initializingSingletons.rend(); ++it) { + if (it->first == location) { + RETURN_OBJ(it->second); + } } ObjHeader* initializing = reinterpret_cast(1); @@ -2044,8 +2045,7 @@ OBJ_GETTER(initSharedInstance, RETURN_OBJ(value); } ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT); - auto insertIt = memoryState->initializingSingletons.insert({location, object}); - RuntimeCheck(insertIt.second, "object cannot be assigned twice into initializingSingletons"); + memoryState->initializingSingletons.push_back(std::make_pair(location, object)); addHeapRef(object); #if KONAN_NO_EXCEPTIONS ctor(object); @@ -2053,7 +2053,7 @@ OBJ_GETTER(initSharedInstance, FreezeSubgraph(object); UpdateHeapRef(location, object); synchronize(); - memoryState->initializingSingletons.erase(location); + memoryState->initializingSingletons.pop_back(); releaseHeapRef(object); return object; #else // KONAN_NO_EXCEPTIONS @@ -2063,13 +2063,13 @@ OBJ_GETTER(initSharedInstance, FreezeSubgraph(object); UpdateHeapRef(location, object); synchronize(); - memoryState->initializingSingletons.erase(location); + memoryState->initializingSingletons.pop_back(); releaseHeapRef(object); return object; } catch (...) { UpdateReturnRef(OBJ_RESULT, nullptr); zeroHeapRef(location); - memoryState->initializingSingletons.erase(location); + memoryState->initializingSingletons.pop_back(); releaseHeapRef(object); synchronize(); throw; From 7295cc0f3979d3e30fdbdb6d56a79dd79459b334 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Thu, 26 Mar 2020 13:05:44 +0300 Subject: [PATCH 6/8] Remove unneeded addHeapRef --- runtime/src/main/cpp/Memory.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index c0ca081afd1..bb4285bdcc2 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -2046,7 +2046,6 @@ OBJ_GETTER(initSharedInstance, } ObjHeader* object = AllocInstance(typeInfo, OBJ_RESULT); memoryState->initializingSingletons.push_back(std::make_pair(location, object)); - addHeapRef(object); #if KONAN_NO_EXCEPTIONS ctor(object); if (Strict) @@ -2054,7 +2053,6 @@ OBJ_GETTER(initSharedInstance, UpdateHeapRef(location, object); synchronize(); memoryState->initializingSingletons.pop_back(); - releaseHeapRef(object); return object; #else // KONAN_NO_EXCEPTIONS try { @@ -2064,13 +2062,11 @@ OBJ_GETTER(initSharedInstance, UpdateHeapRef(location, object); synchronize(); memoryState->initializingSingletons.pop_back(); - releaseHeapRef(object); return object; } catch (...) { UpdateReturnRef(OBJ_RESULT, nullptr); zeroHeapRef(location); memoryState->initializingSingletons.pop_back(); - releaseHeapRef(object); synchronize(); throw; } From b419101f1f417cb4fb2265b1019c0762d5d710e8 Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Thu, 26 Mar 2020 13:21:56 +0300 Subject: [PATCH 7/8] Fix initializer6 test --- backend.native/tests/runtime/basic/initializers6.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/backend.native/tests/runtime/basic/initializers6.kt b/backend.native/tests/runtime/basic/initializers6.kt index 4fc1af5c530..7c08d654282 100644 --- a/backend.native/tests/runtime/basic/initializers6.kt +++ b/backend.native/tests/runtime/basic/initializers6.kt @@ -9,7 +9,7 @@ import kotlin.test.* import kotlin.native.concurrent.* -val aWorkerId = 2 +val aWorkerId = AtomicInt(0) val bWorkersCount = 3 val aWorkerUnlocker = AtomicInt(0) @@ -18,13 +18,13 @@ val bWorkerUnlocker = AtomicInt(0) object A { init { // Must be called by aWorker only. - assertEquals(aWorkerId, Worker.current.id) + assertEquals(aWorkerId.value, Worker.current.id) // Only allow b workers to run, when a worker has started initialization. bWorkerUnlocker.increment() // Only proceed with initialization, when all b workers have started executing. while (aWorkerUnlocker.value < bWorkersCount) {} // And now wait a bit, to increase probability of races. - Worker.current.park(1000 * 1000L) + Worker.current.park(100 * 1000L) } val a = produceA() val b = produceB() @@ -32,21 +32,20 @@ object A { fun produceA(): String { // Must've been called by aWorker only. - assertEquals(aWorkerId, Worker.current.id) + assertEquals(aWorkerId.value, Worker.current.id) return "A" } fun produceB(): String { // Must've been called by aWorker only. - assertEquals(aWorkerId, Worker.current.id) + assertEquals(aWorkerId.value, Worker.current.id) // Also check that it's ok to get A.a while initializing A.b. return "B+${A.a}" } @Test fun runTest() { val aWorker = Worker.start() - // This test relies on aWorkerId value. - assertEquals(aWorkerId, aWorker.id) + aWorkerId.value = aWorker.id val bWorkers = Array(bWorkersCount, { _ -> Worker.start() }) val aFuture = aWorker.execute(TransferMode.SAFE, {}, { From 4d639692940a21a3fd6d101b0c69b30ad9d1138b Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Thu, 26 Mar 2020 15:33:50 +0300 Subject: [PATCH 8/8] Do not use random in initializers7 --- .../tests/runtime/basic/initializers7.kt | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/backend.native/tests/runtime/basic/initializers7.kt b/backend.native/tests/runtime/basic/initializers7.kt index 2e4971830ba..7327675675f 100644 --- a/backend.native/tests/runtime/basic/initializers7.kt +++ b/backend.native/tests/runtime/basic/initializers7.kt @@ -7,25 +7,52 @@ package runtime.basic.initializers7 import kotlin.test.* -import kotlin.random.Random - object A { - val a1 = Random.nextInt(100) - val a2 = Random.nextInt(100) + init { + assertAUninitialized() + } + val a1 = 7 + val a2 = 12 +} + +// Check that A is initialized dynamically. +fun assertAUninitialized() { + assertEquals(0, A.a1) + assertEquals(0, A.a2) } object B { + init { + assertBUninitialized() + } val b1 = A.a2 val b2 = C.c1 } +// Check that B is initialized dynamically. +fun assertBUninitialized() { + assertEquals(0, B.b1) + assertEquals(0, B.b2) +} + object C { - val c1 = Random.nextInt(100) + init { + assertCUninitialized() + } + val c1 = 42 val c2 = A.a1 val c3 = B.b1 val c4 = B.b2 } +// Check that C is initialized dynamically. +fun assertCUninitialized() { + assertEquals(0, C.c1) + assertEquals(0, C.c2) + assertEquals(0, C.c3) + assertEquals(0, C.c4) +} + @Test fun runTest() { assertEquals(A.a1, C.c2) assertEquals(A.a2, C.c3)