230 lines
7.1 KiB
C++
230 lines
7.1 KiB
C++
|
|
/*
|
||
|
|
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||
|
|
*
|
||
|
|
* Use of this source code is governed by a BSD-style license
|
||
|
|
* that can be found in the LICENSE file in the root of the source
|
||
|
|
* tree. An additional intellectual property rights grant can be found
|
||
|
|
* in the file PATENTS. All contributing project authors may
|
||
|
|
* be found in the AUTHORS file in the root of the source tree.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "rtc_base/post_message_with_functor.h"
|
||
|
|
|
||
|
|
#include <memory>
|
||
|
|
|
||
|
|
#include "rtc_base/bind.h"
|
||
|
|
#include "rtc_base/checks.h"
|
||
|
|
#include "rtc_base/event.h"
|
||
|
|
#include "rtc_base/gunit.h"
|
||
|
|
#include "rtc_base/ref_counted_object.h"
|
||
|
|
#include "rtc_base/thread.h"
|
||
|
|
#include "test/gtest.h"
|
||
|
|
|
||
|
|
namespace rtc {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
void ThreadIsCurrent(Thread* thread, bool* result, Event* event) {
|
||
|
|
*result = thread->IsCurrent();
|
||
|
|
event->Set();
|
||
|
|
}
|
||
|
|
|
||
|
|
void WaitAndSetEvent(Event* wait_event, Event* set_event) {
|
||
|
|
wait_event->Wait(Event::kForever);
|
||
|
|
set_event->Set();
|
||
|
|
}
|
||
|
|
|
||
|
|
// A functor that keeps track of the number of copies and moves.
|
||
|
|
class LifeCycleFunctor {
|
||
|
|
public:
|
||
|
|
struct Stats {
|
||
|
|
size_t copy_count = 0;
|
||
|
|
size_t move_count = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
LifeCycleFunctor(Stats* stats, Event* event) : stats_(stats), event_(event) {}
|
||
|
|
LifeCycleFunctor(const LifeCycleFunctor& other) { *this = other; }
|
||
|
|
LifeCycleFunctor(LifeCycleFunctor&& other) { *this = std::move(other); }
|
||
|
|
|
||
|
|
LifeCycleFunctor& operator=(const LifeCycleFunctor& other) {
|
||
|
|
stats_ = other.stats_;
|
||
|
|
event_ = other.event_;
|
||
|
|
++stats_->copy_count;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
LifeCycleFunctor& operator=(LifeCycleFunctor&& other) {
|
||
|
|
stats_ = other.stats_;
|
||
|
|
event_ = other.event_;
|
||
|
|
++stats_->move_count;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
void operator()() { event_->Set(); }
|
||
|
|
|
||
|
|
private:
|
||
|
|
Stats* stats_;
|
||
|
|
Event* event_;
|
||
|
|
};
|
||
|
|
|
||
|
|
// A functor that verifies the thread it was destroyed on.
|
||
|
|
class DestructionFunctor {
|
||
|
|
public:
|
||
|
|
DestructionFunctor(Thread* thread, bool* thread_was_current, Event* event)
|
||
|
|
: thread_(thread),
|
||
|
|
thread_was_current_(thread_was_current),
|
||
|
|
event_(event) {}
|
||
|
|
~DestructionFunctor() {
|
||
|
|
// Only signal the event if this was the functor that was invoked to avoid
|
||
|
|
// the event being signaled due to the destruction of temporary/moved
|
||
|
|
// versions of this object.
|
||
|
|
if (was_invoked_) {
|
||
|
|
*thread_was_current_ = thread_->IsCurrent();
|
||
|
|
event_->Set();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void operator()() { was_invoked_ = true; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
Thread* thread_;
|
||
|
|
bool* thread_was_current_;
|
||
|
|
Event* event_;
|
||
|
|
bool was_invoked_ = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesWithBind) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
Event event;
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
Bind(&Event::Set, &event));
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesWithLambda) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
Event event;
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
[&event] { event.Set(); });
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesWithCopiedFunctor) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
LifeCycleFunctor::Stats stats;
|
||
|
|
Event event;
|
||
|
|
LifeCycleFunctor functor(&stats, &event);
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(), functor);
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
|
||
|
|
EXPECT_EQ(1u, stats.copy_count);
|
||
|
|
EXPECT_EQ(0u, stats.move_count);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesWithMovedFunctor) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
LifeCycleFunctor::Stats stats;
|
||
|
|
Event event;
|
||
|
|
LifeCycleFunctor functor(&stats, &event);
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
std::move(functor));
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
|
||
|
|
EXPECT_EQ(0u, stats.copy_count);
|
||
|
|
EXPECT_EQ(1u, stats.move_count);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest,
|
||
|
|
InvokesWithCopiedFunctorDestroyedOnTargetThread) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
Event event;
|
||
|
|
bool was_invoked_on_background_thread = false;
|
||
|
|
DestructionFunctor functor(background_thread.get(),
|
||
|
|
&was_invoked_on_background_thread, &event);
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(), functor);
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
|
||
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest,
|
||
|
|
InvokesWithMovedFunctorDestroyedOnTargetThread) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
Event event;
|
||
|
|
bool was_invoked_on_background_thread = false;
|
||
|
|
DestructionFunctor functor(background_thread.get(),
|
||
|
|
&was_invoked_on_background_thread, &event);
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
std::move(functor));
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
|
||
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesOnBackgroundThread) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
Event event;
|
||
|
|
bool was_invoked_on_background_thread = false;
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
Bind(&ThreadIsCurrent, background_thread.get(),
|
||
|
|
&was_invoked_on_background_thread, &event));
|
||
|
|
event.Wait(Event::kForever);
|
||
|
|
|
||
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesAsynchronously) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
// The first event ensures that SendSingleMessage() is not blocking this
|
||
|
|
// thread. The second event ensures that the message is processed.
|
||
|
|
Event event_set_by_test_thread;
|
||
|
|
Event event_set_by_background_thread;
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
Bind(&WaitAndSetEvent, &event_set_by_test_thread,
|
||
|
|
&event_set_by_background_thread));
|
||
|
|
event_set_by_test_thread.Set();
|
||
|
|
event_set_by_background_thread.Wait(Event::kForever);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(PostMessageWithFunctorTest, InvokesInPostedOrder) {
|
||
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
||
|
|
background_thread->Start();
|
||
|
|
|
||
|
|
Event first;
|
||
|
|
Event second;
|
||
|
|
Event third;
|
||
|
|
Event fourth;
|
||
|
|
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
Bind(&WaitAndSetEvent, &first, &second));
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
Bind(&WaitAndSetEvent, &second, &third));
|
||
|
|
PostMessageWithFunctor(RTC_FROM_HERE, background_thread.get(),
|
||
|
|
Bind(&WaitAndSetEvent, &third, &fourth));
|
||
|
|
|
||
|
|
// All tasks have been posted before the first one is unblocked.
|
||
|
|
first.Set();
|
||
|
|
// Only if the chain is invoked in posted order will the last event be set.
|
||
|
|
fourth.Wait(Event::kForever);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace rtc
|