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 | |
15 | using namespace lldb_private; |
16 | using 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) |
21 | static constexpr auto TEST_TIMEOUT = 10000ms; |
22 | #else |
23 | static 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. |
28 | static constexpr auto ALARM_TIMEOUT = 500ms; |
29 | |
30 | // If there are any pending callbacks, make sure they run before the Alarm |
31 | // object is destroyed. |
32 | static constexpr bool RUN_CALLBACKS_ON_EXIT = true; |
33 | |
34 | TEST(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 | |
64 | TEST(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 | |
91 | TEST(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 | |
124 | TEST(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 | |