diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index f2cb93535b0..f72d7bc4b16 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -4114,11 +4114,7 @@ createInterop("sysstat") { } createInterop("cstdlib") { - def f = File.createTempFile("cstdlib.empty", ".def") - it.packageName 'cstdlib' -// it.headers 'stdlib.h' - f.write("headers = stdlib.h") - it.defFile f + it.defFile 'interop/basics/cstdlib.def' } createInterop("cstdio") { @@ -4424,6 +4420,15 @@ standaloneTest("interop_objc_allocException") { UtilsKt.dependsOnPlatformLibs(it) } +interopTest("interop_available_processors") { + disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. + source = 'interop/basics/available_processors.kt' + interop = 'cstdlib' + outputChecker = { + s -> Integer.parseInt(s.trim()) == Runtime.getRuntime().availableProcessors() + } +} + interopTest("interop0") { disabled = (project.testTarget == 'wasm32') || // No interop for wasm yet. (project.testTarget == 'linux_mips32') || // st_uid of '/' is not equal to 0 when using qemu diff --git a/kotlin-native/backend.native/tests/interop/basics/available_processors.kt b/kotlin-native/backend.native/tests/interop/basics/available_processors.kt new file mode 100644 index 00000000000..a2b3be74bcf --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/basics/available_processors.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2021 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. + */ + +import kotlin.native.* +import kotlin.test.* +import cstdlib.* +import kotlinx.cinterop.* + + +@OptIn(kotlin.ExperimentalStdlibApi::class) +fun main() { + val x = Platform.getAvailableProcessors() + println(x) + assertTrue(x > 0) + setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "12345", 1) + assertEquals(Platform.getAvailableProcessors(), 12345) + setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", Long.MAX_VALUE.toString(), 1) + assertFailsWith { Platform.getAvailableProcessors() } + setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "-1", 1) + assertFailsWith { Platform.getAvailableProcessors() } + setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "0", 1) + assertFailsWith { Platform.getAvailableProcessors() } + // windows doesn't support empty env variables + if (Platform.osFamily != OsFamily.WINDOWS) { + setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "", 1) + assertFailsWith { Platform.getAvailableProcessors() } + } + setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "123aaaa", 1) + assertFailsWith { Platform.getAvailableProcessors() } + unsetenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS") + assertEquals(Platform.getAvailableProcessors(), x) +} diff --git a/kotlin-native/backend.native/tests/interop/basics/cstdlib.def b/kotlin-native/backend.native/tests/interop/basics/cstdlib.def new file mode 100644 index 00000000000..6bff822298e --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/basics/cstdlib.def @@ -0,0 +1,21 @@ +headers = stdlib.h +compilerOpts.mingw = -D ADD_WINDOWS_ENV_FUNCTIONS + +--- + +#ifdef ADD_WINDOWS_ENV_FUNCTIONS +static inline int setenv(const char *name, const char *value, int overwrite) +{ + int errcode = 0; + if (!overwrite) { + size_t envsize = 0; + errcode = getenv_s(&envsize, NULL, 0, name); + if(errcode || envsize) return errcode; + } + return _putenv_s(name, value); +} +static inline int unsetenv(const char *name) +{ + return _putenv_s(name, ""); +} +#endif diff --git a/kotlin-native/runtime/src/main/cpp/Runtime.cpp b/kotlin-native/runtime/src/main/cpp/Runtime.cpp index 09eacc1bc54..bd09085968a 100644 --- a/kotlin-native/runtime/src/main/cpp/Runtime.cpp +++ b/kotlin-native/runtime/src/main/cpp/Runtime.cpp @@ -26,6 +26,11 @@ #include "Runtime.h" #include "RuntimePrivate.hpp" #include "Worker.h" +#include "KString.h" + +#ifndef KONAN_NO_THREADS +#include +#endif using kotlin::internal::FILE_NOT_INITIALIZED; using kotlin::internal::FILE_BEING_INITIALIZED; @@ -365,6 +370,35 @@ KBoolean Konan_Platform_getMemoryLeakChecker() { return g_checkLeaks; } +KInt Konan_Platform_getAvailableProcessors() { +#ifdef KONAN_NO_THREADS + return 1; +#else + auto res = std::thread::hardware_concurrency(); + // C++ standard says that if this function can return 0 if value is not "well defined or not computable" + // In current libstdc++ implementation, seems it can happen only on unsupported targets. + // We consider such systems as single-threaded + if (res == 0) { + res = 1; + } + // Probably it can't happen, but let's not allow overflows + if (res > std::numeric_limits::max()) { + res = std::numeric_limits::max(); + } + return static_cast(res); +#endif +} + +OBJ_GETTER0(Konan_Platform_getAvailableProcessorsEnv) { + char* env = getenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS"); + if (env == nullptr) { + RETURN_OBJ(nullptr) + } + RETURN_RESULT_OF(CreateStringFromCString, env) +} + + + void Konan_Platform_setMemoryLeakChecker(KBoolean value) { g_checkLeaks = value; } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt index 8327bf8cfb6..2bbfb8f019a 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt @@ -102,6 +102,29 @@ public object Platform { public var isCleanersLeakCheckerActive: Boolean get() = Platform_getCleanersLeakChecker() set(value) = Platform_setCleanersLeakChecker(value) + + /** + * The number of logical processors available. + * + * Can be not equal to the number of processors in the system if some restrictions on processor usage were successfully detected. + * Some kinds of processor usage restrictions are not detected, for now, e.g., CPU quotas in containers. + * + * The value is computed on each usage. It can change if some OS scheduler API restricts the process during runtime. + * Also, value can differ on different threads if some thread-specific scheduler API was used. + * + * If one considers the value to be inaccurate and wants another one to be used, it can be overridden by + * `KOTLIN_NATIVE_AVAILABLE_PROCESSORS` environment variable. When the variable is set and contains a value that is not + * positive [Int], [IllegalStateException] will be thrown. + */ + @ExperimentalStdlibApi + public fun getAvailableProcessors() : Int { + val fromEnv = Platform_getAvailableProcessorsEnv() + if (fromEnv == null) { + return Platform_getAvailableProcessors() + } + return fromEnv.toIntOrNull()?.takeIf { it > 0 } ?: + throw IllegalStateException("Available processors has incorrect environment override: $fromEnv") + } } @GCUnsafeCall("Konan_Platform_canAccessUnaligned") @@ -134,6 +157,13 @@ private external fun Platform_getCleanersLeakChecker(): Boolean @GCUnsafeCall("Konan_Platform_setCleanersLeakChecker") private external fun Platform_setCleanersLeakChecker(value: Boolean): Unit +@GCUnsafeCall("Konan_Platform_getAvailableProcessorsEnv") +private external fun Platform_getAvailableProcessorsEnv(): String? + +@GCUnsafeCall("Konan_Platform_getAvailableProcessors") +private external fun Platform_getAvailableProcessors(): Int + + @TypedIntrinsic(IntrinsicType.IS_EXPERIMENTAL_MM) @ExperimentalStdlibApi external fun isExperimentalMM(): Boolean diff --git a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp index 52dbbf52965..ee22c77fcd0 100644 --- a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp +++ b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp @@ -52,8 +52,10 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "_ZSt28_Rb_tree_rebalance_for_erasePSt18_Rb_tree_node_baseRS_", // std::_Rb_tree_rebalance_for_erase(std::_Rb_tree_node_base*, std::_Rb_tree_node_base&) "_ZN9__gnu_cxx27__verbose_terminate_handlerEv", // __gnu_cxx::__verbose_terminate_handler() "_Znwm", // new - "_Znwy", + "_Znwy", // operator new(unsigned long long) "_ZdlPv", // delete + "_ZNSt3__16thread20hardware_concurrencyEv", // std::__1::thread::hardware_concurrency() + "_ZNSt6thread20hardware_concurrencyEv", // std::thread::hardware_concurrency() "__mingw_vsnprintf", "__cxa_allocate_exception", "__cxa_begin_catch", @@ -137,6 +139,10 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = { "vsnprintf", "bcmp", + "getenv", + "setenv", + "unsetenv", + "dispatch_once", "\x01_pthread_cond_init", "_pthread_cond_init",