diff options
author | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-06 18:34:24 +0000 |
---|---|---|
committer | sanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-06 18:34:24 +0000 |
commit | 656475d275524893e4e9b1f02469fe470721a14e (patch) | |
tree | 76198770e24f0bea147c10a50ae2a3bf9c7f7274 /base | |
parent | 7e19edf7255b366b5e4b9b0bb77caf9842a37f1b (diff) | |
download | chromium_src-656475d275524893e4e9b1f02469fe470721a14e.zip chromium_src-656475d275524893e4e9b1f02469fe470721a14e.tar.gz chromium_src-656475d275524893e4e9b1f02469fe470721a14e.tar.bz2 |
Created a stock implementation of the MessageLoopProxy interface than can be used to create an implementation that targets the current thread's message loop.
BUG=None
TEST=Unit tests provided.
Review URL: http://codereview.chromium.org/1837003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46591 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gyp | 1 | ||||
-rw-r--r-- | base/base.gypi | 2 | ||||
-rw-r--r-- | base/message_loop_proxy.h | 28 | ||||
-rw-r--r-- | base/message_loop_proxy_impl.cc | 101 | ||||
-rw-r--r-- | base/message_loop_proxy_impl.h | 61 | ||||
-rw-r--r-- | base/message_loop_proxy_impl_unittest.cc | 131 | ||||
-rw-r--r-- | base/thread.cc | 2 | ||||
-rw-r--r-- | base/thread.h | 15 |
8 files changed, 340 insertions, 1 deletions
diff --git a/base/base.gyp b/base/base.gyp index e3a236c..23201d2 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -98,6 +98,7 @@ 'linked_list_unittest.cc', 'linked_ptr_unittest.cc', 'mac_util_unittest.mm', + 'message_loop_proxy_impl_unittest.cc', 'message_loop_unittest.cc', 'message_pump_glib_unittest.cc', 'object_watcher_unittest.cc', diff --git a/base/base.gypi b/base/base.gypi index 22a72f9..b2074c2 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -118,6 +118,8 @@ 'message_loop.cc', 'message_loop.h', 'message_loop_proxy.h', + 'message_loop_proxy_impl.cc', + 'message_loop_proxy_impl.h', 'message_pump.h', 'message_pump_default.cc', 'message_pump_default.h', diff --git a/base/message_loop_proxy.h b/base/message_loop_proxy.h index 4b6abce..26fd368 100644 --- a/base/message_loop_proxy.h +++ b/base/message_loop_proxy.h @@ -9,9 +9,15 @@ #include "base/ref_counted.h" #include "base/task.h" +namespace base { + +struct MessageLoopProxyTraits; + // This class provides a thread-safe refcounted interface to the Post* methods // of a message loop. This class can outlive the target message loop. -class MessageLoopProxy : public base::RefCountedThreadSafe<MessageLoopProxy> { +class MessageLoopProxy + : public base::RefCountedThreadSafe<MessageLoopProxy, + MessageLoopProxyTraits> { public: // These are the same methods in message_loop.h, but are guaranteed to either // get posted to the MessageLoop if it's still alive, or be deleted otherwise. @@ -42,7 +48,27 @@ class MessageLoopProxy : public base::RefCountedThreadSafe<MessageLoopProxy> { T* object) { return PostNonNestableTask(from_here, new ReleaseTask<T>(object)); } + + // Factory method for creating an implementation of MessageLoopProxy + // for the current thread. + static scoped_refptr<MessageLoopProxy> CreateForCurrentThread(); + + protected: + friend struct MessageLoopProxyTraits; + // Called when the proxy is about to be deleted. Subclasses can override this + // to provide deletion on specific threads. + virtual void OnDestruct() { + delete this; + } }; +struct MessageLoopProxyTraits { + static void Destruct(MessageLoopProxy* proxy) { + proxy->OnDestruct(); + } +}; + +} // namespace base + #endif // BASE_MESSAGE_LOOP_PROXY_H_ diff --git a/base/message_loop_proxy_impl.cc b/base/message_loop_proxy_impl.cc new file mode 100644 index 0000000..983a406 --- /dev/null +++ b/base/message_loop_proxy_impl.cc @@ -0,0 +1,101 @@ +// Copyright (c) 2010 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_impl.h" + +namespace base { + +MessageLoopProxyImpl::MessageLoopProxyImpl() + : target_message_loop_(MessageLoop::current()) { + target_message_loop_->AddDestructionObserver(this); +} + +MessageLoopProxyImpl::~MessageLoopProxyImpl() { + AutoLock lock(message_loop_lock_); + // If the target message loop still exists, the d'tor WILL execute on the + // target loop. + if (target_message_loop_) { + DCHECK(MessageLoop::current() == target_message_loop_); + MessageLoop::current()->RemoveDestructionObserver(this); + } +} + + // MessageLoopProxy implementation +bool MessageLoopProxyImpl::PostTask(const tracked_objects::Location& from_here, + Task* task) { + return PostTaskHelper(from_here, task, 0, true); +} + +bool MessageLoopProxyImpl::PostDelayedTask( + const tracked_objects::Location& from_here, Task* task, int64 delay_ms) { + return PostTaskHelper(from_here, task, delay_ms, true); +} + +bool MessageLoopProxyImpl::PostNonNestableTask( + const tracked_objects::Location& from_here, Task* task) { + return PostTaskHelper(from_here, task, 0, false); +} + +bool MessageLoopProxyImpl::PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms) { + return PostTaskHelper(from_here, task, delay_ms, false); +} + +bool MessageLoopProxyImpl::BelongsToCurrentThread() { + AutoLock lock(message_loop_lock_); + return (target_message_loop_ && + (MessageLoop::current() == target_message_loop_)); +} + +bool MessageLoopProxyImpl::PostTaskHelper( + const tracked_objects::Location& from_here, Task* task, int64 delay_ms, + bool nestable) { + bool ret = false; + { + AutoLock lock(message_loop_lock_); + if (target_message_loop_) { + if (nestable) { + target_message_loop_->PostDelayedTask(from_here, task, delay_ms); + } else { + target_message_loop_->PostNonNestableDelayedTask(from_here, task, + delay_ms); + } + ret = true; + } + } + if (!ret) + delete task; + return ret; +} + +void MessageLoopProxyImpl::OnDestruct() { + 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; +} + +// MessageLoop::DestructionObserver implementation +void MessageLoopProxyImpl::WillDestroyCurrentMessageLoop() { + AutoLock lock(message_loop_lock_); + target_message_loop_ = NULL; +} + +scoped_refptr<MessageLoopProxy> +MessageLoopProxy::CreateForCurrentThread() { + scoped_refptr<MessageLoopProxy> ret = new MessageLoopProxyImpl(); + return ret; +} + +} // namespace base + diff --git a/base/message_loop_proxy_impl.h b/base/message_loop_proxy_impl.h new file mode 100644 index 0000000..f895532 --- /dev/null +++ b/base/message_loop_proxy_impl.h @@ -0,0 +1,61 @@ +// Copyright (c) 2010 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_PROXY_IMPL_H_ +#define BASE_MESSAGE_LOOP_PROXY_IMPL_H_ + +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" + +namespace base { + +// A stock implementation of MessageLoopProxy that takes in a MessageLoop +// and keeps track of its lifetime using the MessageLoop DestructionObserver. +// For now a MessageLoopProxyImpl can only be created for the current thread. +class MessageLoopProxyImpl : public MessageLoopProxy, + public MessageLoop::DestructionObserver { + public: + ~MessageLoopProxyImpl(); + + // MessageLoopProxy implementation + virtual bool PostTask(const tracked_objects::Location& from_here, + Task* task); + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + Task* task, int64 delay_ms); + virtual bool PostNonNestableTask(const tracked_objects::Location& from_here, + Task* task); + virtual bool PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms); + virtual bool BelongsToCurrentThread(); + +// MessageLoop::DestructionObserver implementation + void WillDestroyCurrentMessageLoop(); + + protected: + // Override OnDestruct so that we can delete the object on the target message + // loop if it still exists. + virtual void OnDestruct(); + + private: + MessageLoopProxyImpl(); + bool PostTaskHelper(const tracked_objects::Location& from_here, + Task* task, int64 delay_ms, bool nestable); + + // For the factory method to work + friend class MessageLoopProxy; + + // The lock that protects access to target_message_loop_. + Lock message_loop_lock_; + MessageLoop* target_message_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessageLoopProxyImpl); +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_PROXY_IMPL_H_ + diff --git a/base/message_loop_proxy_impl_unittest.cc b/base/message_loop_proxy_impl_unittest.cc new file mode 100644 index 0000000..5fe341c --- /dev/null +++ b/base/message_loop_proxy_impl_unittest.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2010 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.h" +#include "base/message_loop_proxy_impl.h" +#include "base/thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + + +class MessageLoopProxyImplTest : public testing::Test { + public: + void Release() { + AssertOnIOThread(); + Quit(); + } + + void Quit() { + loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); + } + + void AssertOnIOThread() { + ASSERT_TRUE(io_thread_->message_loop_proxy()->BelongsToCurrentThread()); + } + + void AssertOnFileThread() { + ASSERT_TRUE(file_thread_->message_loop_proxy()->BelongsToCurrentThread()); + } + + protected: + virtual void SetUp() { + io_thread_.reset(new base::Thread("MessageLoopProxyImplTest_IO")); + file_thread_.reset(new base::Thread("MessageLoopProxyImplTest_File")); + io_thread_->Start(); + file_thread_->Start(); + } + + virtual void TearDown() { + io_thread_->Stop(); + file_thread_->Stop(); + } + + static void BasicFunction(MessageLoopProxyImplTest* test) { + test->AssertOnFileThread(); + test->Quit(); + } + + class DummyTask : public Task { + public: + explicit DummyTask(bool* deleted) : deleted_(deleted) { } + ~DummyTask() { + *deleted_ = true; + } + + void Run() { + FAIL(); + } + + private: + bool* deleted_; + }; + + class DeletedOnFile { + public: + explicit DeletedOnFile(MessageLoopProxyImplTest* test) : test_(test) {} + + ~DeletedOnFile() { + test_->AssertOnFileThread(); + test_->Quit(); + } + + private: + MessageLoopProxyImplTest* test_; + }; + + scoped_ptr<base::Thread> io_thread_; + scoped_ptr<base::Thread> file_thread_; + + private: + MessageLoop loop_; +}; + + +TEST_F(MessageLoopProxyImplTest, PostTask) { + EXPECT_TRUE(file_thread_->message_loop_proxy()->PostTask( + FROM_HERE, NewRunnableFunction(&BasicFunction, this))); + MessageLoop::current()->Run(); +} + +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, PostTaskAfterThreadExits) { + scoped_ptr<base::Thread> test_thread( + new base::Thread("MessageLoopProxyImplTest_Dummy")); + test_thread->Start(); + scoped_refptr<base::MessageLoopProxy> message_loop_proxy = + test_thread->message_loop_proxy(); + test_thread->Stop(); + + bool deleted = false; + bool ret = message_loop_proxy->PostTask( + FROM_HERE, new DummyTask(&deleted)); + EXPECT_FALSE(ret); + EXPECT_TRUE(deleted); +} + +TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadIsDeleted) { + scoped_refptr<base::MessageLoopProxy> message_loop_proxy; + { + scoped_ptr<base::Thread> test_thread( + new base::Thread("MessageLoopProxyImplTest_Dummy")); + test_thread->Start(); + message_loop_proxy = test_thread->message_loop_proxy(); + } + bool deleted = false; + bool ret = message_loop_proxy->PostTask(FROM_HERE, new DummyTask(&deleted)); + EXPECT_FALSE(ret); + EXPECT_TRUE(deleted); +} + diff --git a/base/thread.cc b/base/thread.cc index 05323a2b..a81fcb4 100644 --- a/base/thread.cc +++ b/base/thread.cc @@ -144,6 +144,7 @@ void Thread::ThreadMain() { ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. message_loop.set_thread_name(name_); message_loop_ = &message_loop; + message_loop_proxy_ = MessageLoopProxy::CreateForCurrentThread(); // Let the thread do extra initialization. // Let's do this before signaling we are started. @@ -163,6 +164,7 @@ void Thread::ThreadMain() { // We can't receive messages anymore. message_loop_ = NULL; + message_loop_proxy_ = NULL; } CleanUpAfterMessageLoopDestruction(); thread_id_ = 0; diff --git a/base/thread.h b/base/thread.h index 88bff7b..b5a87eb 100644 --- a/base/thread.h +++ b/base/thread.h @@ -8,6 +8,7 @@ #include <string> #include "base/message_loop.h" +#include "base/message_loop_proxy.h" #include "base/platform_thread.h" namespace base { @@ -104,6 +105,16 @@ class Thread : PlatformThread::Delegate { // MessageLoop* message_loop() const { return message_loop_; } + // Returns a MessageLoopProxy for this thread. Use the MessageLoopProxy's + // PostTask methods to execute code on the thread. This only returns + // non-null after a successful call to Start. After Stop has been called, + // this will return NULL. Callers can hold on to this even after the thread + // is gone. + // TODO(sanjeevr): Look into merging MessageLoop and MessageLoopProxy. + scoped_refptr<MessageLoopProxy> message_loop_proxy() { + return message_loop_proxy_; + } + // Set the name of this thread (for display in debugger too). const std::string &thread_name() { return name_; } @@ -162,6 +173,10 @@ class Thread : PlatformThread::Delegate { // by the created thread. MessageLoop* message_loop_; + // A MessageLoopProxy implementation that targets this thread. This can + // outlive the thread. + scoped_refptr<MessageLoopProxy> message_loop_proxy_; + // Our thread's ID. PlatformThreadId thread_id_; |