summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordeanm@google.com <deanm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-29 07:54:54 +0000
committerdeanm@google.com <deanm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-29 07:54:54 +0000
commit8a2cebb5dc1393c74c1921852369cc45e291914e (patch)
treefd6bf76b0f7d6b3469873adb2983bb9fdb80451c
parent7a8475c6da19434a3bae6783279fd9e9ad79fc47 (diff)
downloadchromium_src-8a2cebb5dc1393c74c1921852369cc45e291914e.zip
chromium_src-8a2cebb5dc1393c74c1921852369cc45e291914e.tar.gz
chromium_src-8a2cebb5dc1393c74c1921852369cc45e291914e.tar.bz2
Bring back SimpleThread, but with a Delegate interface.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1519 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/SConscript2
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/simple_thread.cc52
-rw-r--r--base/simple_thread.h141
-rw-r--r--base/simple_thread_unittest.cc99
6 files changed, 306 insertions, 0 deletions
diff --git a/base/SConscript b/base/SConscript
index 27dcab6..46f0cbf 100644
--- a/base/SConscript
+++ b/base/SConscript
@@ -77,6 +77,7 @@ input_files = [
'ref_counted.cc',
'revocable_store.cc',
'sha2.cc',
+ 'simple_thread.cc',
'stats_table.cc',
'string_escape.cc',
'string_piece.cc',
@@ -262,6 +263,7 @@ test_files = [
'run_all_unittests.cc',
'scoped_ptr_unittest.cc',
'sha2_unittest.cc',
+ 'simple_thread_unittest.cc',
'singleton_unittest.cc',
'stack_container_unittest.cc',
'string_escape_unittest.cc',
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
index a0e0890..9d2b459 100644
--- a/base/build/base.vcproj
+++ b/base/build/base.vcproj
@@ -614,6 +614,14 @@
>
</File>
<File
+ RelativePath="..\simple_thread.h"
+ >
+ </File>
+ <File
+ RelativePath="..\simple_thread.cc"
+ >
+ </File>
+ <File
RelativePath="..\singleton.h"
>
</File>
diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj
index 53da8e1..caf59c1 100644
--- a/base/build/base_unittests.vcproj
+++ b/base/build/base_unittests.vcproj
@@ -268,6 +268,10 @@
>
</File>
<File
+ RelativePath="..\simple_thread_unittest.cc"
+ >
+ </File>
+ <File
RelativePath="..\singleton_unittest.cc"
>
</File>
diff --git a/base/simple_thread.cc b/base/simple_thread.cc
new file mode 100644
index 0000000..fa9a723
--- /dev/null
+++ b/base/simple_thread.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2006-2008 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/simple_thread.h"
+
+#include "base/waitable_event.h"
+#include "base/logging.h"
+#include "base/platform_thread.h"
+#include "base/string_util.h"
+
+namespace base {
+
+void SimpleThread::Start() {
+ DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times.";
+ bool success = PlatformThread::Create(options_.stack_size(), this, &thread_);
+ CHECK(success);
+ event_.Wait(); // Wait for the thread to complete initialization.
+}
+
+void SimpleThread::Join() {
+ DCHECK(HasBeenStarted()) << "Tried to Join a never-started thread.";
+ DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times.";
+ PlatformThread::Join(thread_);
+ joined_ = true;
+}
+
+void SimpleThread::ThreadMain() {
+ tid_ = PlatformThread::CurrentId();
+ // Construct our full name of the form "name_prefix_/TID".
+ name_.push_back('/');
+ name_.append(IntToString(tid_));
+ PlatformThread::SetName(name_.c_str());
+
+ // We've initialized our new thread, signal that we're done to Start().
+ event_.Signal();
+
+ Run();
+}
+
+SimpleThread::~SimpleThread() {
+ DCHECK(HasBeenStarted()) << "SimpleThread was never started.";
+ DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed.";
+}
+
+void DelegateSimpleThread::Run() {
+ DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)";
+ delegate_->Run();
+ delegate_ = NULL;
+}
+
+} // namespace base
diff --git a/base/simple_thread.h b/base/simple_thread.h
new file mode 100644
index 0000000..1c7a82b
--- /dev/null
+++ b/base/simple_thread.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2006-2008 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.
+
+// WARNING: You should probably be using Thread (thread.h) instead. Thread is
+// Chrome's message-loop based Thread abstraction, and if you are a
+// thread running in the browser, there will likely be assumptions
+// that your thread will have an associated message loop.
+//
+// This is a simple thread interface that backs to a native operating system
+// thread. You should use this only when you want a thread that does not have
+// an associated MessageLoop. Unittesting is the best example of this.
+//
+// The simplest interface to use is DelegateSimpleThread, which will create
+// a new thread, and execute the Delegate's virtual Run() in this new thread
+// until it has completed, exiting the thread.
+//
+// NOTE: You *MUST* call Join on the thread to clean up the underlying thread
+// resources. You are also responsible for destructing the SimpleThread object.
+// It is invalid to destroy a SimpleThread while it is running, or without
+// Start() having been called (and a thread never created). The Delegate
+// object should live as long as a DelegateSimpleThread.
+//
+// Thread Safety: A SimpleThread is not completely thread safe. It is safe to
+// access it from the creating thread or from the newly created thread. This
+// implies that the creator thread should be the thread that calls Join.
+//
+// Example:
+// class MyThreadRunner : public DelegateSimpleThread::Delegate { ... };
+// MyThreadRunner runner;
+// DelegateSimpleThread thread(&runner, "good_name_here");
+// thread.Start();
+// // Start will return after the Thread has been successfully started and
+// // initialized. The newly created thread will invoke runner->Run(), and
+// // run until it returns.
+// thread.Join(); // Wait until the thread has exited. You *MUST* Join!
+// // The SimpleThread object is still valid, however you may not call Join
+// // or Start again.
+
+#ifndef BASE_SIMPLE_THREAD_H_
+#define BASE_SIMPLE_THREAD_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/waitable_event.h"
+#include "base/platform_thread.h"
+
+namespace base {
+
+// This is the base SimpleThread. You can derive from it and implement the
+// virtual Run method, or you can use the DelegateSimpleThread interface.
+class SimpleThread : public PlatformThread::Delegate {
+ public:
+ class Options {
+ public:
+ Options() : stack_size_(0) { }
+ ~Options() { }
+
+ // We use the standard compiler-supplied copy constructor.
+
+ // A custom stack size, or 0 for the system default.
+ void set_stack_size(size_t size) { stack_size_ = size; }
+ size_t stack_size() const { return stack_size_; }
+ private:
+ size_t stack_size_;
+ };
+
+ // Create a SimpleThread. |options| should be used to manage any specific
+ // configuration involving the thread creation and management.
+ // Every thread has a name, in the form of |name_prefix|/TID, for example
+ // "my_thread/321". The thread will not be created until Start() is called.
+ SimpleThread(const std::string& name_prefix)
+ : name_prefix_(name_prefix), name_(name_prefix),
+ thread_(), event_(true, false), tid_(0), joined_(false) { }
+ SimpleThread(const std::string& name_prefix, const Options& options)
+ : name_prefix_(name_prefix), name_(name_prefix), options_(options),
+ thread_(), event_(true, false), tid_(0), joined_(false) { }
+
+ virtual ~SimpleThread();
+
+ virtual void Start();
+ virtual void Join();
+
+ // We follow the PlatformThread Delegate interface.
+ virtual void ThreadMain();
+
+ // Subclasses should override the Run method.
+ virtual void Run() = 0;
+
+ // Return the thread name prefix, or "unnamed" if none was supplied.
+ std::string name_prefix() { return name_prefix_; }
+
+ // Return the completed name including TID, only valid after Start().
+ std::string name() { return name_; }
+
+ // Return the thread id, only valid after Start().
+ int tid() { return tid_; }
+
+ // Return True if Start() has ever been called.
+ bool HasBeenStarted() { return event_.IsSignaled(); }
+
+ // Return True if Join() has evern been called.
+ bool HasBeenJoined() { return joined_; }
+
+ private:
+ const std::string name_prefix_;
+ std::string name_;
+ const Options options_;
+ PlatformThreadHandle thread_; // PlatformThread handle, invalid after Join!
+ WaitableEvent event_; // Signaled if Start() was ever called.
+ int tid_; // The backing thread's id.
+ bool joined_; // True if Join has been called.
+};
+
+class DelegateSimpleThread : public SimpleThread {
+ public:
+ class Delegate {
+ public:
+ Delegate() { }
+ virtual ~Delegate() { }
+ virtual void Run() = 0;
+ };
+
+ DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix)
+ : SimpleThread(name_prefix), delegate_(delegate) { }
+ DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix,
+ const Options& options)
+ : SimpleThread(name_prefix, options), delegate_(delegate) { }
+
+ virtual ~DelegateSimpleThread() { }
+ virtual void Run();
+ private:
+ Delegate* delegate_;
+};
+
+} // namespace base
+
+#endif // BASE_SIMPLE_THREAD_H_
diff --git a/base/simple_thread_unittest.cc b/base/simple_thread_unittest.cc
new file mode 100644
index 0000000..3e55858
--- /dev/null
+++ b/base/simple_thread_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2006-2008 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/simple_thread.h"
+#include "base/string_util.h"
+#include "base/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SetIntRunner : public base::DelegateSimpleThread::Delegate {
+ public:
+ SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { }
+ ~SetIntRunner() { }
+
+ virtual void Run() {
+ *ptr_ = val_;
+ }
+
+ private:
+ int* ptr_;
+ int val_;
+};
+
+class WaitEventRunner : public base::DelegateSimpleThread::Delegate {
+ public:
+ WaitEventRunner(base::WaitableEvent* event) : event_(event) { }
+ ~WaitEventRunner() { }
+
+ virtual void Run() {
+ EXPECT_FALSE(event_->IsSignaled());
+ event_->Signal();
+ EXPECT_TRUE(event_->IsSignaled());
+ }
+ private:
+ base::WaitableEvent* event_;
+};
+
+} // namespace
+
+TEST(SimpleThreadTest, CreateAndJoin) {
+ int stack_int = 0;
+
+ SetIntRunner runner(&stack_int, 7);
+ EXPECT_EQ(0, stack_int);
+
+ base::DelegateSimpleThread thread(&runner, "int_setter");
+ EXPECT_FALSE(thread.HasBeenStarted());
+ EXPECT_FALSE(thread.HasBeenJoined());
+ EXPECT_EQ(0, stack_int);
+
+ thread.Start();
+ EXPECT_TRUE(thread.HasBeenStarted());
+ EXPECT_FALSE(thread.HasBeenJoined());
+
+ thread.Join();
+ EXPECT_TRUE(thread.HasBeenStarted());
+ EXPECT_TRUE(thread.HasBeenJoined());
+ EXPECT_EQ(7, stack_int);
+}
+
+TEST(SimpleThreadTest, WaitForEvent) {
+ // Create a thread, and wait for it to signal us.
+ base::WaitableEvent event(true, false);
+
+ WaitEventRunner runner(&event);
+ base::DelegateSimpleThread thread(&runner, "event_waiter");
+
+ EXPECT_FALSE(event.IsSignaled());
+ thread.Start();
+ event.Wait();
+ EXPECT_TRUE(event.IsSignaled());
+ thread.Join();
+}
+
+TEST(SimpleThreadTest, NamedWithOptions) {
+ base::WaitableEvent event(true, false);
+
+ WaitEventRunner runner(&event);
+ base::SimpleThread::Options options;
+ base::DelegateSimpleThread thread(&runner, "event_waiter", options);
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_FALSE(event.IsSignaled());
+
+ thread.Start();
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_EQ(thread.name(), std::string("event_waiter/") +
+ IntToString(thread.tid()));
+ event.Wait();
+
+ EXPECT_TRUE(event.IsSignaled());
+ thread.Join();
+
+ // We keep the name and tid, even after the thread is gone.
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_EQ(thread.name(), std::string("event_waiter/") +
+ IntToString(thread.tid()));
+}