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

Unified Diff: base/message_loop_proxy_unittest.cc

Issue 7210053: Implementation of PostTaskAndReply() in MessageLoopProxy and BrowserThread. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: copyright Created 9 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 | « base/message_loop_proxy.cc ('k') | base/task.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/message_loop_proxy_unittest.cc
diff --git a/base/message_loop_proxy_unittest.cc b/base/message_loop_proxy_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..aeb2f0c92e57b68c09d781bd3c07da3ee689d166
--- /dev/null
+++ b/base/message_loop_proxy_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2011 The Chromium 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 "base/message_loop_proxy.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class MessageLoopProxyTest : public testing::Test {
+ public:
+ MessageLoopProxyTest()
+ : current_loop_(new MessageLoop()),
+ task_thread_("task_thread"),
+ thread_sync_(true, false) {
+ }
+
+ void DeleteCurrentMessageLoop() {
+ current_loop_.reset();
+ }
+
+ protected:
+ virtual void SetUp() {
+ // Use SetUp() instead of the constructor to avoid posting a task to a
+ // partialy constructed object.
+ task_thread_.Start();
+
+ // Allow us to pause the |task_thread_|'s MessageLoop.
+ task_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ Bind(&MessageLoopProxyTest::BlockTaskThreadHelper, Unretained(this)));
+ }
+
+ virtual void TearDown() {
+ // Make sure the |task_thread_| is not blocked, and stop the thread
+ // fully before destuction because its tasks may still depend on the
+ // |thread_sync_| event.
+ thread_sync_.Signal();
+ task_thread_.Stop();
+ DeleteCurrentMessageLoop();
+ }
+
+ // Make LoopRecorder threadsafe so that there is defined behavior even if a
+ // threading mistake sneaks into the PostTaskAndReplyRelay implementation.
+ class LoopRecorder : public RefCountedThreadSafe<LoopRecorder> {
+ public:
+ LoopRecorder(MessageLoop** run_on, MessageLoop** deleted_on,
+ int* destruct_order)
+ : run_on_(run_on),
+ deleted_on_(deleted_on),
+ destruct_order_(destruct_order) {
+ }
+
+ void RecordRun() {
+ *run_on_ = MessageLoop::current();
+ }
+
+ private:
+ friend class RefCountedThreadSafe<LoopRecorder>;
+ ~LoopRecorder() {
+ *deleted_on_ = MessageLoop::current();
+ *destruct_order_ = g_order.GetNext();
+ }
+
+ MessageLoop** run_on_;
+ MessageLoop** deleted_on_;
+ int* destruct_order_;
+ };
+
+ static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
+ recorder->RecordRun();
+ }
+
+ static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
+ recorder->RecordRun();
+ MessageLoop::current()->Quit();
+ }
+
+ void UnblockTaskThread() {
+ thread_sync_.Signal();
+ }
+
+ void BlockTaskThreadHelper() {
+ thread_sync_.Wait();
+ }
+
+ static AtomicSequenceNumber g_order;
+
+ scoped_ptr<MessageLoop> current_loop_;
+ Thread task_thread_;
+
+ private:
+ base::WaitableEvent thread_sync_;
+};
+
+AtomicSequenceNumber MessageLoopProxyTest::g_order(LINKER_INITIALIZED);
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReply_Basic) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ ASSERT_TRUE(task_thread_.message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ UnblockTaskThread();
+ current_loop_->Run();
+
+ EXPECT_EQ(task_thread_.message_loop(), task_run_on);
+ EXPECT_EQ(current_loop_.get(), task_deleted_on);
+ EXPECT_EQ(current_loop_.get(), reply_run_on);
+ EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+ EXPECT_LT(task_delete_order, reply_delete_order);
+}
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Grab a MessageLoopProxy to a dead MessageLoop.
+ scoped_refptr<MessageLoopProxy> task_loop_proxy =
+ task_thread_.message_loop_proxy();
+ UnblockTaskThread();
+ task_thread_.Stop();
+
+ ASSERT_FALSE(task_loop_proxy->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // The relay should have properly deleted its resources leaving us as the only
+ // reference.
+ EXPECT_EQ(task_delete_order, reply_delete_order);
+ ASSERT_TRUE(task_recoder->HasOneRef());
+ ASSERT_TRUE(reply_recoder->HasOneRef());
+
+ // Nothing should have run though.
+ EXPECT_FALSE(task_run_on);
+ EXPECT_FALSE(reply_run_on);
+}
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReply_SameLoop) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Enqueue the relay.
+ ASSERT_TRUE(current_loop_->message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ current_loop_->Run();
+
+ EXPECT_EQ(current_loop_.get(), task_run_on);
+ EXPECT_EQ(current_loop_.get(), task_deleted_on);
+ EXPECT_EQ(current_loop_.get(), reply_run_on);
+ EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+ EXPECT_LT(task_delete_order, reply_delete_order);
+}
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReply_DeadReplyLoopDoesNotDelete) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Enqueue the relay.
+ task_thread_.message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ UnblockTaskThread();
+
+ // Mercilessly whack the current loop before |reply| gets to run.
+ current_loop_.reset();
+
+ // This should ensure the relay has been run. We need to record the
+ // MessageLoop pointer before stopping the thread because Thread::Stop() will
+ // NULL out its own pointer.
+ MessageLoop* task_loop = task_thread_.message_loop();
+ task_thread_.Stop();
+
+ EXPECT_EQ(task_loop, task_run_on);
+ ASSERT_FALSE(task_deleted_on);
+ EXPECT_FALSE(reply_run_on);
+ ASSERT_FALSE(reply_deleted_on);
+ EXPECT_EQ(task_delete_order, reply_delete_order);
+
+ // The PostTaskAndReplyRelay is leaked here. Even if we had a reference to
+ // it, we cannot just delete it because PostTaskAndReplyRelay's destructor
+ // checks that MessageLoop::current() is the the same as when the
+ // PostTaskAndReplyRelay object was constructed. However, this loop must have
+ // aleady been deleted in order to perform this test. See
+ // http://crbug.com/86301.
+}
+
+} // namespace
+
+} // namespace base
« no previous file with comments | « base/message_loop_proxy.cc ('k') | base/task.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698