173 lines
4.9 KiB
C++
173 lines
4.9 KiB
C++
//===-- AlarmTest.cpp -----------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Host/Alarm.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
using namespace lldb_private;
|
|
using namespace std::chrono_literals;
|
|
|
|
// Increase the timeout tenfold when running under ASan as it can have about the
|
|
// same performance overhead.
|
|
#if __has_feature(address_sanitizer)
|
|
static constexpr auto TEST_TIMEOUT = 10000ms;
|
|
#else
|
|
static constexpr auto TEST_TIMEOUT = 1000ms;
|
|
#endif
|
|
|
|
// The time between scheduling a callback and it getting executed. This should
|
|
// NOT be increased under ASan.
|
|
static constexpr auto ALARM_TIMEOUT = 500ms;
|
|
|
|
// If there are any pending callbacks, make sure they run before the Alarm
|
|
// object is destroyed.
|
|
static constexpr bool RUN_CALLBACKS_ON_EXIT = true;
|
|
|
|
TEST(AlarmTest, Create) {
|
|
std::mutex m;
|
|
|
|
std::vector<Alarm::TimePoint> callbacks_actual;
|
|
std::vector<Alarm::TimePoint> callbacks_expected;
|
|
|
|
Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
|
|
|
|
// Create 5 alarms some time apart.
|
|
for (size_t i = 0; i < 5; ++i) {
|
|
callbacks_actual.emplace_back();
|
|
callbacks_expected.emplace_back(std::chrono::system_clock::now() +
|
|
ALARM_TIMEOUT);
|
|
|
|
alarm.Create([&callbacks_actual, &m, i]() {
|
|
std::lock_guard<std::mutex> guard(m);
|
|
callbacks_actual[i] = std::chrono::system_clock::now();
|
|
});
|
|
|
|
std::this_thread::sleep_for(ALARM_TIMEOUT / 5);
|
|
}
|
|
|
|
// Leave plenty of time for all the alarms to fire.
|
|
std::this_thread::sleep_for(TEST_TIMEOUT);
|
|
|
|
// Acquire the lock to check the callbacks.
|
|
std::lock_guard<std::mutex> guard(m);
|
|
|
|
// Make sure all the alarms fired around the expected time.
|
|
for (size_t i = 0; i < 5; ++i)
|
|
EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
|
|
}
|
|
|
|
TEST(AlarmTest, Exit) {
|
|
std::mutex m;
|
|
|
|
std::vector<Alarm::Handle> handles;
|
|
std::vector<bool> callbacks;
|
|
|
|
{
|
|
Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
|
|
|
|
// Create 5 alarms.
|
|
for (size_t i = 0; i < 5; ++i) {
|
|
callbacks.emplace_back(false);
|
|
|
|
handles.push_back(alarm.Create([&callbacks, &m, i]() {
|
|
std::lock_guard<std::mutex> guard(m);
|
|
callbacks[i] = true;
|
|
}));
|
|
}
|
|
|
|
// Let the alarm go out of scope before any alarm had a chance to fire.
|
|
}
|
|
|
|
// Acquire the lock to check the callbacks.
|
|
std::lock_guard<std::mutex> guard(m);
|
|
|
|
// Make sure none of the alarms fired.
|
|
for (bool callback : callbacks)
|
|
EXPECT_TRUE(callback);
|
|
}
|
|
|
|
TEST(AlarmTest, Cancel) {
|
|
std::mutex m;
|
|
|
|
std::vector<Alarm::Handle> handles;
|
|
std::vector<bool> callbacks;
|
|
|
|
Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
|
|
|
|
// Create 5 alarms.
|
|
for (size_t i = 0; i < 5; ++i) {
|
|
callbacks.emplace_back(false);
|
|
|
|
handles.push_back(alarm.Create([&callbacks, &m, i]() {
|
|
std::lock_guard<std::mutex> guard(m);
|
|
callbacks[i] = true;
|
|
}));
|
|
}
|
|
|
|
// Make sure we can cancel the first 4 alarms.
|
|
for (size_t i = 0; i < 4; ++i)
|
|
EXPECT_TRUE(alarm.Cancel(handles[i]));
|
|
|
|
// Leave plenty of time for all the alarms to fire.
|
|
std::this_thread::sleep_for(TEST_TIMEOUT);
|
|
|
|
// Acquire the lock to check the callbacks.
|
|
std::lock_guard<std::mutex> guard(m);
|
|
|
|
// Make sure none of the first 4 alarms fired.
|
|
for (size_t i = 0; i < 4; ++i)
|
|
EXPECT_FALSE(callbacks[i]);
|
|
|
|
// Make sure the fifth alarm still fired.
|
|
EXPECT_TRUE(callbacks[4]);
|
|
}
|
|
|
|
TEST(AlarmTest, Restart) {
|
|
std::mutex m;
|
|
|
|
std::vector<Alarm::Handle> handles;
|
|
std::vector<Alarm::TimePoint> callbacks_actual;
|
|
std::vector<Alarm::TimePoint> callbacks_expected;
|
|
|
|
Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
|
|
|
|
// Create 5 alarms some time apart.
|
|
for (size_t i = 0; i < 5; ++i) {
|
|
callbacks_actual.emplace_back();
|
|
callbacks_expected.emplace_back(std::chrono::system_clock::now() +
|
|
ALARM_TIMEOUT);
|
|
|
|
handles.push_back(alarm.Create([&callbacks_actual, &m, i]() {
|
|
std::lock_guard<std::mutex> guard(m);
|
|
callbacks_actual[i] = std::chrono::system_clock::now();
|
|
}));
|
|
|
|
std::this_thread::sleep_for(ALARM_TIMEOUT / 5);
|
|
}
|
|
|
|
// Update the last 2 alarms.
|
|
for (size_t i = 3; i < 5; ++i) {
|
|
std::lock_guard<std::mutex> guard(m);
|
|
callbacks_expected[i] = std::chrono::system_clock::now() + ALARM_TIMEOUT;
|
|
EXPECT_TRUE(alarm.Restart(handles[i]));
|
|
}
|
|
|
|
// Leave plenty of time for all the alarms to fire.
|
|
std::this_thread::sleep_for(TEST_TIMEOUT);
|
|
|
|
// Acquire the lock to check the callbacks.
|
|
std::lock_guard<std::mutex> guard(m);
|
|
|
|
// Make sure all the alarms around the expected time.
|
|
for (size_t i = 0; i < 5; ++i)
|
|
EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
|
|
}
|