//===-- 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 #include 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 callbacks_actual; std::vector 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 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 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 handles; std::vector 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 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 guard(m); // Make sure none of the alarms fired. for (bool callback : callbacks) EXPECT_TRUE(callback); } TEST(AlarmTest, Cancel) { std::mutex m; std::vector handles; std::vector 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 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 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 handles; std::vector callbacks_actual; std::vector 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 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 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 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]); }