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