[K/N] Implement Platform.availableProcessors

^KT-48179
This commit is contained in:
Pavel Kunyavskiy
2021-12-15 10:29:07 +03:00
committed by Space
parent 964cf937c3
commit 2ba0a7b40f
6 changed files with 136 additions and 6 deletions
@@ -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
@@ -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<IllegalStateException> { Platform.getAvailableProcessors() }
setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "-1", 1)
assertFailsWith<IllegalStateException> { Platform.getAvailableProcessors() }
setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "0", 1)
assertFailsWith<IllegalStateException> { Platform.getAvailableProcessors() }
// windows doesn't support empty env variables
if (Platform.osFamily != OsFamily.WINDOWS) {
setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "", 1)
assertFailsWith<IllegalStateException> { Platform.getAvailableProcessors() }
}
setenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS", "123aaaa", 1)
assertFailsWith<IllegalStateException> { Platform.getAvailableProcessors() }
unsetenv("KOTLIN_NATIVE_AVAILABLE_PROCESSORS")
assertEquals(Platform.getAvailableProcessors(), x)
}
@@ -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
@@ -26,6 +26,11 @@
#include "Runtime.h"
#include "RuntimePrivate.hpp"
#include "Worker.h"
#include "KString.h"
#ifndef KONAN_NO_THREADS
#include <thread>
#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<int>::max()) {
res = std::numeric_limits<int>::max();
}
return static_cast<KInt>(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;
}
@@ -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
@@ -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",