Create a simple abstraction to a native OS thread, mostly useful for unittesting when you don't want a message loop.

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1217 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: 578c7cc2380e32ca348b940be71f6b5e3e6c3fea
diff --git a/base/SConscript b/base/SConscript
index a7fc566..88c2020 100644
--- a/base/SConscript
+++ b/base/SConscript
@@ -77,6 +77,7 @@
     'revocable_store.cc',
     'ref_counted.cc',
     'sha2.cc',
+    'simple_thread.cc',
     'stats_table.cc',
     'string_escape.cc',
     'string_piece.cc',
@@ -255,6 +256,7 @@
     'ref_counted_unittest.cc',
     'run_all_unittests.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 7864bd0..ad52b1d 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 2c7908a..29997f4 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/platform_thread.h b/base/platform_thread.h
index 1274f0f..542716b 100644
--- a/base/platform_thread.h
+++ b/base/platform_thread.h
@@ -32,6 +32,8 @@
 
 #include "base/basictypes.h"
 
+// PlatformThreadHandle should be a numeric type on all platforms, so it can
+// be initialized to 0.  However, 0 cannot be assumed to be an invalid handle.
 #if defined(OS_WIN)
 typedef void* PlatformThreadHandle;  // HANDLE
 #elif defined(OS_POSIX)
@@ -68,14 +70,16 @@
   // that the default stack size should be used.  Upon success,
   // |*thread_handle| will be assigned a handle to the newly created thread,
   // and |delegate|'s ThreadMain method will be executed on the newly created
-  // thread.  When you are done with the thread handle, you must call Join to
+  // thread.
+  // NOTE: When you are done with the thread handle, you must call Join to
   // release system resources associated with the thread.  You must ensure that
   // the Delegate object outlives the thread.
   static bool Create(size_t stack_size, Delegate* delegate,
                      PlatformThreadHandle* thread_handle);
 
   // Joins with a thread created via the Create function.  This function blocks
-  // the caller until the designated thread exits.
+  // the caller until the designated thread exits.  This will invalidate
+  // |thread_handle|.
   static void Join(PlatformThreadHandle thread_handle);
 
  private:
diff --git a/base/simple_thread.cc b/base/simple_thread.cc
new file mode 100644
index 0000000..797843d
--- /dev/null
+++ b/base/simple_thread.cc
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/simple_thread.h"
+
+#include "base/call_wrapper.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(tid_, 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 CallWrapperSimpleThread::Run() {
+  DCHECK(wrapper_) << "Tried to call Run without a wrapper (called twice?)";
+  wrapper_->Run();
+  wrapper_ = NULL;
+}
+
+CallWrapperSimpleThread::~CallWrapperSimpleThread() {
+  DCHECK(!wrapper_) << "CallWrapper was never released.";
+}
+
+}  // namespace base
diff --git a/base/simple_thread.h b/base/simple_thread.h
new file mode 100644
index 0000000..fe9a136
--- /dev/null
+++ b/base/simple_thread.h
@@ -0,0 +1,162 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// 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 CallWrapperSimpleThread, which will create
+// a new thread, and execute the CallWrapper in this new thread until it has
+// completed, exiting the thread.  See call_wrapper.h for that interface.
+//
+// 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).
+//
+// 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:
+//   CallWrapper* wrapper = NewMethodCallWrapper(obj, &Foo::Main);
+//   scoped_ptr<SimpleThread> thread(new CallWrapperSimpleThread(wrapper));
+//   thread->Start();
+//   // Start will return after the Thread has been successfully started and
+//   // initialized.  The newly created thread will invoke obj->Main, and run
+//   // until it returns.  The CallWrapper will then delete itself.
+//   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.  In this example the scoper will destroy the object.
+
+#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"
+
+class CallWrapper;
+
+namespace base {
+
+// This is the base SimpleThread.  You can derive from it and implement the
+// virtual Run method, or you can use the CallWrapperSimpleThread 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 Options& options, const std::string& name_prefix)
+      : name_prefix_(name_prefix), name_(name_prefix_), options_(options),
+        thread_(0), event_(true, false), tid_(0), joined_(false) { }
+
+  SimpleThread()
+      : name_prefix_("unnamed"), name_(name_prefix_),
+        thread_(0), 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 CallWrapperSimpleThread : public SimpleThread {
+ public:
+  typedef SimpleThread::Options Options;
+
+  explicit CallWrapperSimpleThread(CallWrapper* wrapper)
+      : SimpleThread(), wrapper_(wrapper) { }
+
+  CallWrapperSimpleThread(CallWrapper* wrapper,
+                          const Options& options,
+                          const std::string& name_prefix)
+      : SimpleThread(options, name_prefix), wrapper_(wrapper) { }
+
+  virtual ~CallWrapperSimpleThread();
+  virtual void Run();
+ private:
+  CallWrapper* wrapper_;
+};
+
+}  // 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..c894637
--- /dev/null
+++ b/base/simple_thread_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <vector>
+
+#include "base/call_wrapper.h"
+#include "base/scoped_ptr.h"
+#include "base/simple_thread.h"
+#include "base/string_util.h"
+#include "base/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void SetInt(int* p, int x) {
+  *p = x;
+}
+
+void SignalEvent(base::WaitableEvent* event) {
+  EXPECT_FALSE(event->IsSignaled());
+  event->Signal();
+  EXPECT_TRUE(event->IsSignaled());
+}
+
+}  // namespace
+
+TEST(SimpleThreadTest, CreateAndJoin) {
+  int stack_int = 0;
+
+  CallWrapper* wrapper = NewFunctionCallWrapper(SetInt, &stack_int, 7);
+  EXPECT_EQ(0, stack_int);
+  scoped_ptr<base::SimpleThread> thread(
+      new base::CallWrapperSimpleThread(wrapper));
+  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);
+
+  scoped_ptr<base::SimpleThread> thread(new base::CallWrapperSimpleThread(
+      NewFunctionCallWrapper(SignalEvent, &event)));
+
+  EXPECT_FALSE(event.IsSignaled());
+  thread->Start();
+  event.Wait();
+  EXPECT_TRUE(event.IsSignaled());
+  thread->Join();
+}
+
+TEST(SimpleThreadTest, Named) {
+  base::WaitableEvent event(true, false);
+
+  base::SimpleThread::Options options;
+  scoped_ptr<base::SimpleThread> thread(new base::CallWrapperSimpleThread(
+      NewFunctionCallWrapper(SignalEvent, &event), options, "testy"));
+  EXPECT_EQ(thread->name_prefix(), "testy");
+  EXPECT_FALSE(event.IsSignaled());
+
+  thread->Start();
+  EXPECT_EQ(thread->name_prefix(), "testy");
+  EXPECT_EQ(thread->name(), std::string("testy/") + 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(), "testy");
+  EXPECT_EQ(thread->name(), std::string("testy/") + IntToString(thread->tid()));
+}