Home | History | Annotate | Download | only in threading
      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