From 43a7711fcc8ab9d2707bb94aa1ce2a615914bb9b Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Wed, 14 Jun 2023 14:06:14 +0200 Subject: [PATCH] [K/N] Add CallsCheckerIgnoreGuard --- .../src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp | 7 +++++-- .../runtime/src/main/cpp/CallsChecker.hpp | 20 ++++++++++++++++++ .../runtime/src/main/cpp/Logging.cpp | 5 ++++- .../runtime/src/mm/cpp/CallsChecker.cpp | 21 ++++++++++++------- .../runtime/src/mm/cpp/ThreadSuspension.cpp | 9 ++++++-- .../runtime/src/mm/cpp/ThreadSuspension.hpp | 2 +- 6 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 kotlin-native/runtime/src/main/cpp/CallsChecker.hpp diff --git a/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp b/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp index 8e28911df73..a02706f1b0a 100644 --- a/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp +++ b/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp @@ -8,6 +8,7 @@ #include #include +#include "CallsChecker.hpp" #include "CompilerConstants.hpp" #include "GlobalData.hpp" #include "GCImpl.hpp" @@ -90,7 +91,9 @@ void gc::ConcurrentMarkAndSweep::ThreadData::OnOOM(size_t size) noexcept { ScheduleAndWaitFullGC(); } -NO_EXTERNAL_CALLS_CHECK void gc::ConcurrentMarkAndSweep::ThreadData::OnSuspendForGC() noexcept { +void gc::ConcurrentMarkAndSweep::ThreadData::OnSuspendForGC() noexcept { + CallsCheckerIgnoreGuard guard; + std::unique_lock lock(markingMutex); if (!markingRequested.load()) return; AutoReset scopedAssignMarking(&marking_, true); @@ -259,7 +262,7 @@ void gc::ConcurrentMarkAndSweep::WaitForThreadsReadyToMark() noexcept { } } -NO_EXTERNAL_CALLS_CHECK void gc::ConcurrentMarkAndSweep::CollectRootSetAndStartMarking(GCHandle gcHandle) noexcept { +void gc::ConcurrentMarkAndSweep::CollectRootSetAndStartMarking(GCHandle gcHandle) noexcept { std::unique_lock lock(markingMutex); markingRequested = false; gc::collectRootSet( diff --git a/kotlin-native/runtime/src/main/cpp/CallsChecker.hpp b/kotlin-native/runtime/src/main/cpp/CallsChecker.hpp new file mode 100644 index 00000000000..59e4fb1d054 --- /dev/null +++ b/kotlin-native/runtime/src/main/cpp/CallsChecker.hpp @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2023 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. + */ + +#include "Common.h" +#include "Utils.hpp" + +namespace kotlin { + +// Ignore thread state checker for the scope. +// +// Can be used to ignore specific call sites that are known to be safe. +class CallsCheckerIgnoreGuard : private Pinned { +public: + ALWAYS_INLINE CallsCheckerIgnoreGuard() noexcept; + ALWAYS_INLINE ~CallsCheckerIgnoreGuard(); +}; + +} diff --git a/kotlin-native/runtime/src/main/cpp/Logging.cpp b/kotlin-native/runtime/src/main/cpp/Logging.cpp index 72f06abb97f..f8530863841 100644 --- a/kotlin-native/runtime/src/main/cpp/Logging.cpp +++ b/kotlin-native/runtime/src/main/cpp/Logging.cpp @@ -8,6 +8,7 @@ #include #include +#include "CallsChecker.hpp" #include "Format.h" #include "KAssert.h" #include "Porting.h" @@ -104,7 +105,7 @@ private: class StderrLogger : public logging::internal::Logger { public: - NO_EXTERNAL_CALLS_CHECK void Log(logging::Level level, std_support::span tags, std::string_view message) const noexcept override { + void Log(logging::Level level, std_support::span tags, std::string_view message) const noexcept override { konan::consoleErrorUtf8(message.data(), message.size()); } }; @@ -204,6 +205,8 @@ void logging::Log(Level level, std::initializer_list tags, const ch } void logging::VLog(Level level, std::initializer_list tags, const char* format, std::va_list args) noexcept { + CallsCheckerIgnoreGuard guard; + [[clang::no_destroy]] static DefaultLogContext ctx(compiler::runtimeLogs()); RuntimeAssert(tags.size() > 0, "Cannot Log without tags"); std_support::span tagsSpan(std::data(tags), std::size(tags)); diff --git a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp index de3f7a1218b..98086477d1b 100644 --- a/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp +++ b/kotlin-native/runtime/src/mm/cpp/CallsChecker.cpp @@ -3,6 +3,8 @@ * that can be found in the LICENSE file. */ +#include "CallsChecker.hpp" + #ifndef KONAN_NO_EXTERNAL_CALLS_CHECKER #include #include @@ -10,6 +12,7 @@ #include "KAssert.h" #include "Memory.h" #include "Porting.h" +#include "StackTrace.hpp" #include "ThreadData.hpp" #include "ThreadRegistry.hpp" #include "ExecFormat.h" @@ -321,6 +324,8 @@ private: constexpr int MSG_SEND_TO_NULL = -1; constexpr int CALLED_LLVM_BUILTIN = -2; +thread_local size_t ignoreGuardsCount = 0; + } /** @@ -335,15 +340,10 @@ constexpr int CALLED_LLVM_BUILTIN = -2; */ extern "C" RUNTIME_NOTHROW RUNTIME_NODEBUG void Kotlin_mm_checkStateAtExternalFunctionCall(const char* caller, const char *callee, const void *calleePtr) noexcept { if (reinterpret_cast(calleePtr) == MSG_SEND_TO_NULL) return; // objc_sendMsg called on nil, it does nothing, so it's ok + if (ignoreGuardsCount != 0) return; if (konan::isOnThreadExitNotSetOrAlreadyStarted()) return; - static thread_local bool recursiveCallGuard = false; - if (recursiveCallGuard) return; if (!mm::ThreadRegistry::Instance().IsCurrentThreadRegistered()) return; - struct unlockGuard { - unlockGuard() { recursiveCallGuard = true; } - ~unlockGuard() { recursiveCallGuard = false; } - } guard; - + CallsCheckerIgnoreGuard recursiveGuard; auto actualState = GetThreadState(); if (actualState == ThreadState::kNative) { @@ -367,7 +367,14 @@ extern "C" RUNTIME_NOTHROW RUNTIME_NODEBUG void Kotlin_mm_checkStateAtExternalFu return; } + PrintStackTraceStderr(); RuntimeFail("Expected kNative thread state at call of function %s by function %s", callee, caller); } +CallsCheckerIgnoreGuard::CallsCheckerIgnoreGuard() noexcept { ++ignoreGuardsCount; } +CallsCheckerIgnoreGuard::~CallsCheckerIgnoreGuard() { --ignoreGuardsCount; } + +#else +kotlin::CallsCheckerIgnoreGuard::CallsCheckerIgnoreGuard() noexcept {} +kotlin::CallsCheckerIgnoreGuard::~CallsCheckerIgnoreGuard() {} #endif // KONAN_NO_EXTERNAL_CALLS_CHECKER diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.cpp index 5d9cffefc8a..bba307da4a2 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.cpp @@ -10,6 +10,7 @@ #include #include +#include "CallsChecker.hpp" #include "Logging.hpp" #include "StackTrace.hpp" @@ -50,7 +51,9 @@ THREAD_LOCAL_VARIABLE bool gSuspensionRequestedByCurrentThread = false; std::atomic kotlin::mm::internal::gSuspensionRequested = false; -NO_EXTERNAL_CALLS_CHECK void kotlin::mm::ThreadSuspensionData::suspendIfRequestedSlowPath() noexcept { +void kotlin::mm::ThreadSuspensionData::suspendIfRequestedSlowPath() noexcept { + CallsCheckerIgnoreGuard guard; + if (IsThreadSuspensionRequested()) { threadData_.gc().OnSuspendForGC(); std::unique_lock lock(gSuspensionMutex); @@ -65,7 +68,9 @@ NO_EXTERNAL_CALLS_CHECK void kotlin::mm::ThreadSuspensionData::suspendIfRequeste } } -NO_EXTERNAL_CALLS_CHECK bool kotlin::mm::RequestThreadsSuspension() noexcept { +bool kotlin::mm::RequestThreadsSuspension() noexcept { + CallsCheckerIgnoreGuard guard; + RuntimeAssert(gSuspensionRequestedByCurrentThread == false, "Current thread already suspended threads."); { std::unique_lock lock(gSuspensionMutex); diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.hpp b/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.hpp index fe2c8af93d0..c40036c61ae 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadSuspension.hpp @@ -44,7 +44,7 @@ public: bool suspended() noexcept { return suspended_; } - NO_EXTERNAL_CALLS_CHECK void suspendIfRequested() noexcept { + void suspendIfRequested() noexcept { if (IsThreadSuspensionRequested()) { suspendIfRequestedSlowPath(); }