blob: 5eb7c29a60439e2cd42274a945a79a7ddd2b2d7c [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 <cinttypes>
#include <thread>
#include "Utils.hpp"
namespace kotlin {
// `std::condition_variable_any` implemented via spinning on atomics.
class ConditionVariableSpin : private Pinned {
public:
// Notify all threads waiting for `this.
// Exactly the same as `notify_all`. Providing this method to be fully compatible
// with `std::condition_variable_any`.
void notify_one() noexcept {
// Conditional variable does not protect the data: a mutex must be
// used to protect it, so we don't need synchronization.
epoch_.fetch_add(1, std::memory_order_relaxed);
}
// Notify all threads waiting for `this.
void notify_all() noexcept {
// Conditional variable does not protect the data: a mutex must be
// used to protect it, so we don't need synchronization.
epoch_.fetch_add(1, std::memory_order_relaxed);
}
// Wait until next call to `notify_*`.
template <typename Lock>
void wait(Lock& lock) {
auto currentEpoch = epoch_.load(std::memory_order_relaxed);
lock.unlock();
// Waiting for any change of the epoch.
while (epoch_.load(std::memory_order_relaxed) == currentEpoch) {
std::this_thread::yield();
}
lock.lock();
}
// Waits until `stopWaiting()` starts returning true. Any change
// that leads to `stopWaiting()` must be followed by `notify_*` for
// this `wait` to stop waiting.
template <typename Lock, typename Predicate>
void wait(Lock& lock, Predicate stopWaiting) {
while (!stopWaiting()) {
wait(lock);
}
}
// TODO: Implement wait_until and wait_for using kotlin::steady_clock.
private:
std::atomic<size_t> epoch_ = 0;
};
} // namespace kotlin