| 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 }; | |
| 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 PlatformThreadId thread_id; | |
| 112 void* thread_handle = CreateThread( | |
| 113 NULL, stack_size, ThreadFunc, params, flags, &thread_id); | |
| 114 if (!thread_handle) { | |
| 115 delete params; | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 if (out_thread_handle) | |
| 120 *out_thread_handle = PlatformThreadHandle(thread_handle, thread_id); | |
| 121 else | |
| 122 CloseHandle(thread_handle); | |
| 123 return true; | |
| 124 } | |
| 125 | |
| 126 } // namespace | |
| 127 | |
| 128 // static | |
| 129 PlatformThreadId PlatformThread::CurrentId() { | |
| 130 return ::GetCurrentThreadId(); | |
| 131 } | |
| 132 | |
| 133 // static | |
| 134 PlatformThreadRef PlatformThread::CurrentRef() { | |
| 135 return PlatformThreadRef(::GetCurrentThreadId()); | |
| 136 } | |
| 137 | |
| 138 // static | |
| 139 PlatformThreadHandle PlatformThread::CurrentHandle() { | |
| 140 return PlatformThreadHandle(::GetCurrentThread()); | |
| 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 for (TimeTicks now = TimeTicks::Now(); now < end; now = TimeTicks::Now()) | |
| 154 ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp())); | |
| 155 } | |
| 156 | |
| 157 // static | |
| 158 void PlatformThread::SetName(const std::string& name) { | |
| 159 ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); | |
| 160 | |
| 161 // On Windows only, we don't need to tell the profiler about the "BrokerEvent" | |
| 162 // thread, as it exists only in the chrome.exe image, and never spawns or runs | |
| 163 // tasks (items which could be profiled). This test avoids the notification, | |
| 164 // which would also (as a side effect) initialize the profiler in this unused | |
| 165 // context, including setting up thread local storage, etc. The performance | |
| 166 // impact is not terrible, but there is no reason to do initialize it. | |
| 167 if (name != "BrokerEvent") | |
| 168 tracked_objects::ThreadData::InitializeThreadContext(name); | |
| 169 | |
| 170 // The debugger needs to be around to catch the name in the exception. If | |
| 171 // there isn't a debugger, we are just needlessly throwing an exception. | |
| 172 // If this image file is instrumented, we raise the exception anyway | |
| 173 // to provide the profiler with human-readable thread names. | |
| 174 if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) | |
| 175 return; | |
| 176 | |
| 177 SetNameInternal(CurrentId(), name.c_str()); | |
| 178 } | |
| 179 | |
| 180 // static | |
| 181 const char* PlatformThread::GetName() { | |
| 182 return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); | |
| 183 } | |
| 184 | |
| 185 // static | |
| 186 bool PlatformThread::Create(size_t stack_size, Delegate* delegate, | |
| 187 PlatformThreadHandle* thread_handle) { | |
| 188 DCHECK(thread_handle); | |
| 189 return CreateThreadInternal(stack_size, delegate, thread_handle); | |
| 190 } | |
| 191 | |
| 192 // static | |
| 193 bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, | |
| 194 PlatformThreadHandle* thread_handle, | |
| 195 ThreadPriority priority) { | |
| 196 bool result = Create(stack_size, delegate, thread_handle); | |
| 197 if (result) | |
| 198 SetThreadPriority(*thread_handle, priority); | |
| 199 return result; | |
| 200 } | |
| 201 | |
| 202 // static | |
| 203 bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { | |
| 204 return CreateThreadInternal(stack_size, delegate, NULL); | |
| 205 } | |
| 206 | |
| 207 // static | |
| 208 void PlatformThread::Join(PlatformThreadHandle thread_handle) { | |
| 209 DCHECK(thread_handle.platform_handle()); | |
| 210 // TODO(willchan): Enable this check once I can get it to work for Windows | |
| 211 // shutdown. | |
| 212 // Joining another thread may block the current thread for a long time, since | |
| 213 // the thread referred to by |thread_handle| may still be running long-lived / | |
| 214 // blocking tasks. | |
| 215 #if 0 | |
| 216 base::ThreadRestrictions::AssertIOAllowed(); | |
| 217 #endif | |
| 218 | |
| 219 // Wait for the thread to exit. It should already have terminated but make | |
| 220 // sure this assumption is valid. | |
| 221 DWORD result = WaitForSingleObject(thread_handle.platform_handle(), INFINITE); | |
| 222 if (result != WAIT_OBJECT_0) { | |
| 223 // Debug info for bug 127931. | |
| 224 DWORD error = GetLastError(); | |
| 225 debug::Alias(&error); | |
| 226 debug::Alias(&result); | |
| 227 CHECK(false); | |
| 228 } | |
| 229 | |
| 230 CloseHandle(thread_handle.platform_handle()); | |
| 231 } | |
| 232 | |
| 233 // static | |
| 234 void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, | |
| 235 ThreadPriority priority) { | |
| 236 DCHECK(!handle.is_null()); | |
| 237 | |
| 238 int desired_priority = THREAD_PRIORITY_ERROR_RETURN; | |
| 239 switch (priority) { | |
| 240 case ThreadPriority::BACKGROUND: | |
| 241 desired_priority = THREAD_PRIORITY_LOWEST; | |
| 242 break; | |
| 243 case ThreadPriority::NORMAL: | |
| 244 desired_priority = THREAD_PRIORITY_NORMAL; | |
| 245 break; | |
| 246 case ThreadPriority::DISPLAY: | |
| 247 desired_priority = THREAD_PRIORITY_ABOVE_NORMAL; | |
| 248 break; | |
| 249 case ThreadPriority::REALTIME_AUDIO: | |
| 250 desired_priority = THREAD_PRIORITY_TIME_CRITICAL; | |
| 251 break; | |
| 252 default: | |
| 253 NOTREACHED() << "Unknown priority."; | |
| 254 break; | |
| 255 } | |
| 256 DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN); | |
| 257 | |
| 258 #ifndef NDEBUG | |
| 259 const BOOL success = | |
| 260 #endif | |
| 261 ::SetThreadPriority(handle.platform_handle(), desired_priority); | |
| 262 DPLOG_IF(ERROR, !success) << "Failed to set thread priority to " | |
| 263 << desired_priority; | |
| 264 } | |
| 265 | |
| 266 // static | |
| 267 ThreadPriority PlatformThread::GetThreadPriority(PlatformThreadHandle handle) { | |
| 268 DCHECK(!handle.is_null()); | |
| 269 | |
| 270 int priority = ::GetThreadPriority(handle.platform_handle()); | |
| 271 switch (priority) { | |
| 272 case THREAD_PRIORITY_LOWEST: | |
| 273 return ThreadPriority::BACKGROUND; | |
| 274 case THREAD_PRIORITY_NORMAL: | |
| 275 return ThreadPriority::NORMAL; | |
| 276 case THREAD_PRIORITY_ABOVE_NORMAL: | |
| 277 return ThreadPriority::DISPLAY; | |
| 278 case THREAD_PRIORITY_TIME_CRITICAL: | |
| 279 return ThreadPriority::REALTIME_AUDIO; | |
| 280 case THREAD_PRIORITY_ERROR_RETURN: | |
| 281 DPCHECK(false) << "GetThreadPriority error"; // Falls through. | |
| 282 default: | |
| 283 NOTREACHED() << "Unexpected priority: " << priority; | |
| 284 return ThreadPriority::NORMAL; | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 } // namespace base | |
| OLD | NEW |