michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/threading/platform_thread.h" michael@0: michael@0: #include "base/debug/alias.h" michael@0: #include "base/debug/profiler.h" michael@0: #include "base/logging.h" michael@0: #include "base/threading/thread_id_name_manager.h" michael@0: #include "base/threading/thread_restrictions.h" michael@0: #include "base/tracked_objects.h" michael@0: michael@0: #include "base/win/windows_version.h" michael@0: michael@0: namespace base { michael@0: michael@0: namespace { michael@0: michael@0: // The information on how to set the thread name comes from michael@0: // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx michael@0: const DWORD kVCThreadNameException = 0x406D1388; michael@0: michael@0: typedef struct tagTHREADNAME_INFO { michael@0: DWORD dwType; // Must be 0x1000. michael@0: LPCSTR szName; // Pointer to name (in user addr space). michael@0: DWORD dwThreadID; // Thread ID (-1=caller thread). michael@0: DWORD dwFlags; // Reserved for future use, must be zero. michael@0: } THREADNAME_INFO; michael@0: michael@0: // This function has try handling, so it is separated out of its caller. michael@0: void SetNameInternal(PlatformThreadId thread_id, const char* name) { michael@0: THREADNAME_INFO info; michael@0: info.dwType = 0x1000; michael@0: info.szName = name; michael@0: info.dwThreadID = thread_id; michael@0: info.dwFlags = 0; michael@0: michael@0: __try { michael@0: RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), michael@0: reinterpret_cast(&info)); michael@0: } __except(EXCEPTION_CONTINUE_EXECUTION) { michael@0: } michael@0: } michael@0: michael@0: struct ThreadParams { michael@0: PlatformThread::Delegate* delegate; michael@0: bool joinable; michael@0: }; michael@0: michael@0: DWORD __stdcall ThreadFunc(void* params) { michael@0: ThreadParams* thread_params = static_cast(params); michael@0: PlatformThread::Delegate* delegate = thread_params->delegate; michael@0: if (!thread_params->joinable) michael@0: base::ThreadRestrictions::SetSingletonAllowed(false); michael@0: michael@0: /* Retrieve a copy of the thread handle to use as the key in the michael@0: * thread name mapping. */ michael@0: PlatformThreadHandle::Handle platform_handle; michael@0: DuplicateHandle( michael@0: GetCurrentProcess(), michael@0: GetCurrentThread(), michael@0: GetCurrentProcess(), michael@0: &platform_handle, michael@0: 0, michael@0: FALSE, michael@0: DUPLICATE_SAME_ACCESS); michael@0: michael@0: ThreadIdNameManager::GetInstance()->RegisterThread( michael@0: platform_handle, michael@0: PlatformThread::CurrentId()); michael@0: michael@0: delete thread_params; michael@0: delegate->ThreadMain(); michael@0: michael@0: ThreadIdNameManager::GetInstance()->RemoveName( michael@0: platform_handle, michael@0: PlatformThread::CurrentId()); michael@0: return NULL; michael@0: } michael@0: michael@0: // CreateThreadInternal() matches PlatformThread::Create(), except that michael@0: // |out_thread_handle| may be NULL, in which case a non-joinable thread is michael@0: // created. michael@0: bool CreateThreadInternal(size_t stack_size, michael@0: PlatformThread::Delegate* delegate, michael@0: PlatformThreadHandle* out_thread_handle) { michael@0: unsigned int flags = 0; michael@0: if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) { michael@0: flags = STACK_SIZE_PARAM_IS_A_RESERVATION; michael@0: } else { michael@0: stack_size = 0; michael@0: } michael@0: michael@0: ThreadParams* params = new ThreadParams; michael@0: params->delegate = delegate; michael@0: params->joinable = out_thread_handle != NULL; michael@0: michael@0: // Using CreateThread here vs _beginthreadex makes thread creation a bit michael@0: // faster and doesn't require the loader lock to be available. Our code will michael@0: // have to work running on CreateThread() threads anyway, since we run code michael@0: // on the Windows thread pool, etc. For some background on the difference: michael@0: // http://www.microsoft.com/msj/1099/win32/win321099.aspx michael@0: void* thread_handle = CreateThread( michael@0: NULL, stack_size, ThreadFunc, params, flags, NULL); michael@0: if (!thread_handle) { michael@0: delete params; michael@0: return false; michael@0: } michael@0: michael@0: if (out_thread_handle) michael@0: *out_thread_handle = PlatformThreadHandle(thread_handle); michael@0: else michael@0: CloseHandle(thread_handle); michael@0: return true; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: // static michael@0: PlatformThreadId PlatformThread::CurrentId() { michael@0: return GetCurrentThreadId(); michael@0: } michael@0: michael@0: // static michael@0: PlatformThreadHandle PlatformThread::CurrentHandle() { michael@0: NOTIMPLEMENTED(); // See OpenThread() michael@0: return PlatformThreadHandle(); michael@0: } michael@0: michael@0: // static michael@0: void PlatformThread::YieldCurrentThread() { michael@0: ::Sleep(0); michael@0: } michael@0: michael@0: // static michael@0: void PlatformThread::Sleep(TimeDelta duration) { michael@0: // When measured with a high resolution clock, Sleep() sometimes returns much michael@0: // too early. We may need to call it repeatedly to get the desired duration. michael@0: TimeTicks end = TimeTicks::Now() + duration; michael@0: TimeTicks now; michael@0: while ((now = TimeTicks::Now()) < end) michael@0: ::Sleep((end - now).InMillisecondsRoundedUp()); michael@0: } michael@0: michael@0: // static michael@0: void PlatformThread::SetName(const char* name) { michael@0: ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); michael@0: michael@0: // On Windows only, we don't need to tell the profiler about the "BrokerEvent" michael@0: // thread, as it exists only in the chrome.exe image, and never spawns or runs michael@0: // tasks (items which could be profiled). This test avoids the notification, michael@0: // which would also (as a side effect) initialize the profiler in this unused michael@0: // context, including setting up thread local storage, etc. The performance michael@0: // impact is not terrible, but there is no reason to do initialize it. michael@0: if (0 != strcmp(name, "BrokerEvent")) michael@0: tracked_objects::ThreadData::InitializeThreadContext(name); michael@0: michael@0: // The debugger needs to be around to catch the name in the exception. If michael@0: // there isn't a debugger, we are just needlessly throwing an exception. michael@0: // If this image file is instrumented, we raise the exception anyway michael@0: // to provide the profiler with human-readable thread names. michael@0: if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) michael@0: return; michael@0: michael@0: SetNameInternal(CurrentId(), name); michael@0: } michael@0: michael@0: // static michael@0: const char* PlatformThread::GetName() { michael@0: return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); michael@0: } michael@0: michael@0: // static michael@0: bool PlatformThread::Create(size_t stack_size, Delegate* delegate, michael@0: PlatformThreadHandle* thread_handle) { michael@0: DCHECK(thread_handle); michael@0: return CreateThreadInternal(stack_size, delegate, thread_handle); michael@0: } michael@0: michael@0: // static michael@0: bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, michael@0: PlatformThreadHandle* thread_handle, michael@0: ThreadPriority priority) { michael@0: bool result = Create(stack_size, delegate, thread_handle); michael@0: if (result) michael@0: SetThreadPriority(*thread_handle, priority); michael@0: return result; michael@0: } michael@0: michael@0: // static michael@0: bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { michael@0: return CreateThreadInternal(stack_size, delegate, NULL); michael@0: } michael@0: michael@0: // static michael@0: void PlatformThread::Join(PlatformThreadHandle thread_handle) { michael@0: DCHECK(thread_handle.handle_); michael@0: // TODO(willchan): Enable this check once I can get it to work for Windows michael@0: // shutdown. michael@0: // Joining another thread may block the current thread for a long time, since michael@0: // the thread referred to by |thread_handle| may still be running long-lived / michael@0: // blocking tasks. michael@0: #if 0 michael@0: base::ThreadRestrictions::AssertIOAllowed(); michael@0: #endif michael@0: michael@0: // Wait for the thread to exit. It should already have terminated but make michael@0: // sure this assumption is valid. michael@0: DWORD result = WaitForSingleObject(thread_handle.handle_, INFINITE); michael@0: if (result != WAIT_OBJECT_0) { michael@0: // Debug info for bug 127931. michael@0: DWORD error = GetLastError(); michael@0: debug::Alias(&error); michael@0: debug::Alias(&result); michael@0: debug::Alias(&thread_handle.handle_); michael@0: CHECK(false); michael@0: } michael@0: michael@0: CloseHandle(thread_handle.handle_); michael@0: } michael@0: michael@0: // static michael@0: void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, michael@0: ThreadPriority priority) { michael@0: switch (priority) { michael@0: case kThreadPriority_Normal: michael@0: ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_NORMAL); michael@0: break; michael@0: case kThreadPriority_RealtimeAudio: michael@0: ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_TIME_CRITICAL); michael@0: break; michael@0: default: michael@0: NOTREACHED() << "Unknown priority."; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: } // namespace base