1//===-- AlarmTest.cpp -----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Host/Alarm.h"
10#include "gtest/gtest.h"
11
12#include <chrono>
13#include <thread>
14
15using namespace lldb_private;
16using namespace std::chrono_literals;
17
18// Increase the timeout tenfold when running under ASan as it can have about the
19// same performance overhead.
20#if __has_feature(address_sanitizer)
21static constexpr auto TEST_TIMEOUT = 10000ms;
22#else
23static constexpr auto TEST_TIMEOUT = 1000ms;
24#endif
25
26// The time between scheduling a callback and it getting executed. This should
27// NOT be increased under ASan.
28static constexpr auto ALARM_TIMEOUT = 500ms;
29
30// If there are any pending callbacks, make sure they run before the Alarm
31// object is destroyed.
32static constexpr bool RUN_CALLBACKS_ON_EXIT = true;
33
34TEST(AlarmTest, Create) {
35 std::mutex m;
36
37 std::vector<Alarm::TimePoint> callbacks_actual;
38 std::vector<Alarm::TimePoint> callbacks_expected;
39
40 Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
41
42 // Create 5 alarms some time apart.
43 for (size_t i = 0; i < 5; ++i) {
44 callbacks_actual.emplace_back();
45 callbacks_expected.emplace_back(args: std::chrono::system_clock::now() +
46 ALARM_TIMEOUT);
47
48 alarm.Create(callback: [&callbacks_actual, &m, i]() {
49 std::lock_guard<std::mutex> guard(m);
50 callbacks_actual[i] = std::chrono::system_clock::now();
51 });
52
53 std::this_thread::sleep_for(rtime: ALARM_TIMEOUT / 5);
54 }
55
56 // Leave plenty of time for all the alarms to fire.
57 std::this_thread::sleep_for(rtime: TEST_TIMEOUT);
58
59 // Make sure all the alarms fired around the expected time.
60 for (size_t i = 0; i < 5; ++i)
61 EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
62}
63
64TEST(AlarmTest, Exit) {
65 std::mutex m;
66
67 std::vector<Alarm::Handle> handles;
68 std::vector<bool> callbacks;
69
70 {
71 Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
72
73 // Create 5 alarms.
74 for (size_t i = 0; i < 5; ++i) {
75 callbacks.emplace_back(args: false);
76
77 handles.push_back(x: alarm.Create(callback: [&callbacks, &m, i]() {
78 std::lock_guard<std::mutex> guard(m);
79 callbacks[i] = true;
80 }));
81 }
82
83 // Let the alarm go out of scope before any alarm had a chance to fire.
84 }
85
86 // Make sure none of the alarms fired.
87 for (bool callback : callbacks)
88 EXPECT_TRUE(callback);
89}
90
91TEST(AlarmTest, Cancel) {
92 std::mutex m;
93
94 std::vector<Alarm::Handle> handles;
95 std::vector<bool> callbacks;
96
97 Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
98
99 // Create 5 alarms.
100 for (size_t i = 0; i < 5; ++i) {
101 callbacks.emplace_back(args: false);
102
103 handles.push_back(x: alarm.Create(callback: [&callbacks, &m, i]() {
104 std::lock_guard<std::mutex> guard(m);
105 callbacks[i] = true;
106 }));
107 }
108
109 // Make sure we can cancel the first 4 alarms.
110 for (size_t i = 0; i < 4; ++i)
111 EXPECT_TRUE(alarm.Cancel(handles[i]));
112
113 // Leave plenty of time for all the alarms to fire.
114 std::this_thread::sleep_for(rtime: TEST_TIMEOUT);
115
116 // Make sure none of the first 4 alarms fired.
117 for (size_t i = 0; i < 4; ++i)
118 EXPECT_FALSE(callbacks[i]);
119
120 // Make sure the fifth alarm still fired.
121 EXPECT_TRUE(callbacks[4]);
122}
123
124TEST(AlarmTest, Restart) {
125 std::mutex m;
126
127 std::vector<Alarm::Handle> handles;
128 std::vector<Alarm::TimePoint> callbacks_actual;
129 std::vector<Alarm::TimePoint> callbacks_expected;
130
131 Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
132
133 // Create 5 alarms some time apart.
134 for (size_t i = 0; i < 5; ++i) {
135 callbacks_actual.emplace_back();
136 callbacks_expected.emplace_back(args: std::chrono::system_clock::now() +
137 ALARM_TIMEOUT);
138
139 handles.push_back(x: alarm.Create(callback: [&callbacks_actual, &m, i]() {
140 std::lock_guard<std::mutex> guard(m);
141 callbacks_actual[i] = std::chrono::system_clock::now();
142 }));
143
144 std::this_thread::sleep_for(rtime: ALARM_TIMEOUT / 5);
145 }
146
147 // Update the last 2 alarms.
148 for (size_t i = 3; i < 5; ++i) {
149 callbacks_expected[i] = std::chrono::system_clock::now() + ALARM_TIMEOUT;
150 EXPECT_TRUE(alarm.Restart(handles[i]));
151 }
152
153 // Leave plenty of time for all the alarms to fire.
154 std::this_thread::sleep_for(rtime: TEST_TIMEOUT);
155
156 // Make sure all the alarms around the expected time.
157 for (size_t i = 0; i < 5; ++i)
158 EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
159}
160

source code of lldb/unittests/Host/AlarmTest.cpp