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

Last change on this file since 38146 was 38101, checked in by Darin Adler, 17 years ago

2008-11-03 Darin Adler <Darin Adler>

Reviewed by Tim Hatcher.

  • JavaScriptCore.exp: Changed to export functions rather than a global for the atomically initialized static mutex.
  • JavaScriptCore.xcodeproj/project.pbxproj: Added a script phase that runs the check-for-exit-time-destructors script.
  • wtf/MainThread.cpp: (WTF::mainThreadFunctionQueueMutex): Changed to leak an object rather than using an exit time destructor. (WTF::functionQueue): Ditto.
  • wtf/unicode/icu/CollatorICU.cpp: (WTF::cachedCollatorMutex): Ditto.
  • wtf/Threading.h: Changed other platforms to share the Windows approach where the mutex is internal and the functions are exported.
  • wtf/ThreadingGtk.cpp: (WTF::lockAtomicallyInitializedStaticMutex): Ditto. (WTF::unlockAtomicallyInitializedStaticMutex): Ditto.
  • wtf/ThreadingNone.cpp: (WTF::lockAtomicallyInitializedStaticMutex): Ditto. (WTF::unlockAtomicallyInitializedStaticMutex): Ditto.
  • wtf/ThreadingPthreads.cpp: (WTF::threadMapMutex): Changed to leak an object rather than using an exit time destructor. (WTF::lockAtomicallyInitializedStaticMutex): Mutex change. (WTF::unlockAtomicallyInitializedStaticMutex): Ditto. (WTF::threadMap): Changed to leak an object rather than using an exit time destructor.
  • wtf/ThreadingQt.cpp: (WTF::lockAtomicallyInitializedStaticMutex): Mutex change. (WTF::unlockAtomicallyInitializedStaticMutex): Ditto.
  • wtf/ThreadingWin.cpp: (WTF::lockAtomicallyInitializedStaticMutex): Added an assertion.
  • Property svn:eol-style set to native
File size: 15.8 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#include <process.h>
68#include <windows.h>
69#include <wtf/HashMap.h>
70#include <wtf/MathExtras.h>
71
72#if PLATFORM(WIN) && USE(PTHREADS)
73// Currently, Apple's Windows port uses a mixture of native and pthreads functions in FastMalloc.
74// To ensure that thread-specific data is properly destroyed, we need to end each thread with pthread_exit().
75#include <pthread.h>
76#endif
77
78namespace WTF {
79
80// MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadName all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>.
81static const DWORD MS_VC_EXCEPTION = 0x406D1388;
82
83#pragma pack(push, 8)
84typedef struct tagTHREADNAME_INFO {
85 DWORD dwType; // must be 0x1000
86 LPCSTR szName; // pointer to name (in user addr space)
87 DWORD dwThreadID; // thread ID (-1=caller thread)
88 DWORD dwFlags; // reserved for future use, must be zero
89} THREADNAME_INFO;
90#pragma pack(pop)
91
92static void setThreadName(DWORD dwThreadID, LPCSTR szThreadName)
93{
94 // Visual Studio has a 31-character limit on thread names. Longer names will
95 // be truncated silently, but we'd like callers to know about the limit.
96 ASSERT_ARG(szThreadName, strlen(szThreadName) <= 31);
97
98 THREADNAME_INFO info;
99 info.dwType = 0x1000;
100 info.szName = szThreadName;
101 info.dwThreadID = dwThreadID;
102 info.dwFlags = 0;
103
104 __try {
105 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
106 } __except (EXCEPTION_CONTINUE_EXECUTION) {
107 }
108}
109
110static Mutex* atomicallyInitializedStaticMutex;
111
112void lockAtomicallyInitializedStaticMutex()
113{
114 ASSERT(atomicallyInitializedStaticMutex);
115 atomicallyInitializedStaticMutex->lock();
116}
117
118void unlockAtomicallyInitializedStaticMutex()
119{
120 atomicallyInitializedStaticMutex->unlock();
121}
122
123static ThreadIdentifier mainThreadIdentifier;
124
125static Mutex& threadMapMutex()
126{
127 static Mutex mutex;
128 return mutex;
129}
130
131void initializeThreading()
132{
133 if (!atomicallyInitializedStaticMutex) {
134 atomicallyInitializedStaticMutex = new Mutex;
135 threadMapMutex();
136 wtf_random_init();
137 initializeMainThread();
138 mainThreadIdentifier = currentThread();
139 setThreadName(mainThreadIdentifier, "Main Thread");
140 }
141}
142
143static HashMap<DWORD, HANDLE>& threadMap()
144{
145 static HashMap<DWORD, HANDLE> map;
146 return map;
147}
148
149static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
150{
151 MutexLocker locker(threadMapMutex());
152 threadMap().add(threadID, threadHandle);
153}
154
155static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
156{
157 MutexLocker locker(threadMapMutex());
158 return threadMap().get(id);
159}
160
161static void clearThreadHandleForIdentifier(ThreadIdentifier id)
162{
163 MutexLocker locker(threadMapMutex());
164 ASSERT(threadMap().contains(id));
165 threadMap().remove(id);
166}
167
168struct ThreadFunctionInvocation {
169 ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {}
170
171 ThreadFunction function;
172 void* data;
173};
174
175static unsigned __stdcall wtfThreadEntryPoint(void* param)
176{
177 ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param);
178 delete static_cast<ThreadFunctionInvocation*>(param);
179
180 void* result = invocation.function(invocation.data);
181
182#if PLATFORM(WIN) && USE(PTHREADS)
183 // pthreads-win32 knows how to work with threads created with Win32 or CRT functions, so it's OK to mix APIs.
184 pthread_exit(result);
185#endif
186
187 return reinterpret_cast<unsigned>(result);
188}
189
190ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char* threadName)
191{
192 unsigned threadIdentifier = 0;
193 ThreadIdentifier threadID = 0;
194 ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data);
195 HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier));
196 if (!threadHandle) {
197 LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
198 return 0;
199 }
200
201 if (threadName)
202 setThreadName(threadIdentifier, threadName);
203
204 threadID = static_cast<ThreadIdentifier>(threadIdentifier);
205 storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
206
207 return threadID;
208}
209
210// This function is deprecated but needs to be kept around for backward
211// compatibility. Use the 3-argument version of createThread above.
212ThreadIdentifier createThread(ThreadFunction entryPoint, void* data)
213{
214 return createThread(entryPoint, data, 0);
215}
216
217int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
218{
219 ASSERT(threadID);
220
221 HANDLE threadHandle = threadHandleForIdentifier(threadID);
222 if (!threadHandle)
223 LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
224
225 DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE);
226 if (joinResult == WAIT_FAILED)
227 LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
228
229 ::CloseHandle(threadHandle);
230 clearThreadHandleForIdentifier(threadID);
231
232 return joinResult;
233}
234
235void detachThread(ThreadIdentifier threadID)
236{
237 ASSERT(threadID);
238
239 HANDLE threadHandle = threadHandleForIdentifier(threadID);
240 if (threadHandle)
241 ::CloseHandle(threadHandle);
242 clearThreadHandleForIdentifier(threadID);
243}
244
245ThreadIdentifier currentThread()
246{
247 return static_cast<ThreadIdentifier>(::GetCurrentThreadId());
248}
249
250bool isMainThread()
251{
252 return currentThread() == mainThreadIdentifier;
253}
254
255Mutex::Mutex()
256{
257 m_mutex.m_recursionCount = 0;
258 ::InitializeCriticalSection(&m_mutex.m_internalMutex);
259}
260
261Mutex::~Mutex()
262{
263 ::DeleteCriticalSection(&m_mutex.m_internalMutex);
264}
265
266void Mutex::lock()
267{
268 ::EnterCriticalSection(&m_mutex.m_internalMutex);
269 ++m_mutex.m_recursionCount;
270}
271
272bool Mutex::tryLock()
273{
274 // This method is modeled after the behavior of pthread_mutex_trylock,
275 // which will return an error if the lock is already owned by the
276 // current thread. Since the primitive Win32 'TryEnterCriticalSection'
277 // treats this as a successful case, it changes the behavior of several
278 // tests in WebKit that check to see if the current thread already
279 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
280 DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex);
281
282 if (result != 0) { // We got the lock
283 // If this thread already had the lock, we must unlock and
284 // return false so that we mimic the behavior of POSIX's
285 // pthread_mutex_trylock:
286 if (m_mutex.m_recursionCount > 0) {
287 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
288 return false;
289 }
290
291 ++m_mutex.m_recursionCount;
292 return true;
293 }
294
295 return false;
296}
297
298void Mutex::unlock()
299{
300 --m_mutex.m_recursionCount;
301 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
302}
303
304static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
305
306ThreadCondition::ThreadCondition()
307{
308 m_condition.m_timedOut = 0;
309 m_condition.m_blocked = 0;
310 m_condition.m_waitingForRemoval = 0;
311 m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0);
312 m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
313 m_condition.m_mutex = ::CreateMutex(0, 0, 0);
314
315 if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) {
316 if (m_condition.m_gate)
317 ::CloseHandle(m_condition.m_gate);
318 if (m_condition.m_queue)
319 ::CloseHandle(m_condition.m_queue);
320 if (m_condition.m_mutex)
321 ::CloseHandle(m_condition.m_mutex);
322 }
323}
324
325ThreadCondition::~ThreadCondition()
326{
327 ::CloseHandle(m_condition.m_gate);
328 ::CloseHandle(m_condition.m_queue);
329 ::CloseHandle(m_condition.m_mutex);
330}
331
332void ThreadCondition::wait(Mutex& mutex)
333{
334 PlatformMutex& cs = mutex.impl();
335
336 // Enter the wait state.
337 DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
338 ASSERT(res == WAIT_OBJECT_0);
339 ++m_condition.m_blocked;
340 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
341 ASSERT(res);
342
343 ::LeaveCriticalSection(&cs.m_internalMutex);
344
345 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
346 ASSERT(res == WAIT_OBJECT_0);
347
348 res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
349 ASSERT(res == WAIT_OBJECT_0);
350 size_t wasWaiting = m_condition.m_waitingForRemoval;
351 size_t wasTimedOut = m_condition.m_timedOut;
352 if (wasWaiting != 0) {
353 if (--m_condition.m_waitingForRemoval == 0) {
354 if (m_condition.m_blocked != 0) {
355 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate
356 ASSERT(res);
357 wasWaiting = 0;
358 }
359 else if (m_condition.m_timedOut != 0)
360 m_condition.m_timedOut = 0;
361 }
362 } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) {
363 // timeout occured, normalize the m_condition.m_timedOut count
364 // this may occur if many calls to wait with a timeout are made and
365 // no call to notify_* is made
366 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
367 ASSERT(res == WAIT_OBJECT_0);
368 m_condition.m_blocked -= m_condition.m_timedOut;
369 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
370 ASSERT(res);
371 m_condition.m_timedOut = 0;
372 }
373 res = ::ReleaseMutex(m_condition.m_mutex);
374 ASSERT(res);
375
376 if (wasWaiting == 1) {
377 for (/**/ ; wasTimedOut; --wasTimedOut) {
378 // better now than spurious later
379 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
380 ASSERT(res == WAIT_OBJECT_0);
381 }
382 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
383 ASSERT(res);
384 }
385
386 ::EnterCriticalSection (&cs.m_internalMutex);
387}
388
389bool ThreadCondition::timedWait(Mutex& mutex, double interval)
390{
391 // Empty for now
392 ASSERT(false);
393 return false;
394}
395
396void ThreadCondition::signal()
397{
398 unsigned signals = 0;
399
400 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
401 ASSERT(res == WAIT_OBJECT_0);
402
403 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
404 if (m_condition.m_blocked == 0) {
405 res = ::ReleaseMutex(m_condition.m_mutex);
406 ASSERT(res);
407 return;
408 }
409
410 ++m_condition.m_waitingForRemoval;
411 --m_condition.m_blocked;
412
413 signals = 1;
414 } else {
415 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
416 ASSERT(res == WAIT_OBJECT_0);
417 if (m_condition.m_blocked > m_condition.m_timedOut) {
418 if (m_condition.m_timedOut != 0) {
419 m_condition.m_blocked -= m_condition.m_timedOut;
420 m_condition.m_timedOut = 0;
421 }
422 signals = m_condition.m_waitingForRemoval = 1;
423 --m_condition.m_blocked;
424 } else {
425 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
426 ASSERT(res);
427 }
428 }
429
430 res =::ReleaseMutex(m_condition.m_mutex);
431 ASSERT(res);
432
433 if (signals) {
434 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
435 ASSERT(res);
436 }
437}
438
439void ThreadCondition::broadcast()
440{
441 unsigned signals = 0;
442
443 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
444 ASSERT(res == WAIT_OBJECT_0);
445
446 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
447 if (m_condition.m_blocked == 0) {
448 res = ::ReleaseMutex(m_condition.m_mutex);
449 ASSERT(res);
450 return;
451 }
452
453 m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked);
454 m_condition.m_blocked = 0;
455 } else {
456 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
457 ASSERT(res == WAIT_OBJECT_0);
458 if (m_condition.m_blocked > m_condition.m_timedOut) {
459 if (m_condition.m_timedOut != 0) {
460 m_condition.m_blocked -= m_condition.m_timedOut;
461 m_condition.m_timedOut = 0;
462 }
463 signals = m_condition.m_waitingForRemoval = m_condition.m_blocked;
464 m_condition.m_blocked = 0;
465 } else {
466 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
467 ASSERT(res);
468 }
469 }
470
471 res = ::ReleaseMutex(m_condition.m_mutex);
472 ASSERT(res);
473
474 if (signals) {
475 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
476 ASSERT(res);
477 }
478}
479
480} // namespace WTF
Note: See TracBrowser for help on using the repository browser.