xref: /aosp_15_r20/external/cronet/base/synchronization/waitable_event_watcher_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/synchronization/waitable_event_watcher.h"
6 
7 #include "base/functional/bind.h"
8 #include "base/functional/callback.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/run_loop.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "base/test/bind.h"
16 #include "base/test/task_environment.h"
17 #include "base/threading/platform_thread.h"
18 #include "build/build_config.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace base {
22 
23 namespace {
24 
25 // The main thread types on which each waitable event should be tested.
26 const test::TaskEnvironment::MainThreadType testing_main_threads[] = {
27     test::TaskEnvironment::MainThreadType::DEFAULT,
28     test::TaskEnvironment::MainThreadType::IO,
29 #if !BUILDFLAG(IS_IOS)  // iOS does not allow direct running of the UI loop.
30     test::TaskEnvironment::MainThreadType::UI,
31 #endif
32 };
33 
QuitWhenSignaled(base::OnceClosure quit_closure,WaitableEvent * event)34 void QuitWhenSignaled(base::OnceClosure quit_closure, WaitableEvent* event) {
35   std::move(quit_closure).Run();
36 }
37 
38 class DecrementCountContainer {
39  public:
DecrementCountContainer(int * counter)40   explicit DecrementCountContainer(int* counter) : counter_(counter) {}
OnWaitableEventSignaled(WaitableEvent * object)41   void OnWaitableEventSignaled(WaitableEvent* object) {
42     // NOTE: |object| may be already deleted.
43     --(*counter_);
44   }
45 
46  private:
47   raw_ptr<int> counter_;
48 };
49 
50 }  // namespace
51 
52 class WaitableEventWatcherTest
53     : public testing::TestWithParam<test::TaskEnvironment::MainThreadType> {};
54 
TEST_P(WaitableEventWatcherTest,BasicSignalManual)55 TEST_P(WaitableEventWatcherTest, BasicSignalManual) {
56   test::TaskEnvironment task_environment(GetParam());
57   base::RunLoop loop;
58   // A manual-reset event that is not yet signaled.
59   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
60                       WaitableEvent::InitialState::NOT_SIGNALED);
61 
62   WaitableEventWatcher watcher;
63   watcher.StartWatching(&event,
64                         BindOnce(&QuitWhenSignaled, loop.QuitWhenIdleClosure()),
65                         SequencedTaskRunner::GetCurrentDefault());
66 
67   event.Signal();
68 
69   loop.Run();
70 
71   EXPECT_TRUE(event.IsSignaled());
72 }
73 
TEST_P(WaitableEventWatcherTest,BasicSignalAutomatic)74 TEST_P(WaitableEventWatcherTest, BasicSignalAutomatic) {
75   test::TaskEnvironment task_environment(GetParam());
76 
77   WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
78                       WaitableEvent::InitialState::NOT_SIGNALED);
79 
80   WaitableEventWatcher watcher;
81   base::RunLoop loop;
82   watcher.StartWatching(&event,
83                         BindOnce(&QuitWhenSignaled, loop.QuitWhenIdleClosure()),
84                         SequencedTaskRunner::GetCurrentDefault());
85 
86   event.Signal();
87 
88   loop.Run();
89 
90   // The WaitableEventWatcher consumes the event signal.
91   EXPECT_FALSE(event.IsSignaled());
92 }
93 
TEST_P(WaitableEventWatcherTest,BasicCancel)94 TEST_P(WaitableEventWatcherTest, BasicCancel) {
95   test::TaskEnvironment task_environment(GetParam());
96 
97   // A manual-reset event that is not yet signaled.
98   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
99                       WaitableEvent::InitialState::NOT_SIGNALED);
100 
101   WaitableEventWatcher watcher;
102   watcher.StartWatching(&event, DoNothing(),
103                         SequencedTaskRunner::GetCurrentDefault());
104 
105   watcher.StopWatching();
106 }
107 
TEST_P(WaitableEventWatcherTest,CancelAfterSet)108 TEST_P(WaitableEventWatcherTest, CancelAfterSet) {
109   test::TaskEnvironment task_environment(GetParam());
110 
111   // A manual-reset event that is not yet signaled.
112   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
113                       WaitableEvent::InitialState::NOT_SIGNALED);
114 
115   WaitableEventWatcher watcher;
116 
117   int counter = 1;
118   DecrementCountContainer delegate(&counter);
119   WaitableEventWatcher::EventCallback callback = BindOnce(
120       &DecrementCountContainer::OnWaitableEventSignaled, Unretained(&delegate));
121   watcher.StartWatching(&event, std::move(callback),
122                         SequencedTaskRunner::GetCurrentDefault());
123 
124   event.Signal();
125 
126   // Let the background thread do its business
127   PlatformThread::Sleep(Milliseconds(30));
128 
129   watcher.StopWatching();
130 
131   RunLoop().RunUntilIdle();
132 
133   // Our delegate should not have fired.
134   EXPECT_EQ(1, counter);
135 }
136 
TEST_P(WaitableEventWatcherTest,OutlivesTaskEnvironment)137 TEST_P(WaitableEventWatcherTest, OutlivesTaskEnvironment) {
138   // Simulate a task environment that dies before an WaitableEventWatcher.  This
139   // ordinarily doesn't happen when people use the Thread class, but it can
140   // happen when people use the Singleton pattern or atexit.
141   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
142                       WaitableEvent::InitialState::NOT_SIGNALED);
143   {
144     std::unique_ptr<WaitableEventWatcher> watcher;
145     {
146       test::TaskEnvironment task_environment(GetParam());
147       watcher = std::make_unique<WaitableEventWatcher>();
148 
149       watcher->StartWatching(&event, DoNothing(),
150                              SequencedTaskRunner::GetCurrentDefault());
151     }
152   }
153 }
154 
TEST_P(WaitableEventWatcherTest,SignaledAtStartManual)155 TEST_P(WaitableEventWatcherTest, SignaledAtStartManual) {
156   test::TaskEnvironment task_environment(GetParam());
157   base::RunLoop loop;
158   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
159                       WaitableEvent::InitialState::SIGNALED);
160 
161   WaitableEventWatcher watcher;
162   watcher.StartWatching(&event,
163                         BindOnce(&QuitWhenSignaled, loop.QuitWhenIdleClosure()),
164                         SequencedTaskRunner::GetCurrentDefault());
165 
166   loop.Run();
167 
168   EXPECT_TRUE(event.IsSignaled());
169 }
170 
TEST_P(WaitableEventWatcherTest,SignaledAtStartAutomatic)171 TEST_P(WaitableEventWatcherTest, SignaledAtStartAutomatic) {
172   test::TaskEnvironment task_environment(GetParam());
173   base::RunLoop loop;
174   WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
175                       WaitableEvent::InitialState::SIGNALED);
176 
177   WaitableEventWatcher watcher;
178   watcher.StartWatching(&event,
179                         BindOnce(&QuitWhenSignaled, loop.QuitWhenIdleClosure()),
180                         SequencedTaskRunner::GetCurrentDefault());
181 
182   loop.Run();
183 
184   // The watcher consumes the event signal.
185   EXPECT_FALSE(event.IsSignaled());
186 }
187 
TEST_P(WaitableEventWatcherTest,StartWatchingInCallback)188 TEST_P(WaitableEventWatcherTest, StartWatchingInCallback) {
189   test::TaskEnvironment task_environment(GetParam());
190 
191   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
192                       WaitableEvent::InitialState::NOT_SIGNALED);
193 
194   WaitableEventWatcher watcher;
195   base::RunLoop loop;
196   watcher.StartWatching(&event, BindLambdaForTesting([&](WaitableEvent* event) {
197     // |event| is manual, so the second watcher will run
198     // immediately.
199     watcher.StartWatching(
200         event, BindOnce(&QuitWhenSignaled, loop.QuitWhenIdleClosure()),
201         SequencedTaskRunner::GetCurrentDefault());
202   }),
203                         SequencedTaskRunner::GetCurrentDefault());
204 
205   event.Signal();
206 
207   loop.Run();
208 }
209 
TEST_P(WaitableEventWatcherTest,MultipleWatchersManual)210 TEST_P(WaitableEventWatcherTest, MultipleWatchersManual) {
211   test::TaskEnvironment task_environment(GetParam());
212 
213   WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
214                       WaitableEvent::InitialState::NOT_SIGNALED);
215 
216   int watcher1_counter = 0;
217   int watcher2_counter = 0;
218 
219   int total_counter = 0;
220 
221   RunLoop run_loop;
222 
223   auto callback = [&run_loop, &total_counter](int* watcher_counter,
224                                               WaitableEvent*) {
225     ++(*watcher_counter);
226     if (++total_counter == 2) {
227       run_loop.Quit();
228     }
229   };
230 
231   WaitableEventWatcher watcher1;
232   watcher1.StartWatching(
233       &event,
234       BindOnce(BindLambdaForTesting(callback), Unretained(&watcher1_counter)),
235       SequencedTaskRunner::GetCurrentDefault());
236 
237   WaitableEventWatcher watcher2;
238   watcher2.StartWatching(
239       &event,
240       BindOnce(BindLambdaForTesting(callback), Unretained(&watcher2_counter)),
241       SequencedTaskRunner::GetCurrentDefault());
242 
243   event.Signal();
244   run_loop.Run();
245 
246   EXPECT_EQ(1, watcher1_counter);
247   EXPECT_EQ(1, watcher2_counter);
248   EXPECT_EQ(2, total_counter);
249   EXPECT_TRUE(event.IsSignaled());
250 }
251 
252 // Tests that only one async waiter gets called back for an auto-reset event.
TEST_P(WaitableEventWatcherTest,MultipleWatchersAutomatic)253 TEST_P(WaitableEventWatcherTest, MultipleWatchersAutomatic) {
254   test::TaskEnvironment task_environment(GetParam());
255 
256   WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
257                       WaitableEvent::InitialState::NOT_SIGNALED);
258 
259   int counter1 = 0;
260   int counter2 = 0;
261 
262   auto callback = [](RunLoop** run_loop, int* counter, WaitableEvent* event) {
263     ++(*counter);
264     (*run_loop)->QuitWhenIdle();
265   };
266 
267   // The same RunLoop instance cannot be Run more than once, and it is
268   // undefined which watcher will get called back first. Have the callback
269   // dereference this pointer to quit the loop, which will be updated on each
270   // Run.
271   RunLoop* current_run_loop;
272 
273   WaitableEventWatcher watcher1;
274   watcher1.StartWatching(
275       &event,
276       BindOnce(callback, Unretained(&current_run_loop), Unretained(&counter1)),
277       SequencedTaskRunner::GetCurrentDefault());
278 
279   WaitableEventWatcher watcher2;
280   watcher2.StartWatching(
281       &event,
282       BindOnce(callback, Unretained(&current_run_loop), Unretained(&counter2)),
283       SequencedTaskRunner::GetCurrentDefault());
284 
285   event.Signal();
286   {
287     RunLoop run_loop;
288     current_run_loop = &run_loop;
289     run_loop.Run();
290   }
291 
292   // Only one of the waiters should have been signaled.
293   EXPECT_TRUE((counter1 == 1) ^ (counter2 == 1));
294 
295   EXPECT_FALSE(event.IsSignaled());
296 
297   event.Signal();
298   {
299     RunLoop run_loop;
300     current_run_loop = &run_loop;
301     run_loop.Run();
302   }
303 
304   EXPECT_FALSE(event.IsSignaled());
305 
306   // The other watcher should have been signaled.
307   EXPECT_EQ(1, counter1);
308   EXPECT_EQ(1, counter2);
309 }
310 
311 // To help detect errors around deleting WaitableEventWatcher, an additional
312 // bool parameter is used to test sleeping between watching and deletion.
313 class WaitableEventWatcherDeletionTest
314     : public testing::TestWithParam<
315           std::tuple<test::TaskEnvironment::MainThreadType, bool>> {};
316 
TEST_P(WaitableEventWatcherDeletionTest,DeleteUnder)317 TEST_P(WaitableEventWatcherDeletionTest, DeleteUnder) {
318   auto [main_thread_type, delay_after_delete] = GetParam();
319 
320   // Delete the WaitableEvent out from under the Watcher. This is explictly
321   // allowed by the interface.
322 
323   test::TaskEnvironment task_environment(main_thread_type);
324 
325   {
326     WaitableEventWatcher watcher;
327 
328     auto* event = new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC,
329                                     WaitableEvent::InitialState::NOT_SIGNALED);
330 
331     watcher.StartWatching(event, DoNothing(),
332                           SequencedTaskRunner::GetCurrentDefault());
333 
334     if (delay_after_delete) {
335       // On Windows that sleep() improves the chance to catch some problems.
336       // It postpones the dtor |watcher| (which immediately cancel the waiting)
337       // and gives some time to run to a created background thread.
338       // Unfortunately, that thread is under OS control and we can't
339       // manipulate it directly.
340       PlatformThread::Sleep(Milliseconds(30));
341     }
342 
343     delete event;
344   }
345 }
346 
TEST_P(WaitableEventWatcherDeletionTest,SignalAndDelete)347 TEST_P(WaitableEventWatcherDeletionTest, SignalAndDelete) {
348   auto [main_thread_type, delay_after_delete] = GetParam();
349 
350   // Signal and immediately delete the WaitableEvent out from under the Watcher.
351 
352   test::TaskEnvironment task_environment(main_thread_type);
353 
354   {
355     base::RunLoop loop;
356     WaitableEventWatcher watcher;
357 
358     auto event = std::make_unique<WaitableEvent>(
359         WaitableEvent::ResetPolicy::AUTOMATIC,
360         WaitableEvent::InitialState::NOT_SIGNALED);
361 
362     watcher.StartWatching(
363         event.get(), BindOnce(&QuitWhenSignaled, loop.QuitWhenIdleClosure()),
364         SequencedTaskRunner::GetCurrentDefault());
365     event->Signal();
366     event.reset();
367 
368     if (delay_after_delete) {
369       // On Windows that sleep() improves the chance to catch some problems.
370       // It postpones the dtor |watcher| (which immediately cancel the waiting)
371       // and gives some time to run to a created background thread.
372       // Unfortunately, that thread is under OS control and we can't
373       // manipulate it directly.
374       PlatformThread::Sleep(Milliseconds(30));
375     }
376 
377     // Wait for the watcher callback.
378     loop.Run();
379   }
380 }
381 
382 // Tests deleting the WaitableEventWatcher between signaling the event and
383 // when the callback should be run.
TEST_P(WaitableEventWatcherDeletionTest,DeleteWatcherBeforeCallback)384 TEST_P(WaitableEventWatcherDeletionTest, DeleteWatcherBeforeCallback) {
385   auto [main_thread_type, delay_after_delete] = GetParam();
386 
387   test::TaskEnvironment task_environment(main_thread_type);
388   scoped_refptr<SingleThreadTaskRunner> task_runner =
389       SingleThreadTaskRunner::GetCurrentDefault();
390 
391   // Flag used to esnure that the |watcher_callback| never runs.
392   bool did_callback = false;
393 
394   WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
395                       WaitableEvent::InitialState::NOT_SIGNALED);
396   auto watcher = std::make_unique<WaitableEventWatcher>();
397 
398   // Queue up a series of tasks:
399   // 1. StartWatching the WaitableEvent
400   // 2. Signal the event (which will result in another task getting posted to
401   //    the |task_runner|)
402   // 3. Delete the WaitableEventWatcher
403   // 4. WaitableEventWatcher callback should run (from #2)
404 
405   WaitableEventWatcher::EventCallback watcher_callback = BindOnce(
406       [](bool* did_callback, WaitableEvent*) {
407         *did_callback = true;
408       },
409       Unretained(&did_callback));
410 
411   task_runner->PostTask(
412       FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
413                           Unretained(watcher.get()), Unretained(&event),
414                           std::move(watcher_callback), task_runner));
415   task_runner->PostTask(FROM_HERE,
416                         BindOnce(&WaitableEvent::Signal, Unretained(&event)));
417   task_runner->DeleteSoon(FROM_HERE, std::move(watcher));
418   if (delay_after_delete) {
419     task_runner->PostTask(FROM_HERE,
420                           BindOnce(&PlatformThread::Sleep, Milliseconds(30)));
421   }
422 
423   RunLoop().RunUntilIdle();
424 
425   EXPECT_FALSE(did_callback);
426 }
427 
428 INSTANTIATE_TEST_SUITE_P(All,
429                          WaitableEventWatcherTest,
430                          testing::ValuesIn(testing_main_threads));
431 
432 INSTANTIATE_TEST_SUITE_P(
433     All,
434     WaitableEventWatcherDeletionTest,
435     testing::Combine(testing::ValuesIn(testing_main_threads), testing::Bool()));
436 
437 }  // namespace base
438