| OLD | NEW |
| (Empty) |
| 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 ThreadPriority priority; | |
| 50 }; | |
| 51 | |
| 52 DWORD __stdcall ThreadFunc(void* params) { | |
| 53 ThreadParams* thread_params = static_cast<ThreadParams*>(params); | |
| 54 PlatformThread::Delegate* delegate = thread_params->delegate; | |
| 55 if (!thread_params->joinable) | |
| 56 base::ThreadRestrictions::SetSingletonAllowed(false); | |
| 57 | |
| 58 if (thread_params->priority != ThreadPriority::NORMAL) | |
| 59 PlatformThread::SetCurrentThreadPriority(thread_params->priority); | |
| 60 | |
| 61 // Retrieve a copy of the thread handle to use as the key in the | |
| 62 // thread name mapping. | |
| 63 PlatformThreadHandle::Handle platform_handle; | |
| 64 BOOL did_dup = DuplicateHandle(GetCurrentProcess(), | |
| 65 GetCurrentThread(), | |
| 66 GetCurrentProcess(), | |
| 67 &platform_handle, | |
| 68 0, | |
| 69 FALSE, | |
| 70 DUPLICATE_SAME_ACCESS); | |
| 71 | |
| 72 win::ScopedHandle scoped_platform_handle; | |
| 73 | |
| 74 if (did_dup) { | |
| 75 scoped_platform_handle.Set(platform_handle); | |
| 76 ThreadIdNameManager::GetInstance()->RegisterThread( | |
| 77 scoped_platform_handle.Get(), | |
| 78 PlatformThread::CurrentId()); | |
| 79 } | |
| 80 | |
| 81 delete thread_params; | |
| 82 delegate->ThreadMain(); | |
| 83 | |
| 84 if (did_dup) { | |
| 85 ThreadIdNameManager::GetInstance()->RemoveName( | |
| 86 scoped_platform_handle.Get(), | |
| 87 PlatformThread::CurrentId()); | |
| 88 } | |
| 89 | |
| 90 return NULL; | |
| 91 } | |
| 92 | |
| 93 // CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except | |
| 94 // that |out_thread_handle| may be NULL, in which case a non-joinable thread is | |
| 95 // created. | |
| 96 bool CreateThreadInternal(size_t stack_size, | |
| 97 PlatformThread::Delegate* delegate, | |
| 98 PlatformThreadHandle* out_thread_handle, | |
| 99 ThreadPriority priority) { | |
| 100 unsigned int flags = 0; | |
| 101 if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) { | |
| 102 flags = STACK_SIZE_PARAM_IS_A_RESERVATION; | |
| 103 } else { | |
| 104 stack_size = 0; | |
| 105 } | |
| 106 | |
| 107 ThreadParams* params = new ThreadParams; | |
| 108 params->delegate = delegate; | |
| 109 params->joinable = out_thread_handle != NULL; | |
| 110 params->priority = priority; | |
| 111 | |
| 112 // Using CreateThread here vs _beginthreadex makes thread creation a bit | |
| 113 // faster and doesn't require the loader lock to be available. Our code will | |
| 114 // have to work running on CreateThread() threads anyway, since we run code | |
| 115 // on the Windows thread pool, etc. For some background on the difference: | |
| 116 // http://www.microsoft.com/msj/1099/win32/win321099.aspx | |
| 117 PlatformThreadId thread_id; | |
| 118 void* thread_handle = CreateThread( | |
| 119 NULL, stack_size, ThreadFunc, params, flags, &thread_id); | |
| 120 if (!thread_handle) { | |
| 121 delete params; | |
| 122 return false; | |
| 123 } | |
| 124 | |
| 125 if (out_thread_handle) | |
| 126 *out_thread_handle = PlatformThreadHandle(thread_handle, thread_id); | |
| 127 else | |
| 128 CloseHandle(thread_handle); | |
| 129 return true; | |
| 130 } | |
| 131 | |
| 132 } // namespace | |
| 133 | |
| 134 // static | |
| 135 PlatformThreadId PlatformThread::CurrentId() { | |
| 136 return ::GetCurrentThreadId(); | |
| 137 } | |
| 138 | |
| 139 // static | |
| 140 PlatformThreadRef PlatformThread::CurrentRef() { | |
| 141 return PlatformThreadRef(::GetCurrentThreadId()); | |
| 142 } | |
| 143 | |
| 144 // static | |
| 145 PlatformThreadHandle PlatformThread::CurrentHandle() { | |
| 146 return PlatformThreadHandle(::GetCurrentThread()); | |
| 147 } | |
| 148 | |
| 149 // static | |
| 150 void PlatformThread::YieldCurrentThread() { | |
| 151 ::Sleep(0); | |
| 152 } | |
| 153 | |
| 154 // static | |
| 155 void PlatformThread::Sleep(TimeDelta duration) { | |
| 156 // When measured with a high resolution clock, Sleep() sometimes returns much | |
| 157 // too early. We may need to call it repeatedly to get the desired duration. | |
| 158 TimeTicks end = TimeTicks::Now() + duration; | |
| 159 for (TimeTicks now = TimeTicks::Now(); now < end; now = TimeTicks::Now()) | |
| 160 ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp())); | |
| 161 } | |
| 162 | |
| 163 // static | |
| 164 void PlatformThread::SetName(const std::string& name) { | |
| 165 ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); | |
| 166 | |
| 167 // On Windows only, we don't need to tell the profiler about the "BrokerEvent" | |
| 168 // thread, as it exists only in the chrome.exe image, and never spawns or runs | |
| 169 // tasks (items which could be profiled). This test avoids the notification, | |
| 170 // which would also (as a side effect) initialize the profiler in this unused | |
| 171 // context, including setting up thread local storage, etc. The performance | |
| 172 // impact is not terrible, but there is no reason to do initialize it. | |
| 173 if (name != "BrokerEvent") | |
| 174 tracked_objects::ThreadData::InitializeThreadContext(name); | |
| 175 | |
| 176 // The debugger needs to be around to catch the name in the exception. If | |
| 177 // there isn't a debugger, we are just needlessly throwing an exception. | |
| 178 // If this image file is instrumented, we raise the exception anyway | |
| 179 // to provide the profiler with human-readable thread names. | |
| 180 if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) | |
| 181 return; | |
| 182 | |
| 183 SetNameInternal(CurrentId(), name.c_str()); | |
| 184 } | |
| 185 | |
| 186 // static | |
| 187 const char* PlatformThread::GetName() { | |
| 188 return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); | |
| 189 } | |
| 190 | |
| 191 // static | |
| 192 bool PlatformThread::Create(size_t stack_size, Delegate* delegate, | |
| 193 PlatformThreadHandle* thread_handle) { | |
| 194 return CreateWithPriority(stack_size, delegate, thread_handle, | |
| 195 ThreadPriority::NORMAL); | |
| 196 } | |
| 197 | |
| 198 // static | |
| 199 bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, | |
| 200 PlatformThreadHandle* thread_handle, | |
| 201 ThreadPriority priority) { | |
| 202 DCHECK(thread_handle); | |
| 203 return CreateThreadInternal(stack_size, delegate, thread_handle, priority); | |
| 204 } | |
| 205 | |
| 206 // static | |
| 207 bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { | |
| 208 return CreateThreadInternal(stack_size, delegate, NULL, | |
| 209 ThreadPriority::NORMAL); | |
| 210 } | |
| 211 | |
| 212 // static | |
| 213 void PlatformThread::Join(PlatformThreadHandle thread_handle) { | |
| 214 DCHECK(thread_handle.platform_handle()); | |
| 215 // TODO(willchan): Enable this check once I can get it to work for Windows | |
| 216 // shutdown. | |
| 217 // Joining another thread may block the current thread for a long time, since | |
| 218 // the thread referred to by |thread_handle| may still be running long-lived / | |
| 219 // blocking tasks. | |
| 220 #if 0 | |
| 221 base::ThreadRestrictions::AssertIOAllowed(); | |
| 222 #endif | |
| 223 | |
| 224 // Wait for the thread to exit. It should already have terminated but make | |
| 225 // sure this assumption is valid. | |
| 226 DWORD result = WaitForSingleObject(thread_handle.platform_handle(), INFINITE); | |
| 227 if (result != WAIT_OBJECT_0) { | |
| 228 // Debug info for bug 127931. | |
| 229 DWORD error = GetLastError(); | |
| 230 debug::Alias(&error); | |
| 231 debug::Alias(&result); | |
| 232 CHECK(false); | |
| 233 } | |
| 234 | |
| 235 CloseHandle(thread_handle.platform_handle()); | |
| 236 } | |
| 237 | |
| 238 // static | |
| 239 void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { | |
| 240 int desired_priority = THREAD_PRIORITY_ERROR_RETURN; | |
| 241 switch (priority) { | |
| 242 case ThreadPriority::BACKGROUND: | |
| 243 desired_priority = THREAD_PRIORITY_LOWEST; | |
| 244 break; | |
| 245 case ThreadPriority::NORMAL: | |
| 246 desired_priority = THREAD_PRIORITY_NORMAL; | |
| 247 break; | |
| 248 case ThreadPriority::DISPLAY: | |
| 249 desired_priority = THREAD_PRIORITY_ABOVE_NORMAL; | |
| 250 break; | |
| 251 case ThreadPriority::REALTIME_AUDIO: | |
| 252 desired_priority = THREAD_PRIORITY_TIME_CRITICAL; | |
| 253 break; | |
| 254 default: | |
| 255 NOTREACHED() << "Unknown priority."; | |
| 256 break; | |
| 257 } | |
| 258 DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN); | |
| 259 | |
| 260 #ifndef NDEBUG | |
| 261 const BOOL success = | |
| 262 #endif | |
| 263 ::SetThreadPriority(PlatformThread::CurrentHandle().platform_handle(), | |
| 264 desired_priority); | |
| 265 DPLOG_IF(ERROR, !success) << "Failed to set thread priority to " | |
| 266 << desired_priority; | |
| 267 } | |
| 268 | |
| 269 // static | |
| 270 ThreadPriority PlatformThread::GetCurrentThreadPriority() { | |
| 271 int priority = | |
| 272 ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle()); | |
| 273 switch (priority) { | |
| 274 case THREAD_PRIORITY_LOWEST: | |
| 275 return ThreadPriority::BACKGROUND; | |
| 276 case THREAD_PRIORITY_NORMAL: | |
| 277 return ThreadPriority::NORMAL; | |
| 278 case THREAD_PRIORITY_ABOVE_NORMAL: | |
| 279 return ThreadPriority::DISPLAY; | |
| 280 case THREAD_PRIORITY_TIME_CRITICAL: | |
| 281 return ThreadPriority::REALTIME_AUDIO; | |
| 282 case THREAD_PRIORITY_ERROR_RETURN: | |
| 283 DPCHECK(false) << "GetThreadPriority error"; // Falls through. | |
| 284 default: | |
| 285 NOTREACHED() << "Unexpected priority: " << priority; | |
| 286 return ThreadPriority::NORMAL; | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 } // namespace base | |
| OLD | NEW |