| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/fml/synchronization/waitable_event.h" | |
| #include <atomic> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <thread> | |
| #include <type_traits> | |
| #include <vector> | |
| #include "flutter/fml/macros.h" | |
| #include "gtest/gtest.h" | |
| // rand() is only used for tests in this file. | |
| // NOLINTBEGIN(clang-analyzer-security.insecureAPI.rand) | |
| namespace fml { | |
| namespace { | |
| constexpr TimeDelta kEpsilonTimeout = TimeDelta::FromMilliseconds(20); | |
| constexpr TimeDelta kTinyTimeout = TimeDelta::FromMilliseconds(100); | |
| constexpr TimeDelta kActionTimeout = TimeDelta::FromMilliseconds(10000); | |
| // Sleeps for a "very small" amount of time. | |
| void SleepFor(TimeDelta duration) { | |
| std::this_thread::sleep_for( | |
| std::chrono::nanoseconds(duration.ToNanoseconds())); | |
| } | |
| void EpsilonRandomSleep() { | |
| TimeDelta duration = | |
| TimeDelta::FromMilliseconds(static_cast<unsigned>(rand()) % 20u); | |
| SleepFor(duration); | |
| } | |
| // AutoResetWaitableEvent ------------------------------------------------------ | |
| TEST(AutoResetWaitableEventTest, Basic) { | |
| AutoResetWaitableEvent ev; | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Signal(); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| ev.Wait(); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Reset(); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Signal(); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| ev.Reset(); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| EXPECT_TRUE(ev.WaitWithTimeout(TimeDelta::Zero())); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| EXPECT_TRUE(ev.WaitWithTimeout(TimeDelta::FromMilliseconds(1))); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Signal(); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| EXPECT_FALSE(ev.WaitWithTimeout(TimeDelta::Zero())); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| EXPECT_TRUE(ev.WaitWithTimeout(TimeDelta::FromMilliseconds(1))); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Signal(); | |
| EXPECT_FALSE(ev.WaitWithTimeout(TimeDelta::FromMilliseconds(1))); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| } | |
| TEST(AutoResetWaitableEventTest, MultipleWaiters) { | |
| AutoResetWaitableEvent ev; | |
| for (size_t i = 0u; i < 5u; i++) { | |
| std::atomic_uint wake_count(0u); | |
| std::vector<std::thread> threads; | |
| for (size_t j = 0u; j < 4u; j++) { | |
| threads.push_back(std::thread([&ev, &wake_count]() { | |
| if (rand() % 2 == 0) { | |
| ev.Wait(); | |
| } else { | |
| EXPECT_FALSE(ev.WaitWithTimeout(kActionTimeout)); | |
| } | |
| wake_count.fetch_add(1u); | |
| // Note: We can't say anything about the signaled state of |ev| here, | |
| // since the main thread may have already signaled it again. | |
| })); | |
| } | |
| // Unfortunately, we can't really wait for the threads to be waiting, so we | |
| // just sleep for a bit, and count on them having started and advanced to | |
| // waiting. | |
| SleepFor(kTinyTimeout + kTinyTimeout); | |
| for (size_t j = 0u; j < threads.size(); j++) { | |
| unsigned old_wake_count = wake_count.load(); | |
| EXPECT_EQ(j, old_wake_count); | |
| // Each |Signal()| should wake exactly one thread. | |
| ev.Signal(); | |
| // Poll for |wake_count| to change. | |
| while (wake_count.load() == old_wake_count) { | |
| SleepFor(kEpsilonTimeout); | |
| } | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| // And once it's changed, wait a little longer, to see if any other | |
| // threads are awoken (they shouldn't be). | |
| SleepFor(kEpsilonTimeout); | |
| EXPECT_EQ(old_wake_count + 1u, wake_count.load()); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| } | |
| // Having done that, if we signal |ev| now, it should stay signaled. | |
| ev.Signal(); | |
| SleepFor(kEpsilonTimeout); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| for (auto& thread : threads) { | |
| thread.join(); | |
| } | |
| ev.Reset(); | |
| } | |
| } | |
| // ManualResetWaitableEvent ---------------------------------------------------- | |
| TEST(ManualResetWaitableEventTest, Basic) { | |
| ManualResetWaitableEvent ev; | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Signal(); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| ev.Wait(); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| ev.Reset(); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| EXPECT_TRUE(ev.WaitWithTimeout(TimeDelta::Zero())); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| EXPECT_TRUE(ev.WaitWithTimeout(TimeDelta::FromMilliseconds(1))); | |
| EXPECT_FALSE(ev.IsSignaledForTest()); | |
| ev.Signal(); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| EXPECT_FALSE(ev.WaitWithTimeout(TimeDelta::Zero())); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| EXPECT_FALSE(ev.WaitWithTimeout(TimeDelta::FromMilliseconds(1))); | |
| EXPECT_TRUE(ev.IsSignaledForTest()); | |
| } | |
| TEST(ManualResetWaitableEventTest, SignalMultiple) { | |
| ManualResetWaitableEvent ev; | |
| for (size_t i = 0u; i < 10u; i++) { | |
| for (size_t num_waiters = 1u; num_waiters < 5u; num_waiters++) { | |
| std::vector<std::thread> threads; | |
| for (size_t j = 0u; j < num_waiters; j++) { | |
| threads.push_back(std::thread([&ev]() { | |
| EpsilonRandomSleep(); | |
| if (rand() % 2 == 0) { | |
| ev.Wait(); | |
| } else { | |
| EXPECT_FALSE(ev.WaitWithTimeout(kActionTimeout)); | |
| } | |
| })); | |
| } | |
| EpsilonRandomSleep(); | |
| ev.Signal(); | |
| // The threads will only terminate once they've successfully waited (or | |
| // timed out). | |
| for (auto& thread : threads) { | |
| thread.join(); | |
| } | |
| ev.Reset(); | |
| } | |
| } | |
| } | |
| } // namespace | |
| } // namespace fml | |
| // NOLINTEND(clang-analyzer-security.insecureAPI.rand) |