diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/SConscript | 13 | ||||
-rw-r--r-- | base/base.xcodeproj/project.pbxproj | 20 | ||||
-rw-r--r-- | base/build/base.vcproj | 2 | ||||
-rw-r--r-- | base/non_thread_safe.cc | 11 | ||||
-rw-r--r-- | base/non_thread_safe.h | 2 | ||||
-rw-r--r-- | base/platform_thread.h | 46 | ||||
-rw-r--r-- | base/platform_thread_posix.cc (renamed from base/platform_thread.cc) | 77 | ||||
-rw-r--r-- | base/platform_thread_win.cc | 114 | ||||
-rw-r--r-- | base/thread.cc | 150 | ||||
-rw-r--r-- | base/thread.h | 56 | ||||
-rw-r--r-- | base/thread_posix.cc | 121 | ||||
-rw-r--r-- | base/thread_unittest.cc | 4 | ||||
-rw-r--r-- | base/watchdog.cc | 5 |
13 files changed, 287 insertions, 334 deletions
diff --git a/base/SConscript b/base/SConscript index bf07eb4..b6214e3 100644 --- a/base/SConscript +++ b/base/SConscript @@ -71,9 +71,9 @@ input_files = [ 'memory_debug.cc', 'message_loop.cc', 'message_pump_default.cc', + 'non_thread_safe.cc', 'path_service.cc', 'pickle.cc', - 'platform_thread.cc', 'revocable_store.cc', 'ref_counted.cc', 'sha2.cc', @@ -82,6 +82,7 @@ input_files = [ 'string_piece.cc', 'string_util.cc', 'string_util_icu.cc', + 'thread.cc', 'time.cc', 'time_format.cc', 'timer.cc', @@ -106,10 +107,8 @@ if env['PLATFORM'] == 'win32': # This group all depends on MessageLoop. 'idle_timer.cc', - 'non_thread_safe.cc', 'object_watcher.cc', 'shared_event.cc', # Is this used? - 'thread.cc', 'watchdog.cc', 'process.cc', @@ -136,6 +135,7 @@ if env['PLATFORM'] == 'win32': 'lock_impl_win.cc', 'message_pump_win.cc', 'pe_image.cc', + 'platform_thread_win.cc', 'registry.cc', 'shared_memory_win.cc', 'sys_string_conversions_win.cc', @@ -151,6 +151,7 @@ if env['PLATFORM'] in ('darwin', 'posix'): 'condition_variable_posix.cc', 'debug_util_posix.cc', 'lock_impl_posix.cc', + 'platform_thread_posix.cc', 'shared_memory_posix.cc', 'thread_local_storage_posix.cc', 'time_posix.cc', @@ -164,7 +165,6 @@ if env['PLATFORM'] == 'darwin': 'file_util_mac.mm', 'file_version_info_mac.mm', 'sys_string_conversions_mac.cc', - 'thread_posix.cc', ]) if env['PLATFORM'] == 'posix': @@ -173,7 +173,6 @@ if env['PLATFORM'] == 'posix': 'base_paths_linux.cc', 'file_util_linux.cc', 'sys_string_conversions_linux.cc', - 'thread_posix.cc', ]) env.ChromeStaticLibrary('base', input_files) @@ -261,6 +260,7 @@ test_files = [ 'string_piece_unittest.cc', 'string_tokenizer_unittest.cc', 'string_util_unittest.cc', + 'thread_unittest.cc', 'time_unittest.cc', 'values_unittest.cc', 'waitable_event_unittest.cc', @@ -283,7 +283,6 @@ if env['PLATFORM'] == 'win32': 'idletimer_unittest.cc', 'hmac_unittest.cc', 'message_loop_unittest.cc', - 'object_watcher_unittest.cc', 'path_service_unittest.cc', 'process_util_unittest.cc', 'run_all_unittests.cc', @@ -291,7 +290,6 @@ if env['PLATFORM'] == 'win32': 'shared_memory_unittest.cc', 'stats_table_unittest.cc', 'thread_local_storage_unittest.cc', - 'thread_unittest.cc', 'timer_unittest.cc', 'tracked_objects_test.cc', 'gfx/native_theme_unittest.cc', @@ -304,6 +302,7 @@ if env['PLATFORM'] == 'win32': # Windows-specific tests. test_files.extend([ 'file_version_info_unittest.cc', + 'object_watcher_unittest.cc', 'pe_image_unittest.cc', 'sys_string_conversions_win_unittest.cc', 'win_util_unittest.cc', diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj index 89a1aa5..73b5a92 100644 --- a/base/base.xcodeproj/project.pbxproj +++ b/base/base.xcodeproj/project.pbxproj @@ -108,6 +108,9 @@ 93611AE80E5A803700F9405D /* message_loop_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93611ADA0E5A7FC500F9405D /* message_loop_unittest.cc */; }; 93611B180E5A875D00F9405D /* histogram.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93611B160E5A875D00F9405D /* histogram.cc */; }; 93611B1A0E5A878400F9405D /* histogram_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93611B190E5A878400F9405D /* histogram_test.cc */; }; + 93E703170E5D63E00046259B /* platform_thread_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93E703160E5D63E00046259B /* platform_thread_posix.cc */; }; + 93E7031B0E5D64390046259B /* thread_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93E7031A0E5D64390046259B /* thread_unittest.cc */; }; + 93E703240E5D64F00046259B /* thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93E703230E5D64F00046259B /* thread.cc */; }; A5A026550E4A214600498DA9 /* file_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5A026540E4A214600498DA9 /* file_util.cc */; }; A5A0268E0E4A2BDC00498DA9 /* file_util_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5A0268D0E4A2BDC00498DA9 /* file_util_posix.cc */; }; A5A0270B0E4A630D00498DA9 /* file_util_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5A0270A0E4A630D00498DA9 /* file_util_mac.mm */; }; @@ -119,7 +122,6 @@ ABF4B9B00DC2BC6500A6E319 /* json_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254031D0D92D1F40006B936 /* json_writer.cc */; }; ABF4B9B20DC2BC8300A6E319 /* memory_debug.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254032B0D92D2090006B936 /* memory_debug.cc */; }; ABF4B9B50DC2BC9F00A6E319 /* path_service.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF4B9B40DC2BC9F00A6E319 /* path_service.cc */; }; - ABF4B9B90DC2BCE300A6E319 /* platform_thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 82E23FCC0D9C219600F8B40A /* platform_thread.cc */; }; ABF4B9BC0DC2BD1000A6E319 /* sha2.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403620D92D27C0006B936 /* sha2.cc */; }; ABF4B9BE0DC2BD1500A6E319 /* sha512.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403700D92D2840006B936 /* sha512.cc */; }; ABF4B9C30DC2BD6C00A6E319 /* values.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403880D92D2CF0006B936 /* values.cc */; }; @@ -130,7 +132,6 @@ E49115EF0E47B461001EE8C3 /* at_exit.cc in Sources */ = {isa = PBXBuildFile; fileRef = E49115EC0E47B461001EE8C3 /* at_exit.cc */; }; E49357220E422A36008F8B09 /* timer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403850D92D2CF0006B936 /* timer.cc */; }; E4A133440E37A3C400110AA2 /* string_escape.cc in Sources */ = {isa = PBXBuildFile; fileRef = E4A133420E37A3C400110AA2 /* string_escape.cc */; }; - E4BA048E0E25687E00BE02C6 /* thread_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = E4BA048D0E25687E00BE02C6 /* thread_posix.cc */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -476,7 +477,6 @@ 82D094530E5B892600FEC05C /* time_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time_format.h; sourceTree = "<group>"; }; 82D094540E5B892600FEC05C /* time_format.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time_format.cc; sourceTree = "<group>"; }; 82E23FCB0D9C219600F8B40A /* platform_thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_thread.h; sourceTree = "<group>"; }; - 82E23FCC0D9C219600F8B40A /* platform_thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_thread.cc; sourceTree = "<group>"; }; 9301C0390E54C839001EF103 /* waitable_event_generic.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = waitable_event_generic.cc; sourceTree = "<group>"; }; 9301C03A0E54C839001EF103 /* waitable_event_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = waitable_event_unittest.cc; sourceTree = "<group>"; }; 93611ADA0E5A7FC500F9405D /* message_loop_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = message_loop_unittest.cc; sourceTree = "<group>"; }; @@ -487,6 +487,9 @@ 93611B160E5A875D00F9405D /* histogram.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = histogram.cc; sourceTree = "<group>"; }; 93611B170E5A875D00F9405D /* histogram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = histogram.h; sourceTree = "<group>"; }; 93611B190E5A878400F9405D /* histogram_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = histogram_test.cc; sourceTree = "<group>"; }; + 93E703160E5D63E00046259B /* platform_thread_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_thread_posix.cc; sourceTree = "<group>"; }; + 93E7031A0E5D64390046259B /* thread_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_unittest.cc; sourceTree = "<group>"; }; + 93E703230E5D64F00046259B /* thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread.cc; sourceTree = "<group>"; }; A5A026180E48FE1500498DA9 /* base_paths_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_paths_mac.h; sourceTree = "<group>"; }; A5A026540E4A214600498DA9 /* file_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_util.cc; sourceTree = "<group>"; }; A5A0268D0E4A2BDC00498DA9 /* file_util_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_util_posix.cc; sourceTree = "<group>"; }; @@ -540,7 +543,6 @@ E4AFA4C20E50DED500201347 /* string_piece_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_piece_unittest.cc; sourceTree = "<group>"; }; E4AFA4CE0E50E0C500201347 /* word_iterator_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = word_iterator_unittest.cc; sourceTree = "<group>"; }; E4AFA4EF0E50F7B000201347 /* time_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time_unittest.cc; sourceTree = "<group>"; }; - E4BA048D0E25687E00BE02C6 /* thread_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_posix.cc; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -756,8 +758,8 @@ E4562C680E2803C3005E4685 /* pickle_unittest.cc */, A5CB82960E5C74E300FD6825 /* platform_test.h */, A5CB82970E5C74E300FD6825 /* platform_test_mac.mm */, - 82E23FCC0D9C219600F8B40A /* platform_thread.cc */, 82E23FCB0D9C219600F8B40A /* platform_thread.h */, + 93E703160E5D63E00046259B /* platform_thread_posix.cc */, 825403400D92D2210006B936 /* port.h */, E4AFA4B40E50D8B000201347 /* pr_time_test.cc */, 8254034C0D92D23C0006B936 /* prcpucfg.h */, @@ -816,10 +818,11 @@ 7B4C5F470E4B6BF900679E8F /* sys_string_conversions.h */, 7B4C5F480E4B6BF900679E8F /* sys_string_conversions_mac.cc */, 8254037E0D92D2CF0006B936 /* task.h */, + 93E703230E5D64F00046259B /* thread.cc */, 825403800D92D2CF0006B936 /* thread.h */, 825403820D92D2CF0006B936 /* thread_local_storage.h */, 829E36720DC0FBAD00819EBF /* thread_local_storage_posix.cc */, - E4BA048D0E25687E00BE02C6 /* thread_posix.cc */, + 93E7031A0E5D64390046259B /* thread_unittest.cc */, 824654900DC25A8C007C2BAA /* time.cc */, 825403840D92D2CF0006B936 /* time.h */, 82D094540E5B892600FEC05C /* time_format.cc */, @@ -1147,7 +1150,7 @@ 93611ADF0E5A7FC500F9405D /* message_pump_default.cc in Sources */, ABF4B9B50DC2BC9F00A6E319 /* path_service.cc in Sources */, 824654A60DC25CD7007C2BAA /* pickle.cc in Sources */, - ABF4B9B90DC2BCE300A6E319 /* platform_thread.cc in Sources */, + 93E703170E5D63E00046259B /* platform_thread_posix.cc in Sources */, 824654DF0DC26521007C2BAA /* prtime.cc in Sources */, 7B836C050E55BBB800F6AD31 /* ref_counted.cc in Sources */, 8246548C0DC259DB007C2BAA /* revocable_store.cc in Sources */, @@ -1161,7 +1164,6 @@ 820EB4FA0E3A6178009668FC /* string_util_icu.cc in Sources */, 7B4C5F4A0E4B6BF900679E8F /* sys_string_conversions_mac.cc in Sources */, 829E36730DC0FBAD00819EBF /* thread_local_storage_posix.cc in Sources */, - E4BA048E0E25687E00BE02C6 /* thread_posix.cc in Sources */, 824654910DC25A8C007C2BAA /* time.cc in Sources */, 82D094550E5B892600FEC05C /* time_format.cc in Sources */, 824654530DC25633007C2BAA /* time_posix.cc in Sources */, @@ -1171,6 +1173,7 @@ ABF4B9C30DC2BD6C00A6E319 /* values.cc in Sources */, 9301C03B0E54C839001EF103 /* waitable_event_generic.cc in Sources */, 824655DD0DC659B8007C2BAA /* word_iterator.cc in Sources */, + 93E703240E5D64F00046259B /* thread.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1217,6 +1220,7 @@ 7B78D39F0E54FE0100609465 /* string_piece_unittest.cc in Sources */, 7B78D3A00E54FE0100609465 /* string_tokenizer_unittest.cc in Sources */, 7B78D3A10E54FE0100609465 /* string_util_unittest.cc in Sources */, + 93E7031B0E5D64390046259B /* thread_unittest.cc in Sources */, 7B78D3A20E54FE0100609465 /* time_unittest.cc in Sources */, 7B78D3A30E54FE0100609465 /* values_unittest.cc in Sources */, 7B78D3A40E54FE0100609465 /* waitable_event_unittest.cc in Sources */, diff --git a/base/build/base.vcproj b/base/build/base.vcproj index e4c00d1..8648217 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -478,7 +478,7 @@ > </File> <File - RelativePath="..\platform_thread.cc" + RelativePath="..\platform_thread_win.cc" > </File> <File diff --git a/base/non_thread_safe.cc b/base/non_thread_safe.cc index eefc9a5..9619a88 100644 --- a/base/non_thread_safe.cc +++ b/base/non_thread_safe.cc @@ -29,20 +29,21 @@ #include "base/non_thread_safe.h" -#include "base/message_loop.h" +#include "base/platform_thread.h" -// These checks are only done in release builds. +// These checks are only done in debug builds. #ifndef NDEBUG -NonThreadSafe::NonThreadSafe() : valid_thread_id_(GetCurrentThreadId()) { +NonThreadSafe::NonThreadSafe() + : valid_thread_id_(PlatformThread::CurrentId()) { } bool NonThreadSafe::CalledOnValidThread() const { - return valid_thread_id_ == GetCurrentThreadId(); + return valid_thread_id_ == PlatformThread::CurrentId(); } NonThreadSafe::~NonThreadSafe() { DCHECK(CalledOnValidThread()); } -#endif +#endif // NDEBUG diff --git a/base/non_thread_safe.h b/base/non_thread_safe.h index e3f4e97..7734e32 100644 --- a/base/non_thread_safe.h +++ b/base/non_thread_safe.h @@ -60,7 +60,7 @@ class NonThreadSafe { bool CalledOnValidThread() const; private: - int32 valid_thread_id_; + int valid_thread_id_; }; #else // Do nothing in release mode. diff --git a/base/platform_thread.h b/base/platform_thread.h index bb75efe..1274f0f 100644 --- a/base/platform_thread.h +++ b/base/platform_thread.h @@ -30,26 +30,19 @@ #ifndef BASE_PLATFORM_THREAD_H_ #define BASE_PLATFORM_THREAD_H_ -#include "build/build_config.h" +#include "base/basictypes.h" #if defined(OS_WIN) - -#include <windows.h> -typedef HANDLE PlatformThreadHandle; - +typedef void* PlatformThreadHandle; // HANDLE #elif defined(OS_POSIX) - #include <pthread.h> typedef pthread_t PlatformThreadHandle; +#endif -#endif // defined(OS_POSIX) - +// A namespace for low-level thread functions. class PlatformThread { public: - // Gets the current thread. - static PlatformThread Current(); - - // Gets the current thread id + // Gets the current thread id, which may be useful for logging purposes. static int CurrentId(); // Yield the current thread so another thread can be scheduled. @@ -58,10 +51,35 @@ class PlatformThread { // Sleeps for the specified duration (units are milliseconds). static void Sleep(int duration_ms); - bool operator==(const PlatformThread& other_thread); + // Sets the thread name visible to a debugger. This has no effect otherwise. + // To set the name of the current thread, pass PlatformThread::CurrentId() as + // the thread_id parameter. + static void SetName(int thread_id, const char* name); + + // 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; + }; + + // 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. 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. + static void Join(PlatformThreadHandle thread_handle); private: - PlatformThreadHandle thread_; + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); }; #endif // BASE_PLATFORM_THREAD_H_ diff --git a/base/platform_thread.cc b/base/platform_thread_posix.cc index eda6fc1e..00d76dd 100644 --- a/base/platform_thread.cc +++ b/base/platform_thread_posix.cc @@ -29,10 +29,8 @@ #include "base/platform_thread.h" -#if defined(OS_POSIX) #include <errno.h> #include <sched.h> -#endif #if defined(OS_MACOSX) #include <mach/mach.h> @@ -41,60 +39,69 @@ #include <unistd.h> #endif -// static -PlatformThread PlatformThread::Current() { - PlatformThread thread; +static void* ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(closure); + delegate->ThreadMain(); + return NULL; +} -#if defined(OS_WIN) - thread.thread_ = GetCurrentThread(); -#elif defined(OS_POSIX) - thread.thread_ = pthread_self(); +// static +int 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); #endif - - return thread; } // static void PlatformThread::YieldCurrentThread() { -#if defined(OS_WIN) - ::Sleep(0); -#elif defined(OS_POSIX) sched_yield(); -#endif } // static void PlatformThread::Sleep(int duration_ms) { -#if defined(OS_WIN) - ::Sleep(duration_ms); -#elif defined (OS_POSIX) 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) { + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) sleep_time = remaining; - } -#endif } // static -int PlatformThread::CurrentId() { -#if defined(OS_WIN) - return GetCurrentThreadId(); -#elif defined(OS_MACOSX) - return mach_thread_self(); -#elif defined(OS_LINUX) - return syscall(__NR_gettid); -#endif +void PlatformThread::SetName(int thread_id, const char* name) { + // TODO(darin): implement me! } -bool PlatformThread::operator==(const PlatformThread& other_thread) { -#if defined(OS_WIN) - return thread_ == other_thread.thread_; -#elif defined(OS_POSIX) - return pthread_equal(thread_, other_thread.thread_); -#endif +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + bool success = false; + pthread_attr_t attributes; + pthread_attr_init(&attributes); + + // Pthreads are joinable by default, so we don't need to specify any special + // attributes to be able to call pthread_join later. + + if (stack_size > 0) + pthread_attr_setstacksize(&attributes, stack_size); + + success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate); + + pthread_attr_destroy(&attributes); + return success; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + pthread_join(thread_handle, NULL); } diff --git a/base/platform_thread_win.cc b/base/platform_thread_win.cc new file mode 100644 index 0000000..e261c25 --- /dev/null +++ b/base/platform_thread_win.cc @@ -0,0 +1,114 @@ +// 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/platform_thread.h" + +#include <process.h> + +#include "base/logging.h" +#include "base/win_util.h" + +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; + +unsigned __stdcall ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(closure); + delegate->ThreadMain(); + return NULL; +} + +} // namespace + +// static +int PlatformThread::CurrentId() { + return GetCurrentThreadId(); +} + +// static +void PlatformThread::YieldCurrentThread() { + ::Sleep(0); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + ::Sleep(duration_ms); +} + +// static +void PlatformThread::SetName(int thread_id, const char* name) { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = thread_id; + 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) { + unsigned int flags = 0; + if (stack_size > 0 && win_util::GetWinVersion() >= win_util::WINVERSION_XP) { + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; + } else { + stack_size = 0; + } + + *thread_handle = reinterpret_cast<PlatformThreadHandle>(_beginthreadex( + NULL, stack_size, ThreadFunc, delegate, flags, NULL)); + return *thread_handle != NULL; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + DCHECK(thread_handle); + + // 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); +} diff --git a/base/thread.cc b/base/thread.cc index 6998deb..ae1b2c0 100644 --- a/base/thread.cc +++ b/base/thread.cc @@ -29,31 +29,9 @@ #include "base/thread.h" -#include <process.h> -#include <windows.h> - #include "base/message_loop.h" -#include "base/object_watcher.h" -#include "base/ref_counted.h" #include "base/string_util.h" #include "base/waitable_event.h" -#include "base/win_util.h" - -namespace { - -// This class is used when starting a thread. It passes information to the -// thread function. It is referenced counted so we can cleanup the event -// object used to synchronize thread startup properly. -class ThreadStartInfo { - public: - Thread* self; - base::WaitableEvent start_event; - - explicit ThreadStartInfo(Thread* t) : self(t), start_event(false, false) { - } -}; - -} // namespace // This task is used to trigger the message loop to exit. class ThreadQuitTask : public Task { @@ -65,10 +43,10 @@ class ThreadQuitTask : public Task { }; Thread::Thread(const char *name) - : thread_(NULL), - thread_id_(0), - message_loop_(NULL), - name_(name) { + : message_loop_(NULL), + startup_event_(NULL), + name_(name), + thread_created_(false) { } Thread::~Thread() { @@ -98,95 +76,60 @@ bool Thread::GetThreadWasQuitProperly() { return quit_properly; } -// The information on how to set the thread name comes from -// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx -#define MS_VC_EXCEPTION 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; - - -// On XP, you can only get the ThreadId of the current -// thread. So it is expected that you'll call this after the -// thread starts up; hence, it is static. -void Thread::SetThreadName(const char* name, unsigned int tid) { - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = tid; - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, - sizeof(info)/sizeof(DWORD), - reinterpret_cast<DWORD_PTR *>(&info)); - } __except(EXCEPTION_CONTINUE_EXECUTION) { - } -} - - bool Thread::Start() { return StartWithStackSize(0); } bool Thread::StartWithStackSize(size_t stack_size) { - DCHECK(!thread_id_ && !thread_); + DCHECK(!message_loop_); + SetThreadWasQuitProperly(false); - ThreadStartInfo info(this); - unsigned int flags = 0; - if (win_util::GetWinVersion() >= win_util::WINVERSION_XP && stack_size) { - flags = STACK_SIZE_PARAM_IS_A_RESERVATION; - } else { - stack_size = 0; - } - thread_ = reinterpret_cast<HANDLE>( - _beginthreadex(NULL, - static_cast<unsigned int>(stack_size), - ThreadFunc, - &info, - flags, - &thread_id_)); - if (!thread_) { + base::WaitableEvent event(false, false); + startup_event_ = &event; + + if (!PlatformThread::Create(stack_size, this, &thread_)) { DLOG(ERROR) << "failed to create thread"; return false; } // Wait for the thread to start and initialize message_loop_ - info.start_event.Wait(); + startup_event_->Wait(); + startup_event_ = NULL; + + DCHECK(message_loop_); return true; } void Thread::Stop() { - if (!thread_) + if (!thread_created_) return; - DCHECK_NE(thread_id_, GetCurrentThreadId()) << "Can't call Stop() on itself"; + DCHECK_NE(thread_id_, PlatformThread::CurrentId()) << + "Can't call Stop() on the currently executing thread"; - if (message_loop()) + // If StopSoon was called, then we won't have a message loop anymore, but + // more importantly, we won't need to tell the thread to stop. + if (message_loop_) message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); // Wait for the thread to exit. It should already have terminated but make // sure this assumption is valid. - DWORD result = WaitForSingleObject(thread_, INFINITE); - DCHECK_EQ(result, WAIT_OBJECT_0); + PlatformThread::Join(thread_); - // Reset state. - CloseHandle(thread_); - thread_ = NULL; - thread_id_ = 0; + // The thread can't receive messages anymore. + message_loop_ = NULL; + + // The thread no longer needs to be joined. + thread_created_ = false; } void Thread::StopSoon() { - if (!thread_id_) + if (!message_loop_) return; - DCHECK_NE(thread_id_, GetCurrentThreadId()) << - "Can't call StopSoon() on itself"; + DCHECK_NE(thread_id_, PlatformThread::CurrentId()) << + "Can't call StopSoon() on the currently executing thread"; // We had better have a message loop at this point! If we do not, then it // most likely means that the thread terminated unexpectedly, probably due @@ -196,40 +139,35 @@ void Thread::StopSoon() { message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); // The thread can't receive messages anymore. - thread_id_ = 0; + message_loop_ = NULL; } -/*static*/ -unsigned __stdcall Thread::ThreadFunc(void* param) { +void Thread::ThreadMain() { // The message loop for this thread. MessageLoop message_loop; - Thread* self; - // Complete the initialization of our Thread object. - { - ThreadStartInfo* info = static_cast<ThreadStartInfo*>(param); - self = info->self; - self->message_loop_ = &message_loop; - SetThreadName(self->thread_name().c_str(), GetCurrentThreadId()); - message_loop.set_thread_name(self->thread_name()); - info->start_event.Signal(); - // info can't be touched anymore since the starting thread is now unlocked. - } + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::SetName(thread_id_, name_.c_str()); + message_loop.set_thread_name(name_); + message_loop_ = &message_loop; + thread_created_ = true; + + startup_event_->Signal(); + // startup_event_ can't be touched anymore since the starting thread is now + // unlocked. // Let the thread do extra initialization. - self->Init(); + Init(); message_loop.Run(); // Let the thread do extra cleanup. - self->CleanUp(); + CleanUp(); // Assert that MessageLoop::Quit was called by ThreadQuitTask. - DCHECK(Thread::GetThreadWasQuitProperly()); + DCHECK(GetThreadWasQuitProperly()); // We can't receive messages anymore. - self->message_loop_ = NULL; - - return 0; + message_loop_ = NULL; } diff --git a/base/thread.h b/base/thread.h index 37d4eb8..31edd5c 100644 --- a/base/thread.h +++ b/base/thread.h @@ -32,25 +32,21 @@ #include <string> +#include "base/platform_thread.h" #include "base/thread_local_storage.h" class MessageLoop; -// Types that differ -#if defined(OS_WIN) -#include <wtypes.h> -typedef unsigned int ThreadId; -#elif defined(OS_POSIX) -typedef pthread_t ThreadId; -#endif +namespace base { +class WaitableEvent; +} // A simple thread abstraction that establishes a MessageLoop on a new thread. // The consumer uses the MessageLoop of the thread to cause code to execute on // the thread. When this object is destroyed the thread is terminated. All // pending tasks queued on the thread's message loop will run to completion // before the thread is terminated. -// -class Thread { +class Thread : PlatformThread::Delegate { public: // Constructor. // name is a display string to identify the thread. @@ -112,23 +108,13 @@ class Thread { // NOTE: You must not call this MessageLoop's Quit method directly. Use // the Thread's Stop method instead. // - MessageLoop* message_loop() const { - if (thread_id_) - return message_loop_; - return NULL; - } + MessageLoop* message_loop() const { return message_loop_; } // Set the name of this thread (for display in debugger too). const std::string &thread_name() { return name_; } -#if defined(OS_WIN) - HANDLE thread_handle() { return thread_; } -#endif - - // Sets the thread name if a debugger is currently attached. Has no effect - // otherwise. To set the name of the current thread, pass GetCurrentThreadId() - // as the tid parameter. - static void SetThreadName(const char* name, ThreadId tid); + // The native thread handle. + PlatformThreadHandle thread_handle() { return thread_; } protected: // Called just prior to starting the message loop @@ -141,21 +127,27 @@ class Thread { static bool GetThreadWasQuitProperly(); private: -#if defined(OS_WIN) - static unsigned __stdcall ThreadFunc(void* param); - // The thread's handle. Modified by the root thread. - HANDLE thread_; -#endif + // PlatformThread::Delegate methods: + virtual void ThreadMain(); + + // The thread's handle. + PlatformThreadHandle thread_; - // The thread's id. Modified by the root thread. Set to 0 when the thread is - // not ready to receive messages. - ThreadId thread_id_; + // The thread's ID. Used for debugging purposes. + int thread_id_; - // The thread's message loop. Valid only while the thread is alive. Modified + // The thread's message loop. Valid only while the thread is alive. Set // by the created thread. MessageLoop* message_loop_; - const std::string name_; + // Used to synchronize thread startup. + base::WaitableEvent* startup_event_; + + // The name of the thread. Used for debugging purposes. + std::string name_; + + // This flag indicates if we created a thread that needs to be joined. + bool thread_created_; static TLSSlot tls_index_; diff --git a/base/thread_posix.cc b/base/thread_posix.cc deleted file mode 100644 index 59cd770..0000000 --- a/base/thread_posix.cc +++ /dev/null @@ -1,121 +0,0 @@ -// 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/thread.h" - -#include "base/message_loop.h" -#include "base/ref_counted.h" -#include "base/string_util.h" - -// This task is used to trigger the message loop to exit. -class ThreadQuitTask : public Task { - public: - virtual void Run() { - MessageLoop::current()->Quit(); -#ifndef NDEBUG - Thread::SetThreadWasQuitProperly(true); -#endif - } -}; - -Thread::Thread(const char *name) - : thread_id_(0), - message_loop_(NULL), - name_(name) { -} - -Thread::~Thread() { - Stop(); -} - -void* ThreadFunc(void* closure) { - // TODO(pinkerton): I have no clue what to do here - - pthread_exit(NULL); -} - -// We use this thread-local variable to record whether or not a thread exited -// because its Stop method was called. This allows us to catch cases where -// MessageLoop::Quit() is called directly, which is unexpected when using a -// Thread to setup and run a MessageLoop. -// Note that if we start doing complex stuff in other static initializers -// this could cause problems. -// TODO(evanm): don't rely on static initialization. -TLSSlot Thread::tls_index_; - -void Thread::SetThreadWasQuitProperly(bool flag) { - tls_index_.Set(reinterpret_cast<void*>(flag)); -} - -bool Thread::GetThreadWasQuitProperly() { - return (tls_index_.Get() != 0); -} - - -bool Thread::Start() { - return StartWithStackSize(0); -} - -bool Thread::StartWithStackSize(size_t stack_size) { - bool success = false; - pthread_attr_t attributes; - pthread_attr_init(&attributes); - - // A stack size smaller than PTHREAD_STACK_MIN won't change the default value. - pthread_attr_setstacksize(&attributes, stack_size); - int result = pthread_create(&thread_id_, - &attributes, - ThreadFunc, - &message_loop_); - if (!result) - success = true; - - pthread_attr_destroy(&attributes); - return success; -} - -void Thread::Stop() { -// DCHECK_NE(thread_id_, GetCurrentThreadId()) -// << "Can't call Stop() on itself"; - - // We had better have a message loop at this point! If we do not, then it - // most likely means that the thread terminated unexpectedly, probably due - // to someone calling Quit() on our message loop directly. - DCHECK(message_loop_); - - message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); - - message_loop_ = NULL; -} - -void Thread::StopSoon() { - // TODO(paulg): Make Thread::Stop block on thread join, and Thread::StopSoon - // return immediately after calling (like the Windows versions). - Stop(); -} diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc index c4e8be7..2717246 100644 --- a/base/thread_unittest.cc +++ b/base/thread_unittest.cc @@ -51,7 +51,7 @@ class SleepSome : public Task { explicit SleepSome(int msec) : msec_(msec) { } virtual void Run() { - Sleep(msec_); + PlatformThread::Sleep(msec_); } private: int msec_; @@ -89,7 +89,7 @@ TEST(ThreadTest, StartWithStackSize) { // instead to avoid busy waiting, but this is sufficient for // testing purposes). for (int i = 100; i >= 0 && !was_invoked; --i) { - Sleep(10); + PlatformThread::Sleep(10); } EXPECT_TRUE(was_invoked); } diff --git a/base/watchdog.cc b/base/watchdog.cc index 1d9c183..f8540eb 100644 --- a/base/watchdog.cc +++ b/base/watchdog.cc @@ -28,8 +28,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "base/watchdog.h" + +#include "base/platform_thread.h" #include "base/string_util.h" -#include "base/thread.h" //------------------------------------------------------------------------------ // Public API methods. @@ -159,7 +160,7 @@ unsigned Watchdog::Run() { void Watchdog::SetThreadName() const { std::string name = StringPrintf("%s Watchdog", WideToASCII(thread_watched_name_).c_str()); - Thread::SetThreadName(name.c_str(), thread_id_); + PlatformThread::SetName(thread_id_, name.c_str()); DLOG(INFO) << "Watchdog active: " << name; } |