diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index fcaecd0d785..f2d6430e992 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -2396,6 +2396,11 @@ task hash_map0(type: KonanLocalTest) { source = "runtime/collections/hash_map0.kt" } +task hash_map1(type: KonanLocalTest) { + goldValue = "OK\n" + source = "runtime/collections/hash_map1.kt" +} + task hash_set0(type: KonanLocalTest) { goldValue = "OK\n" source = "runtime/collections/hash_set0.kt" diff --git a/kotlin-native/backend.native/tests/runtime/collections/hash_map0.kt b/kotlin-native/backend.native/tests/runtime/collections/hash_map0.kt index d147ecd9485..b0f85d55630 100644 --- a/kotlin-native/backend.native/tests/runtime/collections/hash_map0.kt +++ b/kotlin-native/backend.native/tests/runtime/collections/hash_map0.kt @@ -94,58 +94,6 @@ fun testBasic() { assertEquals(0, m.size) } -fun testRehashAndCompact() { - val m = HashMap() - for (repeat in 1..10) { - val n = when (repeat) { - 1 -> 1000 - 2 -> 10000 - 3 -> 10 - else -> 100000 - } - for (i in 1..n) { - assertFalse(m.containsKey(i.toString())) - assertEquals(null, m.put(i.toString(), "val$i")) - assertTrue(m.containsKey(i.toString())) - assertEquals(i, m.size) - } - for (i in 1..n) { - assertTrue(m.containsKey(i.toString())) - } - for (i in 1..n) { - assertEquals("val$i", m.remove(i.toString())) - assertFalse(m.containsKey(i.toString())) - assertEquals(n - i, m.size) - } - assertTrue(m.isEmpty()) - } -} - -fun testClear() { - val m = HashMap() - for (repeat in 1..10) { - val n = when (repeat) { - 1 -> 1000 - 2 -> 10000 - 3 -> 10 - else -> 100000 - } - for (i in 1..n) { - assertFalse(m.containsKey(i.toString())) - assertEquals(null, m.put(i.toString(), "val$i")) - assertTrue(m.containsKey(i.toString())) - assertEquals(i, m.size) - } - for (i in 1..n) { - assertTrue(m.containsKey(i.toString())) - } - m.clear() - assertEquals(0, m.size) - for (i in 1..n) { - assertFalse(m.containsKey(i.toString())) - } - } -} fun testEquals() { val expected = mapOf("a" to "1", "b" to "2", "c" to "3") val m = HashMap(expected) @@ -257,8 +205,6 @@ fun testEntriesIteratorSet() { @Test fun runTest() { testBasic() - testRehashAndCompact() - testClear() testEquals() testHashCode() testToString() @@ -272,4 +218,4 @@ fun testEntriesIteratorSet() { testEntriesIteratorSet() //testDegenerateKeys() println("OK") -} \ No newline at end of file +} diff --git a/kotlin-native/backend.native/tests/runtime/collections/hash_map1.kt b/kotlin-native/backend.native/tests/runtime/collections/hash_map1.kt new file mode 100644 index 00000000000..b9475ebed8d --- /dev/null +++ b/kotlin-native/backend.native/tests/runtime/collections/hash_map1.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2021 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.collections.hash_map1 + +import kotlin.native.MemoryModel +import kotlin.native.Platform +import kotlin.native.internal.GC +import kotlin.test.* + +fun assertTrue(cond: Boolean) { + if (!cond) + println("FAIL") +} + +fun assertFalse(cond: Boolean) { + if (cond) + println("FAIL") +} + +fun assertEquals(value1: Any?, value2: Any?) { + if (value1 != value2) + println("FAIL") +} + +fun assertNotEquals(value1: Any?, value2: Any?) { + if (value1 == value2) + println("FAIL") +} + +fun assertEquals(value1: Int, value2: Int) { + if (value1 != value2) + println("FAIL") +} + +fun testRehashAndCompact() { + val m = HashMap() + for (repeat in 1..10) { + val n = when (repeat) { + 1 -> 1000 + 2 -> 10000 + 3 -> 10 + else -> 100000 + } + for (i in 1..n) { + assertFalse(m.containsKey(i.toString())) + assertEquals(null, m.put(i.toString(), "val$i")) + assertTrue(m.containsKey(i.toString())) + assertEquals(i, m.size) + } + for (i in 1..n) { + assertTrue(m.containsKey(i.toString())) + } + for (i in 1..n) { + assertEquals("val$i", m.remove(i.toString())) + assertFalse(m.containsKey(i.toString())) + assertEquals(n - i, m.size) + } + assertTrue(m.isEmpty()) + } +} + +fun testClear() { + val m = HashMap() + for (repeat in 1..10) { + val n = when (repeat) { + 1 -> 1000 + 2 -> 10000 + 3 -> 10 + else -> 100000 + } + for (i in 1..n) { + assertFalse(m.containsKey(i.toString())) + assertEquals(null, m.put(i.toString(), "val$i")) + assertTrue(m.containsKey(i.toString())) + assertEquals(i, m.size) + } + for (i in 1..n) { + assertTrue(m.containsKey(i.toString())) + } + m.clear() + assertEquals(0, m.size) + for (i in 1..n) { + assertFalse(m.containsKey(i.toString())) + } + } +} + +@Test fun runTest() { + // TODO: Do not manually control this. + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + GC.threshold = 1000000 + GC.thresholdAllocations = 1000000 + } + testRehashAndCompact() + testClear() + println("OK") +} diff --git a/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp b/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp index 31aecaf7e71..30166c31b0d 100644 --- a/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp +++ b/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp @@ -71,6 +71,11 @@ public: owner_.deletionQueue_.splice(owner_.deletionQueue_.end(), deletionQueue_); } + void ClearForTests() noexcept { + queue_.clear(); + deletionQueue_.clear(); + } + private: MultiSourceQueue& owner_; // weak KStdList queue_; diff --git a/kotlin-native/runtime/src/mm/cpp/GlobalData.cpp b/kotlin-native/runtime/src/mm/cpp/GlobalData.cpp index 73347e58e6f..e8f47f427b5 100644 --- a/kotlin-native/runtime/src/mm/cpp/GlobalData.cpp +++ b/kotlin-native/runtime/src/mm/cpp/GlobalData.cpp @@ -8,7 +8,6 @@ using namespace kotlin; mm::GlobalData::GlobalData() = default; -mm::GlobalData::~GlobalData() = default; // static -mm::GlobalData mm::GlobalData::instance_; +mm::GlobalData mm::GlobalData::instance_ [[clang::no_destroy]]; diff --git a/kotlin-native/runtime/src/mm/cpp/GlobalData.hpp b/kotlin-native/runtime/src/mm/cpp/GlobalData.hpp index 6cc47c09c69..2b084e45cb8 100644 --- a/kotlin-native/runtime/src/mm/cpp/GlobalData.hpp +++ b/kotlin-native/runtime/src/mm/cpp/GlobalData.hpp @@ -29,8 +29,9 @@ public: private: GlobalData(); - ~GlobalData(); + ~GlobalData() = delete; + // This `GlobalData` is never destroyed. static GlobalData instance_; ThreadRegistry threadRegistry_; diff --git a/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp b/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp index a46c01debf3..f63e08f4bf9 100644 --- a/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp @@ -39,6 +39,7 @@ public: // Make sure to clean everything allocated by the tests. for (auto& threadData : threadDatas_) { threadData->objectFactoryThreadQueue().ClearForTests(); + threadData->globalsThreadQueue().ClearForTests(); } } diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp index ddab007b465..67e8ce2f928 100644 --- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp @@ -263,6 +263,48 @@ extern "C" void Kotlin_native_internal_GC_collectCyclic(ObjHeader*) { ThrowIllegalArgumentException(); } +extern "C" void Kotlin_native_internal_GC_setThreshold(ObjHeader*, int32_t value) { + if (value < 0) { + ThrowIllegalArgumentException(); + } + mm::GlobalData::Instance().gc().SetThreshold(static_cast(value)); +} + +extern "C" int32_t Kotlin_native_internal_GC_getThreshold(ObjHeader*) { + auto threshold = mm::GlobalData::Instance().gc().GetThreshold(); + auto maxValue = std::numeric_limits::max(); + if (threshold > static_cast(maxValue)) { + return maxValue; + } + return static_cast(maxValue); +} + +extern "C" void Kotlin_native_internal_GC_setCollectCyclesThreshold(ObjHeader*, int64_t value) { + // TODO: Remove when legacy MM is gone. + ThrowIllegalArgumentException(); +} + +extern "C" int64_t Kotlin_native_internal_GC_getCollectCyclesThreshold(ObjHeader*) { + // TODO: Remove when legacy MM is gone. + ThrowIllegalArgumentException(); +} + +extern "C" void Kotlin_native_internal_GC_setThresholdAllocations(ObjHeader*, int64_t value) { + if (value < 0) { + ThrowIllegalArgumentException(); + } + mm::GlobalData::Instance().gc().SetAllocationThresholdBytes(static_cast(value)); +} + +extern "C" int64_t Kotlin_native_internal_GC_getThresholdAllocations(ObjHeader*) { + auto threshold = mm::GlobalData::Instance().gc().GetAllocationThresholdBytes(); + auto maxValue = std::numeric_limits::max(); + if (threshold > static_cast(maxValue)) { + return maxValue; + } + return static_cast(maxValue); +} + extern "C" OBJ_GETTER(Kotlin_native_internal_GC_detectCycles, ObjHeader*) { // TODO: Remove when legacy MM is gone. RETURN_OBJ(nullptr); diff --git a/kotlin-native/runtime/src/mm/cpp/Stubs.cpp b/kotlin-native/runtime/src/mm/cpp/Stubs.cpp index 9bd72a6b2a3..056020841d5 100644 --- a/kotlin-native/runtime/src/mm/cpp/Stubs.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Stubs.cpp @@ -42,30 +42,6 @@ void Kotlin_native_internal_GC_start(ObjHeader*) { TODO(); } -void Kotlin_native_internal_GC_setThreshold(ObjHeader*, int32_t value) { - TODO(); -} - -int32_t Kotlin_native_internal_GC_getThreshold(ObjHeader*) { - TODO(); -} - -void Kotlin_native_internal_GC_setCollectCyclesThreshold(ObjHeader*, int64_t value) { - TODO(); -} - -int64_t Kotlin_native_internal_GC_getCollectCyclesThreshold(ObjHeader*) { - TODO(); -} - -void Kotlin_native_internal_GC_setThresholdAllocations(ObjHeader*, int64_t value) { - TODO(); -} - -int64_t Kotlin_native_internal_GC_getThresholdAllocations(ObjHeader*) { - TODO(); -} - void Kotlin_native_internal_GC_setTuneThreshold(ObjHeader*, int32_t value) { TODO(); } diff --git a/kotlin-native/runtime/src/mm/cpp/gc/NoOpGC.hpp b/kotlin-native/runtime/src/mm/cpp/gc/NoOpGC.hpp index aa7cce129a5..3adf33f7d4a 100644 --- a/kotlin-native/runtime/src/mm/cpp/gc/NoOpGC.hpp +++ b/kotlin-native/runtime/src/mm/cpp/gc/NoOpGC.hpp @@ -38,10 +38,18 @@ public: private: }; - NoOpGC() noexcept = default; + NoOpGC() noexcept {} ~NoOpGC() = default; + void SetThreshold(size_t value) noexcept { threshold_ = value; } + size_t GetThreshold() noexcept { return threshold_; } + + void SetAllocationThresholdBytes(size_t value) noexcept { allocationThresholdBytes_ = value; } + size_t GetAllocationThresholdBytes() noexcept { return allocationThresholdBytes_; } + private: + size_t threshold_ = 0; + size_t allocationThresholdBytes_ = 0; }; } // namespace mm