| /* |
| * 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. |
| */ |
| |
| #include "GlobalData.hpp" |
| |
| #include <condition_variable> |
| #include <mutex> |
| |
| #include "CompilerConstants.hpp" |
| #include "Porting.h" |
| |
| #if KONAN_WINDOWS |
| #include "concurrent/Mutex.hpp" |
| #endif |
| |
| using namespace kotlin; |
| |
| namespace { |
| |
| enum class InitState { |
| kUninitialized, |
| kInitializing, |
| kInitialized, |
| }; |
| |
| const char* initStateToString(InitState state) noexcept { |
| switch (state) { |
| case InitState::kUninitialized: return "uninitialized"; |
| case InitState::kInitializing: return "initializing"; |
| case InitState::kInitialized: return "initialized"; |
| } |
| } |
| |
| std::atomic<InitState> globalDataInitState = InitState::kUninitialized; |
| std::atomic<std::thread::id> globalDataInitializingThread{}; |
| ManuallyScoped<mm::GlobalData> globalDataInstance{}; |
| |
| #if KONAN_WINDOWS |
| // On winpthreads, there's a weird bug if this is a regular `std::mutex`: |
| // even though `constructGlobalDataInstance()` has already started (and so, |
| // has already successfully tried locking this mutex), `waitGlobalDataInitialized` |
| // may crash trying to lock it too. |
| SpinLock globalDataInitMutex; |
| std::condition_variable_any globalDataInitCV; |
| #else |
| std::mutex globalDataInitMutex; |
| std::condition_variable globalDataInitCV; |
| #endif |
| |
| void constructGlobalDataInstance() noexcept { |
| std::unique_lock guard{globalDataInitMutex}; |
| auto initialState = InitState::kUninitialized; |
| globalDataInitState.compare_exchange_strong(initialState, InitState::kInitializing, std::memory_order_acq_rel); |
| RuntimeAssert(initialState == InitState::kUninitialized, "Expected state %s, but was %s", initStateToString(InitState::kUninitialized), initStateToString(initialState)); |
| globalDataInitializingThread.store(std::this_thread::get_id(), std::memory_order_relaxed); |
| |
| globalDataInstance.construct(); |
| |
| auto initializingState = InitState::kInitializing; |
| globalDataInitState.compare_exchange_strong(initializingState, InitState::kInitialized, std::memory_order_acq_rel); |
| RuntimeAssert(initializingState == InitState::kInitializing, "Expected state %s, but was %s", initStateToString(InitState::kInitializing), initStateToString(initializingState)); |
| guard.unlock(); |
| globalDataInitCV.notify_all(); |
| } |
| |
| [[maybe_unused]] struct GlobalDataEagerInit { |
| GlobalDataEagerInit() noexcept { |
| if (!compiler::globalDataLazyInit()) { |
| constructGlobalDataInstance(); |
| } |
| } |
| } globalDataEagerInit; |
| |
| } |
| |
| // static |
| mm::GlobalData& mm::GlobalData::Instance() noexcept { |
| if (compiler::runtimeAssertsEnabled()) { |
| auto s = globalDataInitState.load(std::memory_order_relaxed); |
| auto initializingThread = globalDataInitializingThread.load(std::memory_order_relaxed); |
| RuntimeAssert(s == InitState::kInitialized || initializingThread == std::this_thread::get_id(), "Expected state %s, but was %s.", initStateToString(InitState::kInitialized), initStateToString(s)); |
| } |
| return *globalDataInstance; |
| } |
| |
| // static |
| void mm::GlobalData::init() noexcept { |
| if (compiler::globalDataLazyInit()) { |
| constructGlobalDataInstance(); |
| } |
| } |
| |
| void mm::waitGlobalDataInitialized() noexcept { |
| if (globalDataInitState.load(std::memory_order_acquire) == InitState::kInitialized) { |
| return; |
| } |
| if (compiler::runtimeAssertsEnabled()) { |
| auto initializingThread = globalDataInitializingThread.load(std::memory_order_relaxed); |
| RuntimeAssert(initializingThread != std::this_thread::get_id(), "A thread that initialized global data cannot be waiting for its initialization"); |
| } |
| std::unique_lock guard{globalDataInitMutex}; |
| globalDataInitCV.wait(guard, []() noexcept { |
| return globalDataInitState.load(std::memory_order_relaxed) == InitState::kInitialized; |
| }); |
| } |
| |
| mm::GlobalData::GlobalData() noexcept = default; |
| |
| // static |