blob: dde70ad598fe04b559a4ca01c4665737a64cc3a3 [file] [log] [blame]
// 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"
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