Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(532)

Unified Diff: webrtc/base/thread_unittest.cc

Issue 1303443003: Add helper class GuardedAsyncInvoker to protect against thread dying (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: tommi@s comments Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/base/asyncinvoker.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/base/thread_unittest.cc
diff --git a/webrtc/base/thread_unittest.cc b/webrtc/base/thread_unittest.cc
index 951a7e14f39670047cf12809d5cf9acef8b9d011..7b9481e823cda246fe1b0c7593709b143f115170 100644
--- a/webrtc/base/thread_unittest.cc
+++ b/webrtc/base/thread_unittest.cc
@@ -518,6 +518,182 @@ TEST_F(AsyncInvokeTest, FlushWithIds) {
EXPECT_TRUE(flag2);
}
+class GuardedAsyncInvokeTest : public testing::Test {
+ public:
+ void IntCallback(int value) {
+ EXPECT_EQ(expected_thread_, Thread::Current());
+ int_value_ = value;
+ }
+ void AsyncInvokeIntCallback(GuardedAsyncInvoker* invoker, Thread* thread) {
+ expected_thread_ = thread;
+ invoker->AsyncInvoke(FunctorC(), &GuardedAsyncInvokeTest::IntCallback,
+ static_cast<GuardedAsyncInvokeTest*>(this));
+ invoke_started_.Set();
+ }
+ void SetExpectedThreadForIntCallback(Thread* thread) {
+ expected_thread_ = thread;
+ }
+
+ protected:
+ const static int kWaitTimeout = 1000;
+ GuardedAsyncInvokeTest()
+ : int_value_(0),
+ invoke_started_(true, false),
+ expected_thread_(nullptr) {}
+
+ int int_value_;
+ Event invoke_started_;
+ Thread* expected_thread_;
+};
+
+// Functor for creating an invoker.
+struct CreateInvoker {
+ CreateInvoker(scoped_ptr<GuardedAsyncInvoker>* invoker) : invoker_(invoker) {}
+ void operator()() { invoker_->reset(new GuardedAsyncInvoker()); }
+ scoped_ptr<GuardedAsyncInvoker>* invoker_;
+};
+
+// Test that we can call AsyncInvoke<void>() after the thread died.
+TEST_F(GuardedAsyncInvokeTest, KillThreadFireAndForget) {
+ // Create and start the thread.
+ scoped_ptr<Thread> thread(new Thread());
+ thread->Start();
+ scoped_ptr<GuardedAsyncInvoker> invoker;
+ // Create the invoker on |thread|.
+ thread->Invoke<void>(CreateInvoker(&invoker));
+ // Kill |thread|.
+ thread = nullptr;
+ // Try calling functor.
+ bool called = false;
+ EXPECT_FALSE(invoker->AsyncInvoke<void>(FunctorB(&called)));
+ // With thread gone, nothing should happen.
+ WAIT(called, kWaitTimeout);
+ EXPECT_FALSE(called);
+}
+
+// Test that we can call AsyncInvoke with callback after the thread died.
+TEST_F(GuardedAsyncInvokeTest, KillThreadWithCallback) {
+ // Create and start the thread.
+ scoped_ptr<Thread> thread(new Thread());
+ thread->Start();
+ scoped_ptr<GuardedAsyncInvoker> invoker;
+ // Create the invoker on |thread|.
+ thread->Invoke<void>(CreateInvoker(&invoker));
+ // Kill |thread|.
+ thread = nullptr;
+ // Try calling functor.
+ EXPECT_FALSE(
+ invoker->AsyncInvoke(FunctorC(), &GuardedAsyncInvokeTest::IntCallback,
+ static_cast<GuardedAsyncInvokeTest*>(this)));
+ // With thread gone, callback should be cancelled.
+ Thread::Current()->ProcessMessages(kWaitTimeout);
+ EXPECT_EQ(0, int_value_);
+}
+
+// The remaining tests check that GuardedAsyncInvoker behaves as AsyncInvoker
+// when Thread is still alive.
+TEST_F(GuardedAsyncInvokeTest, FireAndForget) {
+ GuardedAsyncInvoker invoker;
+ // Try calling functor.
+ bool called = false;
+ EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&called)));
+ EXPECT_TRUE_WAIT(called, kWaitTimeout);
+}
+
+TEST_F(GuardedAsyncInvokeTest, WithCallback) {
+ GuardedAsyncInvoker invoker;
+ // Try calling functor.
+ SetExpectedThreadForIntCallback(Thread::Current());
+ EXPECT_TRUE(invoker.AsyncInvoke(FunctorA(),
+ &GuardedAsyncInvokeTest::IntCallback,
+ static_cast<GuardedAsyncInvokeTest*>(this)));
+ EXPECT_EQ_WAIT(42, int_value_, kWaitTimeout);
+}
+
+TEST_F(GuardedAsyncInvokeTest, CancelInvoker) {
+ // Try destroying invoker during call.
+ {
+ GuardedAsyncInvoker invoker;
+ EXPECT_TRUE(
+ invoker.AsyncInvoke(FunctorC(), &GuardedAsyncInvokeTest::IntCallback,
+ static_cast<GuardedAsyncInvokeTest*>(this)));
+ }
+ // With invoker gone, callback should be cancelled.
+ Thread::Current()->ProcessMessages(kWaitTimeout);
+ EXPECT_EQ(0, int_value_);
+}
+
+TEST_F(GuardedAsyncInvokeTest, CancelCallingThread) {
+ GuardedAsyncInvoker invoker;
+ // Try destroying calling thread during call.
+ {
+ Thread thread;
+ thread.Start();
+ // Try calling functor.
+ thread.Invoke<void>(Bind(&GuardedAsyncInvokeTest::AsyncInvokeIntCallback,
+ static_cast<GuardedAsyncInvokeTest*>(this),
+ &invoker, Thread::Current()));
+ // Wait for the call to begin.
+ ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout));
+ }
+ // Calling thread is gone. Return message shouldn't happen.
+ Thread::Current()->ProcessMessages(kWaitTimeout);
+ EXPECT_EQ(0, int_value_);
+}
+
+TEST_F(GuardedAsyncInvokeTest, KillInvokerBeforeExecute) {
+ Thread thread;
+ thread.Start();
+ {
+ GuardedAsyncInvoker invoker;
+ // Try calling functor.
+ thread.Invoke<void>(Bind(&GuardedAsyncInvokeTest::AsyncInvokeIntCallback,
+ static_cast<GuardedAsyncInvokeTest*>(this),
+ &invoker, Thread::Current()));
+ // Wait for the call to begin.
+ ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout));
+ }
+ // Invoker is destroyed. Function should not execute.
+ Thread::Current()->ProcessMessages(kWaitTimeout);
+ EXPECT_EQ(0, int_value_);
+}
+
+TEST_F(GuardedAsyncInvokeTest, Flush) {
+ GuardedAsyncInvoker invoker;
+ bool flag1 = false;
+ bool flag2 = false;
+ // Queue two async calls to the current thread.
+ EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag1)));
+ EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag2)));
+ // Because we haven't pumped messages, these should not have run yet.
+ EXPECT_FALSE(flag1);
+ EXPECT_FALSE(flag2);
+ // Force them to run now.
+ EXPECT_TRUE(invoker.Flush());
+ EXPECT_TRUE(flag1);
+ EXPECT_TRUE(flag2);
+}
+
+TEST_F(GuardedAsyncInvokeTest, FlushWithIds) {
+ GuardedAsyncInvoker invoker;
+ bool flag1 = false;
+ bool flag2 = false;
+ // Queue two async calls to the current thread, one with a message id.
+ EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag1), 5));
+ EXPECT_TRUE(invoker.AsyncInvoke<void>(FunctorB(&flag2)));
+ // Because we haven't pumped messages, these should not have run yet.
+ EXPECT_FALSE(flag1);
+ EXPECT_FALSE(flag2);
+ // Execute pending calls with id == 5.
+ EXPECT_TRUE(invoker.Flush(5));
+ EXPECT_TRUE(flag1);
+ EXPECT_FALSE(flag2);
+ flag1 = false;
+ // Execute all pending calls. The id == 5 call should not execute again.
+ EXPECT_TRUE(invoker.Flush());
+ EXPECT_FALSE(flag1);
+ EXPECT_TRUE(flag2);
+}
#if defined(WEBRTC_WIN)
class ComThreadTest : public testing::Test, public MessageHandler {
« no previous file with comments | « webrtc/base/asyncinvoker.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698