xref: /llvm-project/lldb/unittests/Host/AlarmTest.cpp (revision 7c20bdf373d6cd7f35dee5c71cf94f0eb1be3200)
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(std::chrono::system_clock::now() +
46                                     ALARM_TIMEOUT);
47 
48     alarm.Create([&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(ALARM_TIMEOUT / 5);
54   }
55 
56   // Leave plenty of time for all the alarms to fire.
57   std::this_thread::sleep_for(TEST_TIMEOUT);
58 
59   // Acquire the lock to check the callbacks.
60   std::lock_guard<std::mutex> guard(m);
61 
62   // Make sure all the alarms fired around the expected time.
63   for (size_t i = 0; i < 5; ++i)
64     EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
65 }
66 
67 TEST(AlarmTest, Exit) {
68   std::mutex m;
69 
70   std::vector<Alarm::Handle> handles;
71   std::vector<bool> callbacks;
72 
73   {
74     Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
75 
76     // Create 5 alarms.
77     for (size_t i = 0; i < 5; ++i) {
78       callbacks.emplace_back(false);
79 
80       handles.push_back(alarm.Create([&callbacks, &m, i]() {
81         std::lock_guard<std::mutex> guard(m);
82         callbacks[i] = true;
83       }));
84     }
85 
86     // Let the alarm go out of scope before any alarm had a chance to fire.
87   }
88 
89   // Acquire the lock to check the callbacks.
90   std::lock_guard<std::mutex> guard(m);
91 
92   // Make sure none of the alarms fired.
93   for (bool callback : callbacks)
94     EXPECT_TRUE(callback);
95 }
96 
97 TEST(AlarmTest, Cancel) {
98   std::mutex m;
99 
100   std::vector<Alarm::Handle> handles;
101   std::vector<bool> callbacks;
102 
103   Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
104 
105   // Create 5 alarms.
106   for (size_t i = 0; i < 5; ++i) {
107     callbacks.emplace_back(false);
108 
109     handles.push_back(alarm.Create([&callbacks, &m, i]() {
110       std::lock_guard<std::mutex> guard(m);
111       callbacks[i] = true;
112     }));
113   }
114 
115   // Make sure we can cancel the first 4 alarms.
116   for (size_t i = 0; i < 4; ++i)
117     EXPECT_TRUE(alarm.Cancel(handles[i]));
118 
119   // Leave plenty of time for all the alarms to fire.
120   std::this_thread::sleep_for(TEST_TIMEOUT);
121 
122   // Acquire the lock to check the callbacks.
123   std::lock_guard<std::mutex> guard(m);
124 
125   // Make sure none of the first 4 alarms fired.
126   for (size_t i = 0; i < 4; ++i)
127     EXPECT_FALSE(callbacks[i]);
128 
129   // Make sure the fifth alarm still fired.
130   EXPECT_TRUE(callbacks[4]);
131 }
132 
133 TEST(AlarmTest, Restart) {
134   std::mutex m;
135 
136   std::vector<Alarm::Handle> handles;
137   std::vector<Alarm::TimePoint> callbacks_actual;
138   std::vector<Alarm::TimePoint> callbacks_expected;
139 
140   Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
141 
142   // Create 5 alarms some time apart.
143   for (size_t i = 0; i < 5; ++i) {
144     callbacks_actual.emplace_back();
145     callbacks_expected.emplace_back(std::chrono::system_clock::now() +
146                                     ALARM_TIMEOUT);
147 
148     handles.push_back(alarm.Create([&callbacks_actual, &m, i]() {
149       std::lock_guard<std::mutex> guard(m);
150       callbacks_actual[i] = std::chrono::system_clock::now();
151     }));
152 
153     std::this_thread::sleep_for(ALARM_TIMEOUT / 5);
154   }
155 
156   // Update the last 2 alarms.
157   for (size_t i = 3; i < 5; ++i) {
158     std::lock_guard<std::mutex> guard(m);
159     callbacks_expected[i] = std::chrono::system_clock::now() + ALARM_TIMEOUT;
160     EXPECT_TRUE(alarm.Restart(handles[i]));
161   }
162 
163   // Leave plenty of time for all the alarms to fire.
164   std::this_thread::sleep_for(TEST_TIMEOUT);
165 
166   // Acquire the lock to check the callbacks.
167   std::lock_guard<std::mutex> guard(m);
168 
169   // Make sure all the alarms around the expected time.
170   for (size_t i = 0; i < 5; ++i)
171     EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
172 }
173