mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
187 lines
5.3 KiB
C++
187 lines
5.3 KiB
C++
// 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 <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <atomic>
|
|
#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
|