/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #include "platform/runtime.hpp" #include "os/os.hpp" #include "thread/thread.hpp" #include "device/device.hpp" #include "utils/flags.hpp" #include "utils/options.hpp" #include "platform/context.hpp" #include "platform/agent.hpp" #include "platform/interop_gl.hpp" #ifdef _WIN32 #include #include #include "CL/cl_d3d10.h" #endif //_WIN32 #if defined(_MSC_VER) // both Win32 and Win64 #include #endif #include #include #include namespace amd { volatile bool Runtime::initialized_ = false; bool Runtime::LibraryDetached = false; volatile int Runtime::pid_ = 0; bool Runtime::init() { if (initialized_) { return true; } // Enter a very basic critical region. We want to prevent 2 threads // from concurrently executing the init() routines. We can't use a // Monitor since the system is not yet initialized. static std::atomic_flag lock = ATOMIC_FLAG_INIT; struct CriticalRegion { std::atomic_flag& lock_; CriticalRegion(std::atomic_flag& lock) : lock_(lock) { while (lock.test_and_set(std::memory_order_acquire)) { Os::yield(); } } ~CriticalRegion() { lock_.clear(std::memory_order_release); } } region(lock); if (initialized_) { return true; } if (!Flag::init() || !option::init() || !Device::init() // Agent initializes last || (!amd::IS_HIP && !Agent::init())) { ClPrint(LOG_ERROR, LOG_INIT, "Runtime initialization failed"); return false; } ClPrint(LOG_INFO, LOG_MISC, "ROCclr version: %s", ROCCLR_VERSION_GITHASH); initialized_ = true; pid_ = amd::Os::getProcessId(); return true; } void Runtime::tearDown() { if (!initialized_) { return; } Agent::tearDown(); Device::tearDown(); option::teardown(); Os::resetPreferredNumaNode(); Flag::tearDown(); if (outFile != stderr && outFile != nullptr) { fclose(outFile); } Command::ReleaseSysmemPool(); initialized_ = false; } // ~RuntimeTearDown() will reference listenerLock. // listenerLock will be constructed earlier and destructed later than // runtime_tear_down. amd::Monitor listenerLock{}; std::vector RuntimeTearDown::external_{}; std::vector> RuntimeTearDown::tear_down_funcs_{}; class RuntimeTearDown runtime_tear_down{}; // ================================================================================================= RuntimeTearDown::~RuntimeTearDown() { // Flush and stop async logging. Note: Windows will destroy other threads by now #ifdef _WIN32 FlushAsyncLogsInCurrentThread(); #else FlushAsyncLogs(); #endif if (IsAsyncLoggingEnabled()) { EnableAsyncLogging(false); } ClPrint(amd::LOG_INFO, amd::LOG_INIT, "Begin runtime teardown"); #if !defined(_WIN32) && !defined(BUILD_STATIC_LIBS) // Only perform destruction if process matches the initialization, // to avoid a call with the child process after fork(). if (amd::IS_HIP && amd::Os::getProcessId() == Runtime::pid()) { // Execute teardown funcs in reverse order of registration. for (auto it = tear_down_funcs_.rbegin(); it != tear_down_funcs_.rend(); ++it) { ClPrint(amd::LOG_DEBUG, amd::LOG_INIT, "~RuntimeTearDown invoke callback: %s", it->first.c_str()); it->second(); } for (auto it : external_) { ClPrint(amd::LOG_DEBUG, amd::LOG_INIT, "~RuntimeTearDown release external object: %p", it); it->release(); } Runtime::tearDown(); } #endif } // ================================================================================================= void RuntimeTearDown::RegisterObject(ReferenceCountedObject* obj) { external_.push_back(obj); } // ================================================================================================= void RuntimeTearDown::RegisterTearDownCallback(const std::string& msg, TearDownCallback func) { tear_down_funcs_.emplace_back(msg, std::move(func)); } // ================================================================================================= uint ReferenceCountedObject::retain() { uint prev = referenceCount_.fetch_add(1, std::memory_order_relaxed); assert(prev != 0 && "An object with count==0 is invalid"); return prev + 1; } uint ReferenceCountedObject::release() { uint newCount = referenceCount_.fetch_sub(1, std::memory_order_acq_rel) - 1; if (newCount == 0) { if (terminate()) { // The destructor should be called with a count==1 for the last thread // releasing the reference. Since an atomic load before the decrement // would add more atomic operations, simply bump the count to 1 here // before destructing the object. referenceCount_.store(1, std::memory_order_relaxed); delete this; } } return newCount; } } // namespace amd