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

Last change on this file since 39708 was 39708, checked in by [email protected], 16 years ago

2009-01-08 Jian Li <[email protected]>

Reviewed by Alexey Proskuryakov.

Add Win32 implementation of ThreadSpecific.
https://bugs.webkit.org/show_bug.cgi?id=22614

  • JavaScriptCore.vcproj/WTF/WTF.vcproj:
  • wtf/ThreadSpecific.h: (WTF::ThreadSpecific::ThreadSpecific): (WTF::ThreadSpecific::~ThreadSpecific): (WTF::ThreadSpecific::get): (WTF::ThreadSpecific::set): (WTF::ThreadSpecific::destroy):
  • wtf/ThreadSpecificWin.cpp: Added. (WTF::ThreadSpecificThreadExit):
  • wtf/ThreadingWin.cpp: (WTF::wtfThreadEntryPoint):
  • Property svn:eol-style set to native
File size: 15.4 KB
Line 
1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * =============================================================================
29 * Note: The implementation of condition variables under the Windows
30 * plaform was based on that of the excellent BOOST C++ library. It
31 * has been rewritten to fit in with the WebKit architecture and to
32 * use its coding conventions.
33 * =============================================================================
34 *
35 * The Boost license is virtually identical to the Apple variation at the
36 * top of this file, but is included here for completeness:
37 *
38 * Boost Software License - Version 1.0 - August 17th, 2003
39 *
40 * Permission is hereby granted, free of charge, to any person or organization
41 * obtaining a copy of the software and accompanying documentation covered by
42 * this license (the "Software") to use, reproduce, display, distribute,
43 * execute, and transmit the Software, and to prepare derivative works of the
44 * Software, and to permit third-parties to whom the Software is furnished to
45 * do so, all subject to the following:
46 *
47 * The copyright notices in the Software and this entire statement, including
48 * the above license grant, this restriction and the following disclaimer,
49 * must be included in all copies of the Software, in whole or in part, and
50 * all derivative works of the Software, unless such copies or derivative
51 * works are solely in the form of machine-executable object code generated by
52 * a source language processor.
53 *
54 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
57 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
58 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
59 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
60 * DEALINGS IN THE SOFTWARE.
61 */
62
63#include "config.h"
64#include "Threading.h"
65
66#include "MainThread.h"
67#if !USE(PTHREADS) && PLATFORM(WIN_OS)
68#include "ThreadSpecific.h"
69#endif
70#include <process.h>
71#include <windows.h>
72#include <wtf/HashMap.h>
73#include <wtf/MathExtras.h>
74#include <wtf/RandomNumberSeed.h>
75
76namespace WTF {
77
78// MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadName all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>.
79static const DWORD MS_VC_EXCEPTION = 0x406D1388;
80
81#pragma pack(push, 8)
82typedef struct tagTHREADNAME_INFO {
83 DWORD dwType; // must be 0x1000
84 LPCSTR szName; // pointer to name (in user addr space)
85 DWORD dwThreadID; // thread ID (-1=caller thread)
86 DWORD dwFlags; // reserved for future use, must be zero
87} THREADNAME_INFO;
88#pragma pack(pop)
89
90static void setThreadName(DWORD dwThreadID, LPCSTR szThreadName)
91{
92 // Visual Studio has a 31-character limit on thread names. Longer names will
93 // be truncated silently, but we'd like callers to know about the limit.
94 ASSERT_ARG(szThreadName, strlen(szThreadName) <= 31);
95
96 THREADNAME_INFO info;
97 info.dwType = 0x1000;
98 info.szName = szThreadName;
99 info.dwThreadID = dwThreadID;
100 info.dwFlags = 0;
101
102 __try {
103 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
104 } __except (EXCEPTION_CONTINUE_EXECUTION) {
105 }
106}
107
108static Mutex* atomicallyInitializedStaticMutex;
109
110void lockAtomicallyInitializedStaticMutex()
111{
112 ASSERT(atomicallyInitializedStaticMutex);
113 atomicallyInitializedStaticMutex->lock();
114}
115
116void unlockAtomicallyInitializedStaticMutex()
117{
118 atomicallyInitializedStaticMutex->unlock();
119}
120
121static ThreadIdentifier mainThreadIdentifier;
122
123static Mutex& threadMapMutex()
124{
125 static Mutex mutex;
126 return mutex;
127}
128
129void initializeThreading()
130{
131 if (!atomicallyInitializedStaticMutex) {
132 atomicallyInitializedStaticMutex = new Mutex;
133 threadMapMutex();
134 initializeRandomNumberGenerator();
135 initializeMainThread();
136 mainThreadIdentifier = currentThread();
137 setThreadName(mainThreadIdentifier, "Main Thread");
138 }
139}
140
141static HashMap<DWORD, HANDLE>& threadMap()
142{
143 static HashMap<DWORD, HANDLE> map;
144 return map;
145}
146
147static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
148{
149 MutexLocker locker(threadMapMutex());
150 ASSERT(!threadMap().contains(threadID));
151 threadMap().add(threadID, threadHandle);
152}
153
154static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
155{
156 MutexLocker locker(threadMapMutex());
157 return threadMap().get(id);
158}
159
160static void clearThreadHandleForIdentifier(ThreadIdentifier id)
161{
162 MutexLocker locker(threadMapMutex());
163 ASSERT(threadMap().contains(id));
164 threadMap().remove(id);
165}
166
167struct ThreadFunctionInvocation {
168 ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {}
169
170 ThreadFunction function;
171 void* data;
172};
173
174static unsigned __stdcall wtfThreadEntryPoint(void* param)
175{
176 ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param);
177 delete static_cast<ThreadFunctionInvocation*>(param);
178
179 void* result = invocation.function(invocation.data);
180
181#if !USE(PTHREADS) && PLATFORM(WIN_OS)
182 // Do the TLS cleanup.
183 ThreadSpecificThreadExit();
184#endif
185
186 return reinterpret_cast<unsigned>(result);
187}
188
189ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char* threadName)
190{
191 unsigned threadIdentifier = 0;
192 ThreadIdentifier threadID = 0;
193 ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data);
194 HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier));
195 if (!threadHandle) {
196 LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
197 return 0;
198 }
199
200 if (threadName)
201 setThreadName(threadIdentifier, threadName);
202
203 threadID = static_cast<ThreadIdentifier>(threadIdentifier);
204 storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
205
206 return threadID;
207}
208
209int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
210{
211 ASSERT(threadID);
212
213 HANDLE threadHandle = threadHandleForIdentifier(threadID);
214 if (!threadHandle)
215 LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
216
217 DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE);
218 if (joinResult == WAIT_FAILED)
219 LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
220
221 ::CloseHandle(threadHandle);
222 clearThreadHandleForIdentifier(threadID);
223
224 return joinResult;
225}
226
227void detachThread(ThreadIdentifier threadID)
228{
229 ASSERT(threadID);
230
231 HANDLE threadHandle = threadHandleForIdentifier(threadID);
232 if (threadHandle)
233 ::CloseHandle(threadHandle);
234 clearThreadHandleForIdentifier(threadID);
235}
236
237ThreadIdentifier currentThread()
238{
239 return static_cast<ThreadIdentifier>(::GetCurrentThreadId());
240}
241
242bool isMainThread()
243{
244 return currentThread() == mainThreadIdentifier;
245}
246
247Mutex::Mutex()
248{
249 m_mutex.m_recursionCount = 0;
250 ::InitializeCriticalSection(&m_mutex.m_internalMutex);
251}
252
253Mutex::~Mutex()
254{
255 ::DeleteCriticalSection(&m_mutex.m_internalMutex);
256}
257
258void Mutex::lock()
259{
260 ::EnterCriticalSection(&m_mutex.m_internalMutex);
261 ++m_mutex.m_recursionCount;
262}
263
264bool Mutex::tryLock()
265{
266 // This method is modeled after the behavior of pthread_mutex_trylock,
267 // which will return an error if the lock is already owned by the
268 // current thread. Since the primitive Win32 'TryEnterCriticalSection'
269 // treats this as a successful case, it changes the behavior of several
270 // tests in WebKit that check to see if the current thread already
271 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
272 DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex);
273
274 if (result != 0) { // We got the lock
275 // If this thread already had the lock, we must unlock and
276 // return false so that we mimic the behavior of POSIX's
277 // pthread_mutex_trylock:
278 if (m_mutex.m_recursionCount > 0) {
279 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
280 return false;
281 }
282
283 ++m_mutex.m_recursionCount;
284 return true;
285 }
286
287 return false;
288}
289
290void Mutex::unlock()
291{
292 --m_mutex.m_recursionCount;
293 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
294}
295
296static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
297
298ThreadCondition::ThreadCondition()
299{
300 m_condition.m_timedOut = 0;
301 m_condition.m_blocked = 0;
302 m_condition.m_waitingForRemoval = 0;
303 m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0);
304 m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
305 m_condition.m_mutex = ::CreateMutex(0, 0, 0);
306
307 if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) {
308 if (m_condition.m_gate)
309 ::CloseHandle(m_condition.m_gate);
310 if (m_condition.m_queue)
311 ::CloseHandle(m_condition.m_queue);
312 if (m_condition.m_mutex)
313 ::CloseHandle(m_condition.m_mutex);
314 }
315}
316
317ThreadCondition::~ThreadCondition()
318{
319 ::CloseHandle(m_condition.m_gate);
320 ::CloseHandle(m_condition.m_queue);
321 ::CloseHandle(m_condition.m_mutex);
322}
323
324void ThreadCondition::wait(Mutex& mutex)
325{
326 PlatformMutex& cs = mutex.impl();
327
328 // Enter the wait state.
329 DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
330 ASSERT(res == WAIT_OBJECT_0);
331 ++m_condition.m_blocked;
332 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
333 ASSERT(res);
334
335 ::LeaveCriticalSection(&cs.m_internalMutex);
336
337 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
338 ASSERT(res == WAIT_OBJECT_0);
339
340 res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
341 ASSERT(res == WAIT_OBJECT_0);
342 size_t wasWaiting = m_condition.m_waitingForRemoval;
343 size_t wasTimedOut = m_condition.m_timedOut;
344 if (wasWaiting != 0) {
345 if (--m_condition.m_waitingForRemoval == 0) {
346 if (m_condition.m_blocked != 0) {
347 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate
348 ASSERT(res);
349 wasWaiting = 0;
350 }
351 else if (m_condition.m_timedOut != 0)
352 m_condition.m_timedOut = 0;
353 }
354 } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) {
355 // timeout occured, normalize the m_condition.m_timedOut count
356 // this may occur if many calls to wait with a timeout are made and
357 // no call to notify_* is made
358 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
359 ASSERT(res == WAIT_OBJECT_0);
360 m_condition.m_blocked -= m_condition.m_timedOut;
361 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
362 ASSERT(res);
363 m_condition.m_timedOut = 0;
364 }
365 res = ::ReleaseMutex(m_condition.m_mutex);
366 ASSERT(res);
367
368 if (wasWaiting == 1) {
369 for (/**/ ; wasTimedOut; --wasTimedOut) {
370 // better now than spurious later
371 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
372 ASSERT(res == WAIT_OBJECT_0);
373 }
374 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
375 ASSERT(res);
376 }
377
378 ::EnterCriticalSection (&cs.m_internalMutex);
379}
380
381bool ThreadCondition::timedWait(Mutex& mutex, double interval)
382{
383 // Empty for now
384 ASSERT(false);
385 return false;
386}
387
388void ThreadCondition::signal()
389{
390 unsigned signals = 0;
391
392 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
393 ASSERT(res == WAIT_OBJECT_0);
394
395 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
396 if (m_condition.m_blocked == 0) {
397 res = ::ReleaseMutex(m_condition.m_mutex);
398 ASSERT(res);
399 return;
400 }
401
402 ++m_condition.m_waitingForRemoval;
403 --m_condition.m_blocked;
404
405 signals = 1;
406 } else {
407 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
408 ASSERT(res == WAIT_OBJECT_0);
409 if (m_condition.m_blocked > m_condition.m_timedOut) {
410 if (m_condition.m_timedOut != 0) {
411 m_condition.m_blocked -= m_condition.m_timedOut;
412 m_condition.m_timedOut = 0;
413 }
414 signals = m_condition.m_waitingForRemoval = 1;
415 --m_condition.m_blocked;
416 } else {
417 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
418 ASSERT(res);
419 }
420 }
421
422 res =::ReleaseMutex(m_condition.m_mutex);
423 ASSERT(res);
424
425 if (signals) {
426 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
427 ASSERT(res);
428 }
429}
430
431void ThreadCondition::broadcast()
432{
433 unsigned signals = 0;
434
435 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
436 ASSERT(res == WAIT_OBJECT_0);
437
438 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
439 if (m_condition.m_blocked == 0) {
440 res = ::ReleaseMutex(m_condition.m_mutex);
441 ASSERT(res);
442 return;
443 }
444
445 m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked);
446 m_condition.m_blocked = 0;
447 } else {
448 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
449 ASSERT(res == WAIT_OBJECT_0);
450 if (m_condition.m_blocked > m_condition.m_timedOut) {
451 if (m_condition.m_timedOut != 0) {
452 m_condition.m_blocked -= m_condition.m_timedOut;
453 m_condition.m_timedOut = 0;
454 }
455 signals = m_condition.m_waitingForRemoval = m_condition.m_blocked;
456 m_condition.m_blocked = 0;
457 } else {
458 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
459 ASSERT(res);
460 }
461 }
462
463 res = ::ReleaseMutex(m_condition.m_mutex);
464 ASSERT(res);
465
466 if (signals) {
467 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
468 ASSERT(res);
469 }
470}
471
472} // namespace WTF
Note: See TracBrowser for help on using the repository browser.