blob: 45c33c01c3904ba3011b4e7e87c63247c2612933 [file]
/*
* 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.
*/
#pragma once
#include <atomic>
#include <utility>
#include "Utils.hpp"
#include "ThreadRegistry.hpp"
#include "CallsChecker.hpp"
#include <shared_mutex>
namespace kotlin::mm {
class ThreadData;
class SafePointActivator : private MoveOnly {
public:
SafePointActivator() noexcept;
~SafePointActivator();
SafePointActivator(SafePointActivator&& rhs) noexcept : active_(rhs.active_) { rhs.active_ = false; }
SafePointActivator& operator=(SafePointActivator&& rhs) noexcept {
SafePointActivator other(std::move(rhs));
swap(other);
return *this;
}
void swap(SafePointActivator& rhs) noexcept { std::swap(active_, rhs.active_); }
private:
bool active_;
};
void safePoint(std::memory_order fastPathOrder = std::memory_order_relaxed) noexcept;
void safePoint(ThreadData& threadData, std::memory_order fastPathOrder = std::memory_order_relaxed) noexcept;
/**
* A helper class template to implement custom safe point action activators.
* An implementation has to inherit from this template, providing itself as a CRTP template argument.
*
* It's guaranteed that the action executed through `doIfActive`
* will be fully completed before the activator destructor returns.
*/
template <typename Impl>
class ExtraSafePointActionActivator : private MoveOnly {
public:
template <typename Action>
static void doIfActive(Action&& action) {
CallsCheckerIgnoreGuard guard;
// Without this check and with many frequent-enough readers,
// a writer might never get a chance to obtain the lock.
if (!active_.load(std::memory_order_relaxed)) return;
std::shared_lock lock(mutex_);
if (active_.load(std::memory_order_relaxed)) {
action();
}
}
static bool isActive() noexcept {
return active_.load(std::memory_order_relaxed);
}
ExtraSafePointActionActivator() noexcept {
std::unique_lock lock(mutex_);
active_.store(true, std::memory_order_relaxed);
}
virtual ~ExtraSafePointActionActivator() noexcept = 0;
private:
[[clang::no_destroy]] inline static std::shared_mutex mutex_{};
inline static std::atomic<bool> active_ = false;
SafePointActivator safePointActivator_{};
};
template <typename Impl>
ExtraSafePointActionActivator<Impl>::~ExtraSafePointActionActivator() noexcept {
// First stop new incoming threads from acquiring the mutex.
active_.store(false, std::memory_order_relaxed);
// Then synchronize with those, already holding the mutex.
std::unique_lock lock(mutex_);
}
namespace test_support {
bool safePointsAreActive() noexcept;
void setSafePointAction(void (*action)(mm::ThreadData&)) noexcept;
} // namespace test_support
} // namespace kotlin::mm