diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-31 20:02:16 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-31 20:02:16 +0000 |
commit | ce072a7181ea5d58133e33654133236f5d9f5551 (patch) | |
tree | 1b1c903fec3fd27038cd17cb4ae9ca17d3736e40 /base/threading | |
parent | a8e2058011129cbef38bf89834ee01715556b392 (diff) | |
download | chromium_src-ce072a7181ea5d58133e33654133236f5d9f5551.zip chromium_src-ce072a7181ea5d58133e33654133236f5d9f5551.tar.gz chromium_src-ce072a7181ea5d58133e33654133236f5d9f5551.tar.bz2 |
Move platform_thread to base/threading and put in the base namespace. I left a
stub and "using" declarations in the old location to avoid having to change the
entire project at once.
TEST=it compiles
BUG=none
Review URL: http://codereview.chromium.org/6001010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70342 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/threading')
-rw-r--r-- | base/threading/platform_thread.h | 99 | ||||
-rw-r--r-- | base/threading/platform_thread_mac.mm | 54 | ||||
-rw-r--r-- | base/threading/platform_thread_posix.cc | 225 | ||||
-rw-r--r-- | base/threading/platform_thread_unittest.cc | 106 | ||||
-rw-r--r-- | base/threading/platform_thread_win.cc | 147 | ||||
-rw-r--r-- | base/threading/simple_thread.cc | 2 | ||||
-rw-r--r-- | base/threading/simple_thread.h | 2 | ||||
-rw-r--r-- | base/threading/thread_checker.cc | 38 | ||||
-rw-r--r-- | base/threading/thread_checker.h | 73 | ||||
-rw-r--r-- | base/threading/thread_checker_unittest.cc | 146 | ||||
-rw-r--r-- | base/threading/watchdog.cc | 2 | ||||
-rw-r--r-- | base/threading/watchdog.h | 2 | ||||
-rw-r--r-- | base/threading/watchdog_unittest.cc | 2 | ||||
-rw-r--r-- | base/threading/worker_pool_posix.cc | 2 | ||||
-rw-r--r-- | base/threading/worker_pool_posix.h | 2 | ||||
-rw-r--r-- | base/threading/worker_pool_posix_unittest.cc | 2 |
16 files changed, 896 insertions, 8 deletions
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h new file mode 100644 index 0000000..0a3c75d7 --- /dev/null +++ b/base/threading/platform_thread.h @@ -0,0 +1,99 @@ +// 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. + +// WARNING: You should *NOT* be using this class directly. PlatformThread is +// the low-level platform-specific abstraction to the OS's threading interface. +// You should instead be using a message-loop driven Thread, see thread.h. + +#ifndef BASE_THREADING_PLATFORM_THREAD_H_ +#define BASE_THREADING_PLATFORM_THREAD_H_ +#pragma once + +#include "base/basictypes.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(OS_POSIX) +#include <pthread.h> +#if defined(OS_MACOSX) +#include <mach/mach.h> +#else // OS_POSIX && !OS_MACOSX +#include <unistd.h> +#endif +#endif + +namespace base { + +// PlatformThreadHandle should not be assumed to be a numeric type, since the +// standard intends to allow pthread_t to be a structure. This means you +// should not initialize it to a value, like 0. If it's a member variable, the +// constructor can safely "value initialize" using () in the initializer list. +#if defined(OS_WIN) +typedef DWORD PlatformThreadId; +typedef void* PlatformThreadHandle; // HANDLE +const PlatformThreadHandle kNullThreadHandle = NULL; +#elif defined(OS_POSIX) +typedef pthread_t PlatformThreadHandle; +const PlatformThreadHandle kNullThreadHandle = 0; +#if defined(OS_MACOSX) +typedef mach_port_t PlatformThreadId; +#else // OS_POSIX && !OS_MACOSX +typedef pid_t PlatformThreadId; +#endif +#endif + +const PlatformThreadId kInvalidThreadId = 0; + +// A namespace for low-level thread functions. +class PlatformThread { + public: + // Implement this interface to run code on a background thread. Your + // ThreadMain method will be called on the newly created thread. + class Delegate { + public: + virtual ~Delegate() {} + virtual void ThreadMain() = 0; + }; + + // Gets the current thread id, which may be useful for logging purposes. + static PlatformThreadId CurrentId(); + + // Yield the current thread so another thread can be scheduled. + static void YieldCurrentThread(); + + // Sleeps for the specified duration (units are milliseconds). + static void Sleep(int duration_ms); + + // Sets the thread name visible to a debugger. This has no effect otherwise. + static void SetName(const char* name); + + // Creates a new thread. The |stack_size| parameter can be 0 to indicate + // 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. + // 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); + + // CreateNonJoinable() does the same thing as Create() except the thread + // cannot be Join()'d. Therefore, it also does not output a + // PlatformThreadHandle. + static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); + + // Joins with a thread created via the Create function. This function blocks + // the caller until the designated thread exits. This will invalidate + // |thread_handle|. + static void Join(PlatformThreadHandle thread_handle); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); +}; + +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_H_ diff --git a/base/threading/platform_thread_mac.mm b/base/threading/platform_thread_mac.mm new file mode 100644 index 0000000..d77307c5 --- /dev/null +++ b/base/threading/platform_thread_mac.mm @@ -0,0 +1,54 @@ +// 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/threading/platform_thread.h" + +#import <Foundation/Foundation.h> +#include <dlfcn.h> + +#include "base/logging.h" + +namespace base { + +// If Cocoa is to be used on more than one thread, it must know that the +// application is multithreaded. Since it's possible to enter Cocoa code +// from threads created by pthread_thread_create, Cocoa won't necessarily +// be aware that the application is multithreaded. Spawning an NSThread is +// enough to get Cocoa to set up for multithreaded operation, so this is done +// if necessary before pthread_thread_create spawns any threads. +// +// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html +void InitThreading() { + static BOOL multithreaded = [NSThread isMultiThreaded]; + if (!multithreaded) { + // +[NSObject class] is idempotent. + [NSThread detachNewThreadSelector:@selector(class) + toTarget:[NSObject class] + withObject:nil]; + multithreaded = YES; + + DCHECK([NSThread isMultiThreaded]); + } +} + +// static +void PlatformThread::SetName(const char* name) { + // pthread_setname_np is only available in 10.6 or later, so test + // for it at runtime. + int (*dynamic_pthread_setname_np)(const char*); + *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + if (!dynamic_pthread_setname_np) + return; + + // Mac OS X does not expose the length limit of the name, so + // hardcode it. + const int kMaxNameLength = 63; + std::string shortened_name = std::string(name).substr(0, kMaxNameLength); + // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does. + // See http://crbug.com/47058 + dynamic_pthread_setname_np(shortened_name.c_str()); +} + +} // namespace base diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc new file mode 100644 index 0000000..0ef4990 --- /dev/null +++ b/base/threading/platform_thread_posix.cc @@ -0,0 +1,225 @@ +// 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/threading/platform_thread.h" + +#include <errno.h> +#include <sched.h> + +#include "base/logging.h" +#include "base/safe_strerror_posix.h" +#include "base/scoped_ptr.h" +#include "base/thread_restrictions.h" + +#if defined(OS_MACOSX) +#include <mach/mach.h> +#include <sys/resource.h> +#include <algorithm> +#endif + +#if defined(OS_LINUX) +#include <dlfcn.h> +#include <sys/prctl.h> +#include <sys/syscall.h> +#include <unistd.h> +#endif + +#if defined(OS_NACL) +#include <sys/nacl_syscalls.h> +#endif + +namespace base { + +#if defined(OS_MACOSX) +void InitThreading(); +#endif + +namespace { + +struct ThreadParams { + PlatformThread::Delegate* delegate; + bool joinable; +}; + +void* ThreadFunc(void* params) { + ThreadParams* thread_params = static_cast<ThreadParams*>(params); + PlatformThread::Delegate* delegate = thread_params->delegate; + if (!thread_params->joinable) + base::ThreadRestrictions::SetSingletonAllowed(false); + delete thread_params; + delegate->ThreadMain(); + return NULL; +} + +bool CreateThread(size_t stack_size, bool joinable, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle) { +#if defined(OS_MACOSX) + base::InitThreading(); +#endif // OS_MACOSX + + bool success = false; + pthread_attr_t attributes; + pthread_attr_init(&attributes); + + // Pthreads are joinable by default, so only specify the detached attribute if + // the thread should be non-joinable. + if (!joinable) { + pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); + } + +#if defined(OS_MACOSX) + // The Mac OS X default for a pthread stack size is 512kB. + // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses + // DEFAULT_STACK_SIZE for this purpose. + // + // 512kB isn't quite generous enough for some deeply recursive threads that + // otherwise request the default stack size by specifying 0. Here, adopt + // glibc's behavior as on Linux, which is to use the current stack size + // limit (ulimit -s) as the default stack size. See + // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To + // avoid setting the limit below the Mac OS X default or the minimum usable + // stack size, these values are also considered. If any of these values + // can't be determined, or if stack size is unlimited (ulimit -s unlimited), + // stack_size is left at 0 to get the system default. + // + // Mac OS X normally only applies ulimit -s to the main thread stack. On + // contemporary OS X and Linux systems alike, this value is generally 8MB + // or in that neighborhood. + if (stack_size == 0) { + size_t default_stack_size; + struct rlimit stack_rlimit; + if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 && + getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 && + stack_rlimit.rlim_cur != RLIM_INFINITY) { + stack_size = std::max(std::max(default_stack_size, + static_cast<size_t>(PTHREAD_STACK_MIN)), + static_cast<size_t>(stack_rlimit.rlim_cur)); + } + } +#endif // OS_MACOSX + + if (stack_size > 0) + pthread_attr_setstacksize(&attributes, stack_size); + + ThreadParams* params = new ThreadParams; + params->delegate = delegate; + params->joinable = joinable; + success = !pthread_create(thread_handle, &attributes, ThreadFunc, params); + + pthread_attr_destroy(&attributes); + if (!success) + delete params; + return success; +} + +} // namespace + +// static +PlatformThreadId PlatformThread::CurrentId() { + // Pthreads doesn't have the concept of a thread ID, so we have to reach down + // into the kernel. +#if defined(OS_MACOSX) + return mach_thread_self(); +#elif defined(OS_LINUX) + return syscall(__NR_gettid); +#elif defined(OS_FREEBSD) + // TODO(BSD): find a better thread ID + return reinterpret_cast<int64>(pthread_self()); +#elif defined(OS_NACL) + return pthread_self(); +#endif +} + +// static +void PlatformThread::YieldCurrentThread() { + sched_yield(); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + struct timespec sleep_time, remaining; + + // Contains the portion of duration_ms >= 1 sec. + sleep_time.tv_sec = duration_ms / 1000; + duration_ms -= sleep_time.tv_sec * 1000; + + // Contains the portion of duration_ms < 1 sec. + sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +// Linux SetName is currently disabled, as we need to distinguish between +// helper threads (where it's ok to make this call) and the main thread +// (where making this call renames our process, causing tools like killall +// to stop working). +#if 0 && defined(OS_LINUX) +// static +void PlatformThread::SetName(const char* name) { + // http://0pointer.de/blog/projects/name-your-threads.html + + // glibc recently added support for pthread_setname_np, but it's not + // commonly available yet. So test for it at runtime. + int (*dynamic_pthread_setname_np)(pthread_t, const char*); + *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + + if (dynamic_pthread_setname_np) { + // This limit comes from glibc, which gets it from the kernel + // (TASK_COMM_LEN). + const int kMaxNameLength = 15; + std::string shortened_name = std::string(name).substr(0, kMaxNameLength); + int err = dynamic_pthread_setname_np(pthread_self(), + shortened_name.c_str()); + if (err < 0) + LOG(ERROR) << "pthread_setname_np: " << safe_strerror(err); + } else { + // Implementing this function without glibc is simple enough. (We + // don't do the name length clipping as above because it will be + // truncated by the callee (see TASK_COMM_LEN above).) + int err = prctl(PR_SET_NAME, name); + if (err < 0) + PLOG(ERROR) << "prctl(PR_SET_NAME)"; + } +} +#elif defined(OS_MACOSX) +// Mac is implemented in platform_thread_mac.mm. +#else +// static +void PlatformThread::SetName(const char* name) { + // Leave it unimplemented. + + // (This should be relatively simple to implement for the BSDs; I + // just don't have one handy to test the code on.) +} +#endif // defined(OS_LINUX) + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + return CreateThread(stack_size, true /* joinable thread */, + delegate, thread_handle); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle unused; + + bool result = CreateThread(stack_size, false /* non-joinable thread */, + delegate, &unused); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + // Joining another thread may block the current thread for a long time, since + // the thread referred to by |thread_handle| may still be running long-lived / + // blocking tasks. + base::ThreadRestrictions::AssertIOAllowed(); + pthread_join(thread_handle, NULL); +} + +} // namespace base diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc new file mode 100644 index 0000000..4b49450 --- /dev/null +++ b/base/threading/platform_thread_unittest.cc @@ -0,0 +1,106 @@ +// 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/threading/platform_thread.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +// Trivial tests that thread runs and doesn't crash on create and join --------- + +class TrivialThread : public PlatformThread::Delegate { + public: + TrivialThread() : did_run_(false) {} + + virtual void ThreadMain() { + did_run_ = true; + } + + bool did_run() const { return did_run_; } + + private: + bool did_run_; + + DISALLOW_COPY_AND_ASSIGN(TrivialThread); +}; + +TEST(PlatformThreadTest, Trivial) { + TrivialThread thread; + PlatformThreadHandle handle = kNullThreadHandle; + + ASSERT_FALSE(thread.did_run()); + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); + PlatformThread::Join(handle); + ASSERT_TRUE(thread.did_run()); +} + +TEST(PlatformThreadTest, TrivialTimesTen) { + TrivialThread thread[10]; + PlatformThreadHandle handle[arraysize(thread)]; + + for (size_t n = 0; n < arraysize(thread); n++) + ASSERT_FALSE(thread[n].did_run()); + for (size_t n = 0; n < arraysize(thread); n++) + ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n])); + for (size_t n = 0; n < arraysize(thread); n++) + PlatformThread::Join(handle[n]); + for (size_t n = 0; n < arraysize(thread); n++) + ASSERT_TRUE(thread[n].did_run()); +} + +// Tests of basic thread functions --------------------------------------------- + +class FunctionTestThread : public TrivialThread { + public: + FunctionTestThread() : thread_id_(0) {} + + virtual void ThreadMain() { + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::YieldCurrentThread(); + PlatformThread::Sleep(50); + + TrivialThread::ThreadMain(); + } + + PlatformThreadId thread_id() const { return thread_id_; } + + private: + PlatformThreadId thread_id_; + + DISALLOW_COPY_AND_ASSIGN(FunctionTestThread); +}; + +TEST(PlatformThreadTest, Function) { + PlatformThreadId main_thread_id = PlatformThread::CurrentId(); + + FunctionTestThread thread; + PlatformThreadHandle handle = kNullThreadHandle; + + ASSERT_FALSE(thread.did_run()); + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); + PlatformThread::Join(handle); + ASSERT_TRUE(thread.did_run()); + EXPECT_NE(thread.thread_id(), main_thread_id); +} + +TEST(PlatformThreadTest, FunctionTimesTen) { + PlatformThreadId main_thread_id = PlatformThread::CurrentId(); + + FunctionTestThread thread[10]; + PlatformThreadHandle handle[arraysize(thread)]; + + for (size_t n = 0; n < arraysize(thread); n++) + ASSERT_FALSE(thread[n].did_run()); + for (size_t n = 0; n < arraysize(thread); n++) + ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n])); + for (size_t n = 0; n < arraysize(thread); n++) + PlatformThread::Join(handle[n]); + for (size_t n = 0; n < arraysize(thread); n++) { + ASSERT_TRUE(thread[n].did_run()); + EXPECT_NE(thread[n].thread_id(), main_thread_id); + } +} + +} // namespace base diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc new file mode 100644 index 0000000..10f3011 --- /dev/null +++ b/base/threading/platform_thread_win.cc @@ -0,0 +1,147 @@ +// 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/threading/platform_thread.h" + +#include "base/logging.h" +#include "base/thread_restrictions.h" +#include "base/win/windows_version.h" + +namespace base { + +namespace { + +// The information on how to set the thread name comes from +// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx +const DWORD kVCThreadNameException = 0x406D1388; + +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +struct ThreadParams { + PlatformThread::Delegate* delegate; + bool joinable; +}; + +DWORD __stdcall ThreadFunc(void* params) { + ThreadParams* thread_params = static_cast<ThreadParams*>(params); + PlatformThread::Delegate* delegate = thread_params->delegate; + if (!thread_params->joinable) + base::ThreadRestrictions::SetSingletonAllowed(false); + delete thread_params; + delegate->ThreadMain(); + return NULL; +} + +// CreateThreadInternal() matches PlatformThread::Create(), except that +// |out_thread_handle| may be NULL, in which case a non-joinable thread is +// created. +bool CreateThreadInternal(size_t stack_size, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* out_thread_handle) { + PlatformThreadHandle thread_handle; + unsigned int flags = 0; + if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) { + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; + } else { + stack_size = 0; + } + + ThreadParams* params = new ThreadParams; + params->delegate = delegate; + params->joinable = out_thread_handle != NULL; + + // Using CreateThread here vs _beginthreadex makes thread creation a bit + // faster and doesn't require the loader lock to be available. Our code will + // have to work running on CreateThread() threads anyway, since we run code + // on the Windows thread pool, etc. For some background on the difference: + // http://www.microsoft.com/msj/1099/win32/win321099.aspx + thread_handle = CreateThread( + NULL, stack_size, ThreadFunc, params, flags, NULL); + if (!thread_handle) { + delete params; + return false; + } + + if (out_thread_handle) + *out_thread_handle = thread_handle; + else + CloseHandle(thread_handle); + return true; +} + +} // namespace + +// static +PlatformThreadId PlatformThread::CurrentId() { + return GetCurrentThreadId(); +} + +// static +void PlatformThread::YieldCurrentThread() { + ::Sleep(0); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + ::Sleep(duration_ms); +} + +// static +void PlatformThread::SetName(const char* name) { + // The debugger needs to be around to catch the name in the exception. If + // there isn't a debugger, we are just needlessly throwing an exception. + if (!::IsDebuggerPresent()) + return; + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = CurrentId(); + info.dwFlags = 0; + + __try { + RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), + reinterpret_cast<DWORD_PTR*>(&info)); + } __except(EXCEPTION_CONTINUE_EXECUTION) { + } +} + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + DCHECK(thread_handle); + return CreateThreadInternal(stack_size, delegate, thread_handle); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + return CreateThreadInternal(stack_size, delegate, NULL); +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + DCHECK(thread_handle); + // TODO(willchan): Enable this check once I can get it to work for Windows + // shutdown. + // Joining another thread may block the current thread for a long time, since + // the thread referred to by |thread_handle| may still be running long-lived / + // blocking tasks. +#if 0 + base::ThreadRestrictions::AssertIOAllowed(); +#endif + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + DWORD result = WaitForSingleObject(thread_handle, INFINITE); + DCHECK_EQ(WAIT_OBJECT_0, result); + + CloseHandle(thread_handle); +} + +} // namespace base diff --git a/base/threading/simple_thread.cc b/base/threading/simple_thread.cc index df1953f..2b030f6 100644 --- a/base/threading/simple_thread.cc +++ b/base/threading/simple_thread.cc @@ -5,7 +5,7 @@ #include "base/threading/simple_thread.h" #include "base/logging.h" -#include "base/platform_thread.h" +#include "base/threading/platform_thread.h" #include "base/string_number_conversions.h" namespace base { diff --git a/base/threading/simple_thread.h b/base/threading/simple_thread.h index dbff3ae..c401e01 100644 --- a/base/threading/simple_thread.h +++ b/base/threading/simple_thread.h @@ -47,8 +47,8 @@ #include "base/basictypes.h" #include "base/lock.h" +#include "base/threading/platform_thread.h" #include "base/waitable_event.h" -#include "base/platform_thread.h" namespace base { diff --git a/base/threading/thread_checker.cc b/base/threading/thread_checker.cc new file mode 100644 index 0000000..28ba400 --- /dev/null +++ b/base/threading/thread_checker.cc @@ -0,0 +1,38 @@ +// 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/threading/thread_checker.h" + +// This code is only done in debug builds. +#ifndef NDEBUG + +namespace base { + +ThreadChecker::ThreadChecker() : valid_thread_id_(kInvalidThreadId) { + EnsureThreadIdAssigned(); +} + +ThreadChecker::~ThreadChecker() {} + +bool ThreadChecker::CalledOnValidThread() const { + EnsureThreadIdAssigned(); + AutoLock auto_lock(lock_); + return valid_thread_id_ == PlatformThread::CurrentId(); +} + +void ThreadChecker::DetachFromThread() { + AutoLock auto_lock(lock_); + valid_thread_id_ = kInvalidThreadId; +} + +void ThreadChecker::EnsureThreadIdAssigned() const { + AutoLock auto_lock(lock_); + if (valid_thread_id_ != kInvalidThreadId) + return; + valid_thread_id_ = PlatformThread::CurrentId(); +} + +} // namespace base + +#endif // NDEBUG diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h new file mode 100644 index 0000000..c0010fb --- /dev/null +++ b/base/threading/thread_checker.h @@ -0,0 +1,73 @@ +// 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_THREADING_THREAD_CHECKER_H_ +#define BASE_THREADING_THREAD_CHECKER_H_ +#pragma once + +#ifndef NDEBUG +#include "base/lock.h" +#include "base/threading/platform_thread.h" +#endif // NDEBUG + +namespace base { + +// Before using this class, please consider using NonThreadSafe as it +// makes it much easier to determine the nature of your class. +// +// A helper class used to help verify that some methods of a class are +// called from the same thread. One can inherit from this class and use +// CalledOnValidThread() to verify. +// +// Inheriting from class indicates that one must be careful when using the class +// with multiple threads. However, it is up to the class document to indicate +// how it can be used with threads. +// +// Example: +// class MyClass : public ThreadChecker { +// public: +// void Foo() { +// DCHECK(CalledOnValidThread()); +// ... (do stuff) ... +// } +// } +// +// In Release mode, CalledOnValidThread will always return true. +// +#ifndef NDEBUG +class ThreadChecker { + public: + ThreadChecker(); + ~ThreadChecker(); + + bool CalledOnValidThread() const; + + // Changes the thread that is checked for in CalledOnValidThread. This may + // be useful when an object may be created on one thread and then used + // exclusively on another thread. + void DetachFromThread(); + + private: + void EnsureThreadIdAssigned() const; + + mutable Lock lock_; + // This is mutable so that CalledOnValidThread can set it. + // It's guarded by |lock_|. + mutable PlatformThreadId valid_thread_id_; +}; +#else +// Do nothing in release mode. +class ThreadChecker { + public: + bool CalledOnValidThread() const { + return true; + } + + void DetachFromThread() {} +}; +#endif // NDEBUG + +} // namespace base + +#endif // BASE_THREADING_THREAD_CHECKER_H_ diff --git a/base/threading/thread_checker_unittest.cc b/base/threading/thread_checker_unittest.cc new file mode 100644 index 0000000..6ce5bf1 --- /dev/null +++ b/base/threading/thread_checker_unittest.cc @@ -0,0 +1,146 @@ +// 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/basictypes.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/threading/simple_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +#ifndef NDEBUG + +namespace base { + +// Simple class to exersice the basics of ThreadChecker. +// Both the destructor and DoStuff should verify that they were +// called on the same thread as the constructor. +class ThreadCheckerClass : public ThreadChecker { + public: + ThreadCheckerClass() {} + + // Verifies that it was called on the same thread as the constructor. + void DoStuff() { + DCHECK(CalledOnValidThread()); + } + + void DetachFromThread() { + ThreadChecker::DetachFromThread(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass); +}; + +// Calls ThreadCheckerClass::DoStuff on another thread. +class CallDoStuffOnThread : public base::SimpleThread { + public: + CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class) + : SimpleThread("call_do_stuff_on_thread"), + thread_checker_class_(thread_checker_class) { + } + + virtual void Run() { + thread_checker_class_->DoStuff(); + } + + private: + ThreadCheckerClass* thread_checker_class_; + + DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread); +}; + +// Deletes ThreadCheckerClass on a different thread. +class DeleteThreadCheckerClassOnThread : public base::SimpleThread { + public: + DeleteThreadCheckerClassOnThread(ThreadCheckerClass* thread_checker_class) + : SimpleThread("delete_thread_checker_class_on_thread"), + thread_checker_class_(thread_checker_class) { + } + + virtual void Run() { + thread_checker_class_.reset(); + } + + private: + scoped_ptr<ThreadCheckerClass> thread_checker_class_; + + DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread); +}; + +TEST(ThreadCheckerTest, CallsAllowedOnSameThread) { + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); + + // Verify that DoStuff doesn't assert. + thread_checker_class->DoStuff(); + + // Verify that the destructor doesn't assert. + thread_checker_class.reset(); +} + +TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) { + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); + + // Verify that the destructor doesn't assert + // when called on a different thread. + DeleteThreadCheckerClassOnThread delete_on_thread( + thread_checker_class.release()); + + delete_on_thread.Start(); + delete_on_thread.Join(); +} + +TEST(ThreadCheckerTest, DetachFromThread) { + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); + + // Verify that DoStuff doesn't assert when called on a different thread after + // a call to DetachFromThread. + thread_checker_class->DetachFromThread(); + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + + call_on_thread.Start(); + call_on_thread.Join(); +} + +#if GTEST_HAS_DEATH_TEST + +TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThread) { + ASSERT_DEBUG_DEATH({ + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); + + // Verify that DoStuff asserts when called on a different thread. + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + + call_on_thread.Start(); + call_on_thread.Join(); + }, ""); +} + +TEST(ThreadCheckerDeathTest, DetachFromThread) { + ASSERT_DEBUG_DEATH({ + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); + + // Verify that DoStuff doesn't assert when called on a different thread + // after a call to DetachFromThread. + thread_checker_class->DetachFromThread(); + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + + call_on_thread.Start(); + call_on_thread.Join(); + + // Verify that DoStuff asserts after moving to another thread. + thread_checker_class->DoStuff(); + }, ""); +} + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace base + +#endif // NDEBUG diff --git a/base/threading/watchdog.cc b/base/threading/watchdog.cc index 8474744..cd21578 100644 --- a/base/threading/watchdog.cc +++ b/base/threading/watchdog.cc @@ -6,7 +6,7 @@ #include "base/compiler_specific.h" #include "base/logging.h" -#include "base/platform_thread.h" +#include "base/threading/platform_thread.h" namespace base { diff --git a/base/threading/watchdog.h b/base/threading/watchdog.h index 025fe09..8641f04 100644 --- a/base/threading/watchdog.h +++ b/base/threading/watchdog.h @@ -23,7 +23,7 @@ #include "base/condition_variable.h" #include "base/lock.h" -#include "base/platform_thread.h" +#include "base/threading/platform_thread.h" #include "base/time.h" namespace base { diff --git a/base/threading/watchdog_unittest.cc b/base/threading/watchdog_unittest.cc index 347781e..f96487b 100644 --- a/base/threading/watchdog_unittest.cc +++ b/base/threading/watchdog_unittest.cc @@ -5,8 +5,8 @@ #include "base/threading/watchdog.h" #include "base/logging.h" -#include "base/platform_thread.h" #include "base/spin_wait.h" +#include "base/threading/platform_thread.h" #include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/base/threading/worker_pool_posix.cc b/base/threading/worker_pool_posix.cc index 2facc01..8466403 100644 --- a/base/threading/worker_pool_posix.cc +++ b/base/threading/worker_pool_posix.cc @@ -6,10 +6,10 @@ #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/platform_thread.h" #include "base/ref_counted.h" #include "base/stringprintf.h" #include "base/task.h" +#include "base/threading/platform_thread.h" #include "base/threading/worker_pool.h" namespace base { diff --git a/base/threading/worker_pool_posix.h b/base/threading/worker_pool_posix.h index 6c99e76..44f0208 100644 --- a/base/threading/worker_pool_posix.h +++ b/base/threading/worker_pool_posix.h @@ -31,9 +31,9 @@ #include "base/basictypes.h" #include "base/condition_variable.h" #include "base/lock.h" -#include "base/platform_thread.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "base/threading/platform_thread.h" class Task; diff --git a/base/threading/worker_pool_posix_unittest.cc b/base/threading/worker_pool_posix_unittest.cc index 48df16e..25509bf 100644 --- a/base/threading/worker_pool_posix_unittest.cc +++ b/base/threading/worker_pool_posix_unittest.cc @@ -8,8 +8,8 @@ #include "base/condition_variable.h" #include "base/lock.h" -#include "base/platform_thread.h" #include "base/task.h" +#include "base/threading/platform_thread.h" #include "base/waitable_event.h" #include "testing/gtest/include/gtest/gtest.h" |