[K/N] Add RepeatedTimer
This commit is contained in:
committed by
Space
parent
12d31adbc4
commit
3e8c1be5c1
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_REPEATED_TIMER_H
|
||||
#define RUNTIME_REPEATED_TIMER_H
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "KAssert.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
class RepeatedTimer : private Pinned {
|
||||
public:
|
||||
template <typename Rep, typename Period, typename F>
|
||||
RepeatedTimer(std::chrono::duration<Rep, Period> interval, F f) noexcept :
|
||||
thread_([this, interval, f]() noexcept { Run(interval, f); }) {}
|
||||
|
||||
~RepeatedTimer() {
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
run_ = false;
|
||||
}
|
||||
wait_.notify_all();
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Rep, typename Period, typename F>
|
||||
void Run(std::chrono::duration<Rep, Period> interval, F f) noexcept {
|
||||
while (true) {
|
||||
std::unique_lock lock(mutex_);
|
||||
if (wait_.wait_for(lock, interval, [this]() noexcept { return !run_; })) {
|
||||
RuntimeAssert(!run_, "Can only happen once run_ is set to false");
|
||||
return;
|
||||
}
|
||||
RuntimeAssert(run_, "Can only happen if we timed out on waiting and run_ is still true");
|
||||
auto newInterval = f();
|
||||
// The next waiting will use the new interval.
|
||||
interval = std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(newInterval);
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable wait_;
|
||||
bool run_ = true;
|
||||
std::thread thread_;
|
||||
};
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_REPEATED_TIMER_H
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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 "RepeatedTimer.hpp"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
TEST(RepeatedTimerTest, WillRun) {
|
||||
std::atomic<int> counter = 0;
|
||||
RepeatedTimer timer(std::chrono::milliseconds(10), [&counter]() {
|
||||
++counter;
|
||||
return std::chrono::milliseconds(10);
|
||||
});
|
||||
// The function is not executed immediately.
|
||||
EXPECT_THAT(counter.load(), 0);
|
||||
// And now wait until the counter increases at least twice
|
||||
while (counter < 2) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RepeatedTimerTest, WillStop) {
|
||||
std::atomic<int> counter = 0;
|
||||
{
|
||||
RepeatedTimer timer(std::chrono::minutes(1), [&counter]() {
|
||||
++counter;
|
||||
return std::chrono::minutes(10);
|
||||
});
|
||||
}
|
||||
// The function was never executed.
|
||||
EXPECT_THAT(counter.load(), 0);
|
||||
}
|
||||
|
||||
TEST(RepeatedTimerTest, AdjustInterval) {
|
||||
std::atomic<int> counter = 0;
|
||||
RepeatedTimer timer(std::chrono::milliseconds(1), [&counter]() {
|
||||
++counter;
|
||||
if (counter < 2) {
|
||||
return std::chrono::milliseconds(1);
|
||||
} else {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::minutes(10));
|
||||
}
|
||||
});
|
||||
// Wait until counter grows to 2, when the waiting time changes to 10 minutes.
|
||||
while (counter < 2) {
|
||||
}
|
||||
EXPECT_THAT(counter.load(), 2);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// After we've slept for 10ms, we still haven't executed the function another time.
|
||||
EXPECT_THAT(counter.load(), 2);
|
||||
}
|
||||
Reference in New Issue
Block a user