diff options
-rw-r--r-- | base/SConscript | 2 | ||||
-rw-r--r-- | base/build/base.vcproj | 8 | ||||
-rw-r--r-- | base/build/base_unittests.vcproj | 4 | ||||
-rw-r--r-- | base/simple_thread.cc | 52 | ||||
-rw-r--r-- | base/simple_thread.h | 141 | ||||
-rw-r--r-- | base/simple_thread_unittest.cc | 99 |
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())); +} |