1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/threading/platform_thread.h" 6 7 #include "base/debug/alias.h" 8 #include "base/debug/profiler.h" 9 #include "base/logging.h" 10 #include "base/threading/thread_id_name_manager.h" 11 #include "base/threading/thread_restrictions.h" 12 #include "base/tracked_objects.h" 13 #include "base/win/scoped_handle.h" 14 #include "base/win/windows_version.h" 15 16 namespace base { 17 18 namespace { 19 20 // The information on how to set the thread name comes from 21 // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx 22 const DWORD kVCThreadNameException = 0x406D1388; 23 24 typedef struct tagTHREADNAME_INFO { 25 DWORD dwType; // Must be 0x1000. 26 LPCSTR szName; // Pointer to name (in user addr space). 27 DWORD dwThreadID; // Thread ID (-1=caller thread). 28 DWORD dwFlags; // Reserved for future use, must be zero. 29 } THREADNAME_INFO; 30 31 // This function has try handling, so it is separated out of its caller. 32 void SetNameInternal(PlatformThreadId thread_id, const char* name) { 33 THREADNAME_INFO info; 34 info.dwType = 0x1000; 35 info.szName = name; 36 info.dwThreadID = thread_id; 37 info.dwFlags = 0; 38 39 __try { 40 RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), 41 reinterpret_cast<DWORD_PTR*>(&info)); 42 } __except(EXCEPTION_CONTINUE_EXECUTION) { 43 } 44 } 45 46 struct ThreadParams { 47 PlatformThread::Delegate* delegate; 48 bool joinable; 49 }; 50 51 DWORD __stdcall ThreadFunc(void* params) { 52 ThreadParams* thread_params = static_cast<ThreadParams*>(params); 53 PlatformThread::Delegate* delegate = thread_params->delegate; 54 if (!thread_params->joinable) 55 base::ThreadRestrictions::SetSingletonAllowed(false); 56 57 // Retrieve a copy of the thread handle to use as the key in the 58 // thread name mapping. 59 PlatformThreadHandle::Handle platform_handle; 60 BOOL did_dup = DuplicateHandle(GetCurrentProcess(), 61 GetCurrentThread(), 62 GetCurrentProcess(), 63 &platform_handle, 64 0, 65 FALSE, 66 DUPLICATE_SAME_ACCESS); 67 68 win::ScopedHandle scoped_platform_handle; 69 70 if (did_dup) { 71 scoped_platform_handle.Set(platform_handle); 72 ThreadIdNameManager::GetInstance()->RegisterThread( 73 scoped_platform_handle.Get(), 74 PlatformThread::CurrentId()); 75 } 76 77 delete thread_params; 78 delegate->ThreadMain(); 79 80 if (did_dup) { 81 ThreadIdNameManager::GetInstance()->RemoveName( 82 scoped_platform_handle.Get(), 83 PlatformThread::CurrentId()); 84 } 85 86 return NULL; 87 } 88 89 // CreateThreadInternal() matches PlatformThread::Create(), except that 90 // |out_thread_handle| may be NULL, in which case a non-joinable thread is 91 // created. 92 bool CreateThreadInternal(size_t stack_size, 93 PlatformThread::Delegate* delegate, 94 PlatformThreadHandle* out_thread_handle) { 95 unsigned int flags = 0; 96 if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) { 97 flags = STACK_SIZE_PARAM_IS_A_RESERVATION; 98 } else { 99 stack_size = 0; 100 } 101 102 ThreadParams* params = new ThreadParams; 103 params->delegate = delegate; 104 params->joinable = out_thread_handle != NULL; 105 106 // Using CreateThread here vs _beginthreadex makes thread creation a bit 107 // faster and doesn't require the loader lock to be available. Our code will 108 // have to work running on CreateThread() threads anyway, since we run code 109 // on the Windows thread pool, etc. For some background on the difference: 110 // http://www.microsoft.com/msj/1099/win32/win321099.aspx 111 void* thread_handle = CreateThread( 112 NULL, stack_size, ThreadFunc, params, flags, NULL); 113 if (!thread_handle) { 114 delete params; 115 return false; 116 } 117 118 if (out_thread_handle) 119 *out_thread_handle = PlatformThreadHandle(thread_handle); 120 else 121 CloseHandle(thread_handle); 122 return true; 123 } 124 125 } // namespace 126 127 // static 128 PlatformThreadId PlatformThread::CurrentId() { 129 return GetCurrentThreadId(); 130 } 131 132 // static 133 PlatformThreadRef PlatformThread::CurrentRef() { 134 return PlatformThreadRef(GetCurrentThreadId()); 135 } 136 137 // static 138 PlatformThreadHandle PlatformThread::CurrentHandle() { 139 NOTIMPLEMENTED(); // See OpenThread() 140 return PlatformThreadHandle(); 141 } 142 143 // static 144 void PlatformThread::YieldCurrentThread() { 145 ::Sleep(0); 146 } 147 148 // static 149 void PlatformThread::Sleep(TimeDelta duration) { 150 // When measured with a high resolution clock, Sleep() sometimes returns much 151 // too early. We may need to call it repeatedly to get the desired duration. 152 TimeTicks end = TimeTicks::Now() + duration; 153 TimeTicks now; 154 while ((now = TimeTicks::Now()) < end) 155 ::Sleep((end - now).InMillisecondsRoundedUp()); 156 } 157 158 // static 159 void PlatformThread::SetName(const char* name) { 160 ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); 161 162 // On Windows only, we don't need to tell the profiler about the "BrokerEvent" 163 // thread, as it exists only in the chrome.exe image, and never spawns or runs 164 // tasks (items which could be profiled). This test avoids the notification, 165 // which would also (as a side effect) initialize the profiler in this unused 166 // context, including setting up thread local storage, etc. The performance 167 // impact is not terrible, but there is no reason to do initialize it. 168 if (0 != strcmp(name, "BrokerEvent")) 169 tracked_objects::ThreadData::InitializeThreadContext(name); 170 171 // The debugger needs to be around to catch the name in the exception. If 172 // there isn't a debugger, we are just needlessly throwing an exception. 173 // If this image file is instrumented, we raise the exception anyway 174 // to provide the profiler with human-readable thread names. 175 if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) 176 return; 177 178 SetNameInternal(CurrentId(), name); 179 } 180 181 // static 182 const char* PlatformThread::GetName() { 183 return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); 184 } 185 186 // static 187 bool PlatformThread::Create(size_t stack_size, Delegate* delegate, 188 PlatformThreadHandle* thread_handle) { 189 DCHECK(thread_handle); 190 return CreateThreadInternal(stack_size, delegate, thread_handle); 191 } 192 193 // static 194 bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, 195 PlatformThreadHandle* thread_handle, 196 ThreadPriority priority) { 197 bool result = Create(stack_size, delegate, thread_handle); 198 if (result) 199 SetThreadPriority(*thread_handle, priority); 200 return result; 201 } 202 203 // static 204 bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { 205 return CreateThreadInternal(stack_size, delegate, NULL); 206 } 207 208 // static 209 void PlatformThread::Join(PlatformThreadHandle thread_handle) { 210 DCHECK(thread_handle.handle_); 211 // TODO(willchan): Enable this check once I can get it to work for Windows 212 // shutdown. 213 // Joining another thread may block the current thread for a long time, since 214 // the thread referred to by |thread_handle| may still be running long-lived / 215 // blocking tasks. 216 #if 0 217 base::ThreadRestrictions::AssertIOAllowed(); 218 #endif 219 220 // Wait for the thread to exit. It should already have terminated but make 221 // sure this assumption is valid. 222 DWORD result = WaitForSingleObject(thread_handle.handle_, INFINITE); 223 if (result != WAIT_OBJECT_0) { 224 // Debug info for bug 127931. 225 DWORD error = GetLastError(); 226 debug::Alias(&error); 227 debug::Alias(&result); 228 debug::Alias(&thread_handle.handle_); 229 CHECK(false); 230 } 231 232 CloseHandle(thread_handle.handle_); 233 } 234 235 // static 236 void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, 237 ThreadPriority priority) { 238 switch (priority) { 239 case kThreadPriority_Normal: 240 ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_NORMAL); 241 break; 242 case kThreadPriority_RealtimeAudio: 243 ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_TIME_CRITICAL); 244 break; 245 default: 246 NOTREACHED() << "Unknown priority."; 247 break; 248 } 249 } 250 251 } // namespace base 252