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

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

<rdar://problem/6467376> Race condition in WTF::currentThread can lead to a thread using two different identifiers during its lifetime

If a newly-created thread calls WTF::currentThread() before WTF::createThread calls establishIdentifierForPthreadHandle
then more than one identifier will be used for the same thread. We can avoid this by adding some extra synchronization
during thread creation that delays the execution of the thread function until the thread identifier has been set up, and
an assertion to catch this problem should it reappear in the future.

Reviewed by Alexey Proskuryakov.

  • wtf/Threading.cpp: Added.

(WTF::NewThreadContext::NewThreadContext):
(WTF::threadEntryPoint):
(WTF::createThread): Add cross-platform createThread function that delays the execution of the thread function until
after the thread identifier has been set up.

  • wtf/Threading.h:
  • wtf/ThreadingGtk.cpp:

(WTF::establishIdentifierForThread):
(WTF::createThreadInternal):

  • wtf/ThreadingNone.cpp:

(WTF::createThreadInternal):

  • wtf/ThreadingPthreads.cpp:

(WTF::establishIdentifierForPthreadHandle):
(WTF::createThreadInternal):

  • wtf/ThreadingQt.cpp:

(WTF::identifierByQthreadHandle):
(WTF::establishIdentifierForThread):
(WTF::createThreadInternal):

  • wtf/ThreadingWin.cpp:

(WTF::storeThreadHandleByIdentifier):
(WTF::createThreadInternal):

Add Threading.cpp to the build.

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