summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorsanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-06 18:34:24 +0000
committersanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-06 18:34:24 +0000
commit656475d275524893e4e9b1f02469fe470721a14e (patch)
tree76198770e24f0bea147c10a50ae2a3bf9c7f7274 /base
parent7e19edf7255b366b5e4b9b0bb77caf9842a37f1b (diff)
downloadchromium_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.gyp1
-rw-r--r--base/base.gypi2
-rw-r--r--base/message_loop_proxy.h28
-rw-r--r--base/message_loop_proxy_impl.cc101
-rw-r--r--base/message_loop_proxy_impl.h61
-rw-r--r--base/message_loop_proxy_impl_unittest.cc131
-rw-r--r--base/thread.cc2
-rw-r--r--base/thread.h15
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_;