source: webkit/trunk/JavaScriptCore/wtf/ThreadingWin.cpp@ 41605

Last change on this file since 41605 was 41605, checked in by Darin Adler, 16 years ago

2009-03-11 Darin Adler <Darin Adler>

Reviewed by Mark Rowe.

Give threads names on platforms with pthread_setname_np.

  • wtf/Threading.cpp: (WTF::NewThreadContext::NewThreadContext): Initialize thread name. (WTF::threadEntryPoint): Call setThreadNameInternal. (WTF::createThread): Pass thread name.
  • wtf/Threading.h: Added new comments, setThreadNameInternal.
  • wtf/ThreadingGtk.cpp: (WTF::setThreadNameInternal): Added. Empty.
  • wtf/ThreadingNone.cpp: (WTF::setThreadNameInternal): Added. Empty.
  • wtf/ThreadingPthreads.cpp: (WTF::setThreadNameInternal): Call pthread_setname_np when available.
  • wtf/ThreadingQt.cpp: (WTF::setThreadNameInternal): Added. Empty.
  • wtf/ThreadingWin.cpp: (WTF::setThreadNameInternal): Added. Empty.
  • Property svn:eol-style set to native
File size: 16.4 KB
Line 
1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/*
31 * There are numerous academic and practical works on how to implement pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast
32 * functions on Win32. Here is one example: http://www.cs.wustl.edu/~schmidt/win32-cv-1.html which is widely credited as a 'starting point'
33 * of modern attempts. There are several more or less proven implementations, one in Boost C++ library (http://www.boost.org) and another
34 * in pthreads-win32 (http://sourceware.org/pthreads-win32/).
35 *
36 * The number of articles and discussions is the evidence of significant difficulties in implementing these primitives correctly.
37 * The brief search of revisions, ChangeLog entries, discussions in comp.programming.threads and other places clearly documents
38 * numerous pitfalls and performance problems the authors had to overcome to arrive to the suitable implementations.
39 * Optimally, WebKit would use one of those supported/tested libraries directly. To roll out our own implementation is impractical,
40 * if even for the lack of sufficient testing. However, a faithful reproduction of the code from one of the popular supported
41 * libraries seems to be a good compromise.
42 *
43 * The early Boost implementation (http://www.boxbackup.org/trac/browser/box/nick/win/lib/win32/boost_1_32_0/libs/thread/src/condition.cpp?rev=30)
44 * is identical to pthreads-win32 (http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32).
45 * Current Boost uses yet another (although seemingly equivalent) algorithm which came from their 'thread rewrite' effort.
46 *
47 * This file includes timedWait/signal/broadcast implementations translated to WebKit coding style from the latest algorithm by
48 * Alexander Terekhov and Louis Thomas, as captured here: http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32
49 * It replaces the implementation of their previous algorithm, also documented in the same source above.
50 * The naming and comments are left very close to original to enable easy cross-check.
51 *
52 * The corresponding Pthreads-win32 License is included below, and CONTRIBUTORS file which it refers to is added to
53 * source directory (as CONTRIBUTORS.pthreads-win32).
54 */
55
56/*
57 * Pthreads-win32 - POSIX Threads Library for Win32
58 * Copyright(C) 1998 John E. Bossom
59 * Copyright(C) 1999,2005 Pthreads-win32 contributors
60 *
61 * Contact Email: [email protected]
62 *
63 * The current list of contributors is contained
64 * in the file CONTRIBUTORS included with the source
65 * code distribution. The list can also be seen at the
66 * following World Wide Web location:
67 * http://sources.redhat.com/pthreads-win32/contributors.html
68 *
69 * This library is free software; you can redistribute it and/or
70 * modify it under the terms of the GNU Lesser General Public
71 * License as published by the Free Software Foundation; either
72 * version 2 of the License, or (at your option) any later version.
73 *
74 * This library is distributed in the hope that it will be useful,
75 * but WITHOUT ANY WARRANTY; without even the implied warranty of
76 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
77 * Lesser General Public License for more details.
78 *
79 * You should have received a copy of the GNU Lesser General Public
80 * License along with this library in the file COPYING.LIB;
81 * if not, write to the Free Software Foundation, Inc.,
82 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
83 */
84
85#include "config.h"
86#include "Threading.h"
87
88#include "MainThread.h"
89#if !USE(PTHREADS) && PLATFORM(WIN_OS)
90#include "ThreadSpecific.h"
91#endif
92#include <process.h>
93#include <windows.h>
94#include <wtf/CurrentTime.h>
95#include <wtf/HashMap.h>
96#include <wtf/MathExtras.h>
97#include <wtf/RandomNumberSeed.h>
98
99namespace WTF {
100
101// MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadName all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>.
102static const DWORD MS_VC_EXCEPTION = 0x406D1388;
103
104#pragma pack(push, 8)
105typedef struct tagTHREADNAME_INFO {
106 DWORD dwType; // must be 0x1000
107 LPCSTR szName; // pointer to name (in user addr space)
108 DWORD dwThreadID; // thread ID (-1=caller thread)
109 DWORD dwFlags; // reserved for future use, must be zero
110} THREADNAME_INFO;
111#pragma pack(pop)
112
113static void setThreadName(DWORD dwThreadID, LPCSTR szThreadName)
114{
115 // Visual Studio has a 31-character limit on thread names. Longer names will
116 // be truncated silently, but we'd like callers to know about the limit.
117 ASSERT_ARG(szThreadName, strlen(szThreadName) <= 31);
118
119 THREADNAME_INFO info;
120 info.dwType = 0x1000;
121 info.szName = szThreadName;
122 info.dwThreadID = dwThreadID;
123 info.dwFlags = 0;
124
125 __try {
126 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
127 } __except (EXCEPTION_CONTINUE_EXECUTION) {
128 }
129}
130
131static Mutex* atomicallyInitializedStaticMutex;
132
133void lockAtomicallyInitializedStaticMutex()
134{
135 ASSERT(atomicallyInitializedStaticMutex);
136 atomicallyInitializedStaticMutex->lock();
137}
138
139void unlockAtomicallyInitializedStaticMutex()
140{
141 atomicallyInitializedStaticMutex->unlock();
142}
143
144static ThreadIdentifier mainThreadIdentifier;
145
146static Mutex& threadMapMutex()
147{
148 static Mutex mutex;
149 return mutex;
150}
151
152void initializeThreading()
153{
154 if (!atomicallyInitializedStaticMutex) {
155 atomicallyInitializedStaticMutex = new Mutex;
156 threadMapMutex();
157 initializeRandomNumberGenerator();
158 initializeMainThread();
159 mainThreadIdentifier = currentThread();
160 setThreadName(mainThreadIdentifier, "Main Thread");
161 }
162}
163
164static HashMap<DWORD, HANDLE>& threadMap()
165{
166 static HashMap<DWORD, HANDLE> map;
167 return map;
168}
169
170static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
171{
172 MutexLocker locker(threadMapMutex());
173 ASSERT(!threadMap().contains(threadID));
174 threadMap().add(threadID, threadHandle);
175}
176
177static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
178{
179 MutexLocker locker(threadMapMutex());
180 return threadMap().get(id);
181}
182
183static void clearThreadHandleForIdentifier(ThreadIdentifier id)
184{
185 MutexLocker locker(threadMapMutex());
186 ASSERT(threadMap().contains(id));
187 threadMap().remove(id);
188}
189
190struct ThreadFunctionInvocation {
191 ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {}
192
193 ThreadFunction function;
194 void* data;
195};
196
197static unsigned __stdcall wtfThreadEntryPoint(void* param)
198{
199 ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param);
200 delete static_cast<ThreadFunctionInvocation*>(param);
201
202 void* result = invocation.function(invocation.data);
203
204#if !USE(PTHREADS) && PLATFORM(WIN_OS)
205 // Do the TLS cleanup.
206 ThreadSpecificThreadExit();
207#endif
208
209 return reinterpret_cast<unsigned>(result);
210}
211
212ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char* threadName)
213{
214 unsigned threadIdentifier = 0;
215 ThreadIdentifier threadID = 0;
216 ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data);
217 HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier));
218 if (!threadHandle) {
219 LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
220 return 0;
221 }
222
223 if (threadName)
224 setThreadName(threadIdentifier, threadName);
225
226 threadID = static_cast<ThreadIdentifier>(threadIdentifier);
227 storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
228
229 return threadID;
230}
231
232void setThreadNameInternal(const char*)
233{
234}
235
236int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
237{
238 ASSERT(threadID);
239
240 HANDLE threadHandle = threadHandleForIdentifier(threadID);
241 if (!threadHandle)
242 LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
243
244 DWORD joinResult = WaitForSingleObject(threadHandle, INFINITE);
245 if (joinResult == WAIT_FAILED)
246 LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
247
248 CloseHandle(threadHandle);
249 clearThreadHandleForIdentifier(threadID);
250
251 return joinResult;
252}
253
254void detachThread(ThreadIdentifier threadID)
255{
256 ASSERT(threadID);
257
258 HANDLE threadHandle = threadHandleForIdentifier(threadID);
259 if (threadHandle)
260 CloseHandle(threadHandle);
261 clearThreadHandleForIdentifier(threadID);
262}
263
264ThreadIdentifier currentThread()
265{
266 return static_cast<ThreadIdentifier>(GetCurrentThreadId());
267}
268
269bool isMainThread()
270{
271 return currentThread() == mainThreadIdentifier;
272}
273
274Mutex::Mutex()
275{
276 m_mutex.m_recursionCount = 0;
277 InitializeCriticalSection(&m_mutex.m_internalMutex);
278}
279
280Mutex::~Mutex()
281{
282 DeleteCriticalSection(&m_mutex.m_internalMutex);
283}
284
285void Mutex::lock()
286{
287 EnterCriticalSection(&m_mutex.m_internalMutex);
288 ++m_mutex.m_recursionCount;
289}
290
291bool Mutex::tryLock()
292{
293 // This method is modeled after the behavior of pthread_mutex_trylock,
294 // which will return an error if the lock is already owned by the
295 // current thread. Since the primitive Win32 'TryEnterCriticalSection'
296 // treats this as a successful case, it changes the behavior of several
297 // tests in WebKit that check to see if the current thread already
298 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
299 DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex);
300
301 if (result != 0) { // We got the lock
302 // If this thread already had the lock, we must unlock and
303 // return false so that we mimic the behavior of POSIX's
304 // pthread_mutex_trylock:
305 if (m_mutex.m_recursionCount > 0) {
306 LeaveCriticalSection(&m_mutex.m_internalMutex);
307 return false;
308 }
309
310 ++m_mutex.m_recursionCount;
311 return true;
312 }
313
314 return false;
315}
316
317void Mutex::unlock()
318{
319 --m_mutex.m_recursionCount;
320 LeaveCriticalSection(&m_mutex.m_internalMutex);
321}
322
323bool PlatformCondition::timedWait(PlatformMutex& mutex, DWORD durationMilliseconds)
324{
325 // Enter the wait state.
326 DWORD res = WaitForSingleObject(m_blockLock, INFINITE);
327 ASSERT(res == WAIT_OBJECT_0);
328 ++m_waitersBlocked;
329 res = ReleaseSemaphore(m_blockLock, 1, 0);
330 ASSERT(res);
331
332 LeaveCriticalSection(&mutex.m_internalMutex);
333
334 // Main wait - use timeout.
335 bool timedOut = (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT);
336
337 res = WaitForSingleObject(m_unblockLock, INFINITE);
338 ASSERT(res == WAIT_OBJECT_0);
339
340 int signalsLeft = m_waitersToUnblock;
341
342 if (m_waitersToUnblock)
343 --m_waitersToUnblock;
344 else if (++m_waitersGone == (INT_MAX / 2)) { // timeout/canceled or spurious semaphore
345 // timeout or spurious wakeup occured, normalize the m_waitersGone count
346 // this may occur if many calls to wait with a timeout are made and
347 // no call to notify_* is made
348 res = WaitForSingleObject(m_blockLock, INFINITE);
349 ASSERT(res == WAIT_OBJECT_0);
350 m_waitersBlocked -= m_waitersGone;
351 res = ReleaseSemaphore(m_blockLock, 1, 0);
352 ASSERT(res);
353 m_waitersGone = 0;
354 }
355
356 res = ReleaseMutex(m_unblockLock);
357 ASSERT(res);
358
359 if (signalsLeft == 1) {
360 res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate.
361 ASSERT(res);
362 }
363
364 EnterCriticalSection (&mutex.m_internalMutex);
365
366 return !timedOut;
367}
368
369void PlatformCondition::signal(bool unblockAll)
370{
371 unsigned signalsToIssue = 0;
372
373 DWORD res = WaitForSingleObject(m_unblockLock, INFINITE);
374 ASSERT(res == WAIT_OBJECT_0);
375
376 if (m_waitersToUnblock) { // the gate is already closed
377 if (!m_waitersBlocked) { // no-op
378 res = ReleaseMutex(m_unblockLock);
379 ASSERT(res);
380 return;
381 }
382
383 if (unblockAll) {
384 signalsToIssue = m_waitersBlocked;
385 m_waitersToUnblock += m_waitersBlocked;
386 m_waitersBlocked = 0;
387 } else {
388 signalsToIssue = 1;
389 ++m_waitersToUnblock;
390 --m_waitersBlocked;
391 }
392 } else if (m_waitersBlocked > m_waitersGone) {
393 res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate.
394 ASSERT(res == WAIT_OBJECT_0);
395 if (m_waitersGone != 0) {
396 m_waitersBlocked -= m_waitersGone;
397 m_waitersGone = 0;
398 }
399 if (unblockAll) {
400 signalsToIssue = m_waitersBlocked;
401 m_waitersToUnblock = m_waitersBlocked;
402 m_waitersBlocked = 0;
403 } else {
404 signalsToIssue = 1;
405 m_waitersToUnblock = 1;
406 --m_waitersBlocked;
407 }
408 } else { // No-op.
409 res = ReleaseMutex(m_unblockLock);
410 ASSERT(res);
411 return;
412 }
413
414 res = ReleaseMutex(m_unblockLock);
415 ASSERT(res);
416
417 if (signalsToIssue) {
418 res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0);
419 ASSERT(res);
420 }
421}
422
423static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
424
425ThreadCondition::ThreadCondition()
426{
427 m_condition.m_waitersGone = 0;
428 m_condition.m_waitersBlocked = 0;
429 m_condition.m_waitersToUnblock = 0;
430 m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0);
431 m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
432 m_condition.m_unblockLock = CreateMutex(0, 0, 0);
433
434 if (!m_condition.m_blockLock || !m_condition.m_blockQueue || !m_condition.m_unblockLock) {
435 if (m_condition.m_blockLock)
436 CloseHandle(m_condition.m_blockLock);
437 if (m_condition.m_blockQueue)
438 CloseHandle(m_condition.m_blockQueue);
439 if (m_condition.m_unblockLock)
440 CloseHandle(m_condition.m_unblockLock);
441 }
442}
443
444ThreadCondition::~ThreadCondition()
445{
446 CloseHandle(m_condition.m_blockLock);
447 CloseHandle(m_condition.m_blockQueue);
448 CloseHandle(m_condition.m_unblockLock);
449}
450
451void ThreadCondition::wait(Mutex& mutex)
452{
453 m_condition.timedWait(mutex.impl(), INFINITE);
454}
455
456bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime)
457{
458 double currentTime = WTF::currentTime();
459
460 // Time is in the past - return immediately.
461 if (absoluteTime < currentTime)
462 return false;
463
464 // Time is too far in the future (and would overflow unsigned long) - wait forever.
465 if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) {
466 wait(mutex);
467 return true;
468 }
469
470 double intervalMilliseconds = (absoluteTime - currentTime) * 1000.0;
471 return m_condition.timedWait(mutex.impl(), static_cast<unsigned long>(intervalMilliseconds));
472}
473
474void ThreadCondition::signal()
475{
476 m_condition.signal(false); // Unblock only 1 thread.
477}
478
479void ThreadCondition::broadcast()
480{
481 m_condition.signal(true); // Unblock all threads.
482}
483
484} // namespace WTF
Note: See TracBrowser for help on using the repository browser.