Files
platform-external-webrtc/rtc_base/post_message_with_functor_unittest.cc
Henrik Boström 13bc8713af PostMessageWithFunctor() added.
This function is used to post messages onto rtc::Threads. The thread
invokes the functor without blocking the calling thread. Messages posted
in this way are executed in the order that they were posted. This is
meant to work as the equivalent of "thread->PostTask()" in Chromium.

Note: AsyncInvoker currently does something similar but it is more
cumbersome to use (somebody has to create it and own it and make sure
not to destroy it while tasks are pending or else they're cancelled). It
also comes with a fundamental flaw: You cannot destroy the AsyncInvoker
from within the functor (this results in a neverending Wait). This makes
the AsyncInvoker not suitable for implementing "destructor traits"
amongst other things.

This CL will allow us to easily add "PostTask()" to rtc::Thread or add
support for DestructorTraits, which is especially useful when you have a
reference counted object that is referenced from multiple threads but
owns resources that has to be destroyed on a particular thread.

Blocking invokes are forbidden in Chromium but WebRTC performs them
frequently. Being able to perform the equivalent of PostTask() is a
good thing.

Bug: webrtc:10293
Change-Id: Ie2a612059a783f18ddf98cff6edb7fce447fb5be
Reviewed-on: https://webrtc-review.googlesource.com/c/121408
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26704}
2019-02-15 10:00:18 +00:00

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