1// Copyright (c) 2010 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/logging.h"
8#include "base/threading/thread_restrictions.h"
9#include "base/win/windows_version.h"
10
11namespace base {
12
13namespace {
14
15// The information on how to set the thread name comes from
16// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
17const DWORD kVCThreadNameException = 0x406D1388;
18
19typedef struct tagTHREADNAME_INFO {
20  DWORD dwType;  // Must be 0x1000.
21  LPCSTR szName;  // Pointer to name (in user addr space).
22  DWORD dwThreadID;  // Thread ID (-1=caller thread).
23  DWORD dwFlags;  // Reserved for future use, must be zero.
24} THREADNAME_INFO;
25
26struct ThreadParams {
27  PlatformThread::Delegate* delegate;
28  bool joinable;
29};
30
31DWORD __stdcall ThreadFunc(void* params) {
32  ThreadParams* thread_params = static_cast<ThreadParams*>(params);
33  PlatformThread::Delegate* delegate = thread_params->delegate;
34  if (!thread_params->joinable)
35    base::ThreadRestrictions::SetSingletonAllowed(false);
36  delete thread_params;
37  delegate->ThreadMain();
38  return NULL;
39}
40
41// CreateThreadInternal() matches PlatformThread::Create(), except that
42// |out_thread_handle| may be NULL, in which case a non-joinable thread is
43// created.
44bool CreateThreadInternal(size_t stack_size,
45                          PlatformThread::Delegate* delegate,
46                          PlatformThreadHandle* out_thread_handle) {
47  PlatformThreadHandle thread_handle;
48  unsigned int flags = 0;
49  if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) {
50    flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
51  } else {
52    stack_size = 0;
53  }
54
55  ThreadParams* params = new ThreadParams;
56  params->delegate = delegate;
57  params->joinable = out_thread_handle != NULL;
58
59  // Using CreateThread here vs _beginthreadex makes thread creation a bit
60  // faster and doesn't require the loader lock to be available.  Our code will
61  // have to work running on CreateThread() threads anyway, since we run code
62  // on the Windows thread pool, etc.  For some background on the difference:
63  //   http://www.microsoft.com/msj/1099/win32/win321099.aspx
64  thread_handle = CreateThread(
65      NULL, stack_size, ThreadFunc, params, flags, NULL);
66  if (!thread_handle) {
67    delete params;
68    return false;
69  }
70
71  if (out_thread_handle)
72    *out_thread_handle = thread_handle;
73  else
74    CloseHandle(thread_handle);
75  return true;
76}
77
78}  // namespace
79
80// static
81PlatformThreadId PlatformThread::CurrentId() {
82  return GetCurrentThreadId();
83}
84
85// static
86void PlatformThread::YieldCurrentThread() {
87  ::Sleep(0);
88}
89
90// static
91void PlatformThread::Sleep(int duration_ms) {
92  ::Sleep(duration_ms);
93}
94
95// static
96void PlatformThread::SetName(const char* name) {
97  // The debugger needs to be around to catch the name in the exception.  If
98  // there isn't a debugger, we are just needlessly throwing an exception.
99  if (!::IsDebuggerPresent())
100    return;
101
102  THREADNAME_INFO info;
103  info.dwType = 0x1000;
104  info.szName = name;
105  info.dwThreadID = CurrentId();
106  info.dwFlags = 0;
107
108  __try {
109    RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
110                   reinterpret_cast<DWORD_PTR*>(&info));
111  } __except(EXCEPTION_CONTINUE_EXECUTION) {
112  }
113}
114
115// static
116bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
117                            PlatformThreadHandle* thread_handle) {
118  DCHECK(thread_handle);
119  return CreateThreadInternal(stack_size, delegate, thread_handle);
120}
121
122// static
123bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
124  return CreateThreadInternal(stack_size, delegate, NULL);
125}
126
127// static
128void PlatformThread::Join(PlatformThreadHandle thread_handle) {
129  DCHECK(thread_handle);
130  // TODO(willchan): Enable this check once I can get it to work for Windows
131  // shutdown.
132  // Joining another thread may block the current thread for a long time, since
133  // the thread referred to by |thread_handle| may still be running long-lived /
134  // blocking tasks.
135#if 0
136  base::ThreadRestrictions::AssertIOAllowed();
137#endif
138
139  // Wait for the thread to exit.  It should already have terminated but make
140  // sure this assumption is valid.
141  DWORD result = WaitForSingleObject(thread_handle, INFINITE);
142  DCHECK_EQ(WAIT_OBJECT_0, result);
143
144  CloseHandle(thread_handle);
145}
146
147}  // namespace base
148