
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}
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
|