|
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 |
|
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 DuplicateHandle( |
|
61 GetCurrentProcess(), |
|
62 GetCurrentThread(), |
|
63 GetCurrentProcess(), |
|
64 &platform_handle, |
|
65 0, |
|
66 FALSE, |
|
67 DUPLICATE_SAME_ACCESS); |
|
68 |
|
69 ThreadIdNameManager::GetInstance()->RegisterThread( |
|
70 platform_handle, |
|
71 PlatformThread::CurrentId()); |
|
72 |
|
73 delete thread_params; |
|
74 delegate->ThreadMain(); |
|
75 |
|
76 ThreadIdNameManager::GetInstance()->RemoveName( |
|
77 platform_handle, |
|
78 PlatformThread::CurrentId()); |
|
79 return NULL; |
|
80 } |
|
81 |
|
82 // CreateThreadInternal() matches PlatformThread::Create(), except that |
|
83 // |out_thread_handle| may be NULL, in which case a non-joinable thread is |
|
84 // created. |
|
85 bool CreateThreadInternal(size_t stack_size, |
|
86 PlatformThread::Delegate* delegate, |
|
87 PlatformThreadHandle* out_thread_handle) { |
|
88 unsigned int flags = 0; |
|
89 if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) { |
|
90 flags = STACK_SIZE_PARAM_IS_A_RESERVATION; |
|
91 } else { |
|
92 stack_size = 0; |
|
93 } |
|
94 |
|
95 ThreadParams* params = new ThreadParams; |
|
96 params->delegate = delegate; |
|
97 params->joinable = out_thread_handle != NULL; |
|
98 |
|
99 // Using CreateThread here vs _beginthreadex makes thread creation a bit |
|
100 // faster and doesn't require the loader lock to be available. Our code will |
|
101 // have to work running on CreateThread() threads anyway, since we run code |
|
102 // on the Windows thread pool, etc. For some background on the difference: |
|
103 // http://www.microsoft.com/msj/1099/win32/win321099.aspx |
|
104 void* thread_handle = CreateThread( |
|
105 NULL, stack_size, ThreadFunc, params, flags, NULL); |
|
106 if (!thread_handle) { |
|
107 delete params; |
|
108 return false; |
|
109 } |
|
110 |
|
111 if (out_thread_handle) |
|
112 *out_thread_handle = PlatformThreadHandle(thread_handle); |
|
113 else |
|
114 CloseHandle(thread_handle); |
|
115 return true; |
|
116 } |
|
117 |
|
118 } // namespace |
|
119 |
|
120 // static |
|
121 PlatformThreadId PlatformThread::CurrentId() { |
|
122 return GetCurrentThreadId(); |
|
123 } |
|
124 |
|
125 // static |
|
126 PlatformThreadHandle PlatformThread::CurrentHandle() { |
|
127 NOTIMPLEMENTED(); // See OpenThread() |
|
128 return PlatformThreadHandle(); |
|
129 } |
|
130 |
|
131 // static |
|
132 void PlatformThread::YieldCurrentThread() { |
|
133 ::Sleep(0); |
|
134 } |
|
135 |
|
136 // static |
|
137 void PlatformThread::Sleep(TimeDelta duration) { |
|
138 // When measured with a high resolution clock, Sleep() sometimes returns much |
|
139 // too early. We may need to call it repeatedly to get the desired duration. |
|
140 TimeTicks end = TimeTicks::Now() + duration; |
|
141 TimeTicks now; |
|
142 while ((now = TimeTicks::Now()) < end) |
|
143 ::Sleep((end - now).InMillisecondsRoundedUp()); |
|
144 } |
|
145 |
|
146 // static |
|
147 void PlatformThread::SetName(const char* name) { |
|
148 ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); |
|
149 |
|
150 // On Windows only, we don't need to tell the profiler about the "BrokerEvent" |
|
151 // thread, as it exists only in the chrome.exe image, and never spawns or runs |
|
152 // tasks (items which could be profiled). This test avoids the notification, |
|
153 // which would also (as a side effect) initialize the profiler in this unused |
|
154 // context, including setting up thread local storage, etc. The performance |
|
155 // impact is not terrible, but there is no reason to do initialize it. |
|
156 if (0 != strcmp(name, "BrokerEvent")) |
|
157 tracked_objects::ThreadData::InitializeThreadContext(name); |
|
158 |
|
159 // The debugger needs to be around to catch the name in the exception. If |
|
160 // there isn't a debugger, we are just needlessly throwing an exception. |
|
161 // If this image file is instrumented, we raise the exception anyway |
|
162 // to provide the profiler with human-readable thread names. |
|
163 if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) |
|
164 return; |
|
165 |
|
166 SetNameInternal(CurrentId(), name); |
|
167 } |
|
168 |
|
169 // static |
|
170 const char* PlatformThread::GetName() { |
|
171 return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); |
|
172 } |
|
173 |
|
174 // static |
|
175 bool PlatformThread::Create(size_t stack_size, Delegate* delegate, |
|
176 PlatformThreadHandle* thread_handle) { |
|
177 DCHECK(thread_handle); |
|
178 return CreateThreadInternal(stack_size, delegate, thread_handle); |
|
179 } |
|
180 |
|
181 // static |
|
182 bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, |
|
183 PlatformThreadHandle* thread_handle, |
|
184 ThreadPriority priority) { |
|
185 bool result = Create(stack_size, delegate, thread_handle); |
|
186 if (result) |
|
187 SetThreadPriority(*thread_handle, priority); |
|
188 return result; |
|
189 } |
|
190 |
|
191 // static |
|
192 bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { |
|
193 return CreateThreadInternal(stack_size, delegate, NULL); |
|
194 } |
|
195 |
|
196 // static |
|
197 void PlatformThread::Join(PlatformThreadHandle thread_handle) { |
|
198 DCHECK(thread_handle.handle_); |
|
199 // TODO(willchan): Enable this check once I can get it to work for Windows |
|
200 // shutdown. |
|
201 // Joining another thread may block the current thread for a long time, since |
|
202 // the thread referred to by |thread_handle| may still be running long-lived / |
|
203 // blocking tasks. |
|
204 #if 0 |
|
205 base::ThreadRestrictions::AssertIOAllowed(); |
|
206 #endif |
|
207 |
|
208 // Wait for the thread to exit. It should already have terminated but make |
|
209 // sure this assumption is valid. |
|
210 DWORD result = WaitForSingleObject(thread_handle.handle_, INFINITE); |
|
211 if (result != WAIT_OBJECT_0) { |
|
212 // Debug info for bug 127931. |
|
213 DWORD error = GetLastError(); |
|
214 debug::Alias(&error); |
|
215 debug::Alias(&result); |
|
216 debug::Alias(&thread_handle.handle_); |
|
217 CHECK(false); |
|
218 } |
|
219 |
|
220 CloseHandle(thread_handle.handle_); |
|
221 } |
|
222 |
|
223 // static |
|
224 void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, |
|
225 ThreadPriority priority) { |
|
226 switch (priority) { |
|
227 case kThreadPriority_Normal: |
|
228 ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_NORMAL); |
|
229 break; |
|
230 case kThreadPriority_RealtimeAudio: |
|
231 ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_TIME_CRITICAL); |
|
232 break; |
|
233 default: |
|
234 NOTREACHED() << "Unknown priority."; |
|
235 break; |
|
236 } |
|
237 } |
|
238 |
|
239 } // namespace base |