diff options
Diffstat (limited to 'base/message_loop')
-rw-r--r-- | base/message_loop/message_loop_proxy.cc | 17 | ||||
-rw-r--r-- | base/message_loop/message_loop_proxy.h | 38 | ||||
-rw-r--r-- | base/message_loop/message_loop_proxy_impl.cc | 92 | ||||
-rw-r--r-- | base/message_loop/message_loop_proxy_impl.h | 62 | ||||
-rw-r--r-- | base/message_loop/message_loop_proxy_impl_unittest.cc | 129 | ||||
-rw-r--r-- | base/message_loop/message_loop_proxy_unittest.cc | 266 |
6 files changed, 604 insertions, 0 deletions
diff --git a/base/message_loop/message_loop_proxy.cc b/base/message_loop/message_loop_proxy.cc new file mode 100644 index 0000000..e5f0142 --- /dev/null +++ b/base/message_loop/message_loop_proxy.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2012 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/message_loop_proxy.h" + +#include "base/bind.h" + +namespace base { + +MessageLoopProxy::MessageLoopProxy() { +} + +MessageLoopProxy::~MessageLoopProxy() { +} + +} // namespace base diff --git a/base/message_loop/message_loop_proxy.h b/base/message_loop/message_loop_proxy.h new file mode 100644 index 0000000..4ace802 --- /dev/null +++ b/base/message_loop/message_loop_proxy.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 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. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_H_ + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" + +namespace base { + +// This class provides a thread-safe refcounted interface to the Post* methods +// of a message loop. This class can outlive the target message loop. +// MessageLoopProxy objects are constructed automatically for all MessageLoops. +// So, to access them, you can use any of the following: +// Thread::message_loop_proxy() +// MessageLoop::current()->message_loop_proxy() +// MessageLoopProxy::current() +// +// TODO(akalin): Now that we have the *TaskRunner interfaces, we can +// merge this with MessageLoopProxyImpl. +class BASE_EXPORT MessageLoopProxy : public SingleThreadTaskRunner { + public: + // Gets the MessageLoopProxy for the current message loop, creating one if + // needed. + static scoped_refptr<MessageLoopProxy> current(); + + protected: + MessageLoopProxy(); + virtual ~MessageLoopProxy(); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_H_ diff --git a/base/message_loop/message_loop_proxy_impl.cc b/base/message_loop/message_loop_proxy_impl.cc new file mode 100644 index 0000000..7dc8caa --- /dev/null +++ b/base/message_loop/message_loop_proxy_impl.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2012 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/message_loop_proxy_impl.h" + +#include "base/location.h" +#include "base/threading/thread_restrictions.h" + +namespace base { + +MessageLoopProxyImpl::~MessageLoopProxyImpl() { +} + +bool MessageLoopProxyImpl::PostDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + return PostTaskHelper(from_here, task, delay, true); +} + +bool MessageLoopProxyImpl::PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + return PostTaskHelper(from_here, task, delay, false); +} + +bool MessageLoopProxyImpl::RunsTasksOnCurrentThread() const { + // We shouldn't use MessageLoop::current() since it uses LazyInstance which + // may be deleted by ~AtExitManager when a WorkerPool thread calls this + // function. + // http://crbug.com/63678 + base::ThreadRestrictions::ScopedAllowSingleton allow_singleton; + AutoLock lock(message_loop_lock_); + return (target_message_loop_ && + (MessageLoop::current() == target_message_loop_)); +} + +// MessageLoop::DestructionObserver implementation +void MessageLoopProxyImpl::WillDestroyCurrentMessageLoop() { + AutoLock lock(message_loop_lock_); + target_message_loop_ = NULL; +} + +void MessageLoopProxyImpl::OnDestruct() const { + // We shouldn't use MessageLoop::current() since it uses LazyInstance which + // may be deleted by ~AtExitManager when a WorkerPool thread calls this + // function. + // http://crbug.com/63678 + base::ThreadRestrictions::ScopedAllowSingleton allow_singleton; + bool delete_later = false; + { + AutoLock lock(message_loop_lock_); + if (target_message_loop_ && + (MessageLoop::current() != target_message_loop_)) { + target_message_loop_->DeleteSoon(FROM_HERE, this); + delete_later = true; + } + } + if (!delete_later) + delete this; +} + +MessageLoopProxyImpl::MessageLoopProxyImpl() + : target_message_loop_(MessageLoop::current()) { +} + +bool MessageLoopProxyImpl::PostTaskHelper( + const tracked_objects::Location& from_here, const base::Closure& task, + base::TimeDelta delay, bool nestable) { + AutoLock lock(message_loop_lock_); + if (target_message_loop_) { + if (nestable) { + target_message_loop_->PostDelayedTask(from_here, task, delay); + } else { + target_message_loop_->PostNonNestableDelayedTask(from_here, task, delay); + } + return true; + } + return false; +} + +scoped_refptr<MessageLoopProxy> +MessageLoopProxy::current() { + MessageLoop* cur_loop = MessageLoop::current(); + if (!cur_loop) + return NULL; + return cur_loop->message_loop_proxy(); +} + +} // namespace base diff --git a/base/message_loop/message_loop_proxy_impl.h b/base/message_loop/message_loop_proxy_impl.h new file mode 100644 index 0000000..7da99e2 --- /dev/null +++ b/base/message_loop/message_loop_proxy_impl.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012 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. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_IMPL_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_IMPL_H_ + +#include "base/base_export.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/synchronization/lock.h" + +namespace base { + +// A stock implementation of MessageLoopProxy that is created and managed by a +// MessageLoop. For now a MessageLoopProxyImpl can only be created as part of a +// MessageLoop. +class BASE_EXPORT MessageLoopProxyImpl : public MessageLoopProxy { + public: + // MessageLoopProxy implementation + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) OVERRIDE; + virtual bool PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) OVERRIDE; + virtual bool RunsTasksOnCurrentThread() const OVERRIDE; + + protected: + virtual ~MessageLoopProxyImpl(); + + // Override OnDestruct so that we can delete the object on the target message + // loop if it still exists. + virtual void OnDestruct() const OVERRIDE; + + private: + // Allow the MessageLoop to create a MessageLoopProxyImpl. + friend class ::MessageLoop; + friend class DeleteHelper<MessageLoopProxyImpl>; + + MessageLoopProxyImpl(); + + // Called directly by MessageLoop::~MessageLoop. + virtual void WillDestroyCurrentMessageLoop(); + + + bool PostTaskHelper(const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay, + bool nestable); + + // The lock that protects access to target_message_loop_. + mutable base::Lock message_loop_lock_; + MessageLoop* target_message_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessageLoopProxyImpl); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_IMPL_H_ diff --git a/base/message_loop/message_loop_proxy_impl_unittest.cc b/base/message_loop/message_loop_proxy_impl_unittest.cc new file mode 100644 index 0000000..4c88887 --- /dev/null +++ b/base/message_loop/message_loop_proxy_impl_unittest.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2012 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/message_loop_proxy_impl.h" + +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace base { + +class MessageLoopProxyImplTest : public testing::Test { + public: + void Release() const { + AssertOnIOThread(); + Quit(); + } + + void Quit() const { + loop_.PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure()); + } + + void AssertOnIOThread() const { + ASSERT_TRUE(io_thread_->message_loop_proxy()->BelongsToCurrentThread()); + ASSERT_EQ(io_thread_->message_loop_proxy(), + MessageLoopProxy::current()); + } + + void AssertOnFileThread() const { + ASSERT_TRUE(file_thread_->message_loop_proxy()->BelongsToCurrentThread()); + ASSERT_EQ(file_thread_->message_loop_proxy(), + MessageLoopProxy::current()); + } + + protected: + virtual void SetUp() OVERRIDE { + io_thread_.reset(new Thread("MessageLoopProxyImplTest_IO")); + file_thread_.reset(new Thread("MessageLoopProxyImplTest_File")); + io_thread_->Start(); + file_thread_->Start(); + } + + virtual void TearDown() OVERRIDE { + io_thread_->Stop(); + file_thread_->Stop(); + } + + static void BasicFunction(MessageLoopProxyImplTest* test) { + test->AssertOnFileThread(); + test->Quit(); + } + + static void AssertNotRun() { + FAIL() << "Callback Should not get executed."; + } + + class DeletedOnFile { + public: + explicit DeletedOnFile(MessageLoopProxyImplTest* test) : test_(test) {} + + ~DeletedOnFile() { + test_->AssertOnFileThread(); + test_->Quit(); + } + + private: + MessageLoopProxyImplTest* test_; + }; + + scoped_ptr<Thread> io_thread_; + scoped_ptr<Thread> file_thread_; + + private: + mutable MessageLoop loop_; +}; + +TEST_F(MessageLoopProxyImplTest, Release) { + EXPECT_TRUE(io_thread_->message_loop_proxy()->ReleaseSoon(FROM_HERE, this)); + MessageLoop::current()->Run(); +} + +TEST_F(MessageLoopProxyImplTest, Delete) { + DeletedOnFile* deleted_on_file = new DeletedOnFile(this); + EXPECT_TRUE(file_thread_->message_loop_proxy()->DeleteSoon( + FROM_HERE, deleted_on_file)); + MessageLoop::current()->Run(); +} + +TEST_F(MessageLoopProxyImplTest, PostTask) { + EXPECT_TRUE(file_thread_->message_loop_proxy()->PostTask( + FROM_HERE, Bind(&MessageLoopProxyImplTest::BasicFunction, + Unretained(this)))); + MessageLoop::current()->Run(); +} + +TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadExits) { + scoped_ptr<Thread> test_thread( + new Thread("MessageLoopProxyImplTest_Dummy")); + test_thread->Start(); + scoped_refptr<MessageLoopProxy> message_loop_proxy = + test_thread->message_loop_proxy(); + test_thread->Stop(); + + bool ret = message_loop_proxy->PostTask( + FROM_HERE, + Bind(&MessageLoopProxyImplTest::AssertNotRun)); + EXPECT_FALSE(ret); +} + +TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadIsDeleted) { + scoped_refptr<MessageLoopProxy> message_loop_proxy; + { + scoped_ptr<Thread> test_thread( + new Thread("MessageLoopProxyImplTest_Dummy")); + test_thread->Start(); + message_loop_proxy = test_thread->message_loop_proxy(); + } + bool ret = message_loop_proxy->PostTask( + FROM_HERE, + Bind(&MessageLoopProxyImplTest::AssertNotRun)); + EXPECT_FALSE(ret); +} + +} // namespace base diff --git a/base/message_loop/message_loop_proxy_unittest.cc b/base/message_loop/message_loop_proxy_unittest.cc new file mode 100644 index 0000000..e373059 --- /dev/null +++ b/base/message_loop/message_loop_proxy_unittest.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2012 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/message_loop_proxy.h" + +#include "base/atomic_sequence_num.h" +#include "base/bind.h" +#include "base/debug/leak_annotations.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() OVERRIDE { + // 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() OVERRIDE { + // 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()->QuitWhenIdle(); + } + + void UnblockTaskThread() { + thread_sync_.Signal(); + } + + void BlockTaskThreadHelper() { + thread_sync_.Wait(); + } + + static StaticAtomicSequenceNumber g_order; + + scoped_ptr<MessageLoop> current_loop_; + Thread task_thread_; + + private: + base::WaitableEvent thread_sync_; +}; + +StaticAtomicSequenceNumber MessageLoopProxyTest::g_order; + +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) { + // Annotate the scope as having memory leaks to suppress heapchecker reports. + ANNOTATE_SCOPED_MEMORY_LEAK; + 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 |