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

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

Reviewed by Adam Roben.

https://bugs.webkit.org/show_bug.cgi?id=18488
FastMalloc doesn't release thread-specific data on Windows

  • wtf/ThreadingWin.cpp: (WTF::threadMapMutex): (WTF::initializeThreading): Call threadMapMutex once to initialize the static safely. (WTF::ThreadFunctionInvocation::ThreadFunctionInvocation): Added a structure to wrap thread entry point and arguments. (WTF::wtfThreadEntryPoint): Make sure to end all WTF threads with pthread_exit(), to give pthreads-win32 a chance to call destructors of thread-specific data. (WTF::createThread): Use _beginthreadex instead of CreateThread, because MSDN says so. Also removed a call to CreateEvent, for which I could see no reason at all.
  • Property svn:eol-style set to native
File size: 14.1 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)
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 <pthreads.h>
76#endif
77
78namespace WTF {
79
80Mutex* atomicallyInitializedStaticMutex;
81
82static ThreadIdentifier mainThreadIdentifier;
83
84static Mutex& threadMapMutex()
85{
86 static Mutex mutex;
87 return mutex;
88}
89
90void initializeThreading()
91{
92 if (!atomicallyInitializedStaticMutex) {
93 atomicallyInitializedStaticMutex = new Mutex;
94 threadMapMutex();
95 wtf_random_init();
96 initializeMainThread();
97 mainThreadIdentifier = currentThread();
98 }
99}
100
101static HashMap<DWORD, HANDLE>& threadMap()
102{
103 static HashMap<DWORD, HANDLE> map;
104 return map;
105}
106
107static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
108{
109 MutexLocker locker(threadMapMutex());
110 threadMap().add(threadID, threadHandle);
111}
112
113static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
114{
115 MutexLocker locker(threadMapMutex());
116 return threadMap().get(id);
117}
118
119static void clearThreadHandleForIdentifier(ThreadIdentifier id)
120{
121 MutexLocker locker(threadMapMutex());
122 ASSERT(threadMap().contains(id));
123 threadMap().remove(id);
124}
125
126struct ThreadFunctionInvocation {
127 ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {}
128
129 ThreadFunction function;
130 void* data;
131};
132
133static unsigned __stdcall wtfThreadEntryPoint(void* param)
134{
135 ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param);
136 delete static_cast<ThreadFunctionInvocation*>(param);
137
138 void* result = invocation.function(invocation.data);
139
140#if PLATFORM(WIN)
141 // pthreads-win32 knows how to work with threads created with Win32 or CRT functions, so it's OK to mix APIs.
142 pthread_exit(result);
143#endif
144
145 return reinterpret_cast<unsigned>(result);
146}
147
148ThreadIdentifier createThread(ThreadFunction entryPoint, void* data)
149{
150 unsigned threadIdentifier = 0;
151 ThreadIdentifier threadID = 0;
152 ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data);
153 HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier));
154 if (!threadHandle) {
155 LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
156 return 0;
157 }
158
159 threadID = static_cast<ThreadIdentifier>(threadIdentifier);
160 storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
161
162 return threadID;
163}
164
165int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
166{
167 ASSERT(threadID);
168
169 HANDLE threadHandle = threadHandleForIdentifier(threadID);
170 if (!threadHandle)
171 LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
172
173 DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE);
174 if (joinResult == WAIT_FAILED)
175 LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
176
177 ::CloseHandle(threadHandle);
178 clearThreadHandleForIdentifier(threadID);
179
180 return joinResult;
181}
182
183void detachThread(ThreadIdentifier threadID)
184{
185 ASSERT(threadID);
186
187 HANDLE threadHandle = threadHandleForIdentifier(threadID);
188 if (threadHandle)
189 ::CloseHandle(threadHandle);
190 clearThreadHandleForIdentifier(threadID);
191}
192
193ThreadIdentifier currentThread()
194{
195 return static_cast<ThreadIdentifier>(::GetCurrentThreadId());
196}
197
198bool isMainThread()
199{
200 return currentThread() == mainThreadIdentifier;
201}
202
203Mutex::Mutex()
204{
205 m_mutex.m_recursionCount = 0;
206 ::InitializeCriticalSection(&m_mutex.m_internalMutex);
207}
208
209Mutex::~Mutex()
210{
211 ::DeleteCriticalSection(&m_mutex.m_internalMutex);
212}
213
214void Mutex::lock()
215{
216 ::EnterCriticalSection(&m_mutex.m_internalMutex);
217 ++m_mutex.m_recursionCount;
218}
219
220bool Mutex::tryLock()
221{
222 // This method is modeled after the behavior of pthread_mutex_trylock,
223 // which will return an error if the lock is already owned by the
224 // current thread. Since the primitive Win32 'TryEnterCriticalSection'
225 // treats this as a successful case, it changes the behavior of several
226 // tests in WebKit that check to see if the current thread already
227 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
228 DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex);
229
230 if (result != 0) { // We got the lock
231 // If this thread already had the lock, we must unlock and
232 // return false so that we mimic the behavior of POSIX's
233 // pthread_mutex_trylock:
234 if (m_mutex.m_recursionCount > 0) {
235 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
236 return false;
237 }
238
239 ++m_mutex.m_recursionCount;
240 return true;
241 }
242
243 return false;
244}
245
246void Mutex::unlock()
247{
248 --m_mutex.m_recursionCount;
249 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
250}
251
252static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
253
254ThreadCondition::ThreadCondition()
255{
256 m_condition.m_timedOut = 0;
257 m_condition.m_blocked = 0;
258 m_condition.m_waitingForRemoval = 0;
259 m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0);
260 m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
261 m_condition.m_mutex = ::CreateMutex(0, 0, 0);
262
263 if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) {
264 if (m_condition.m_gate)
265 ::CloseHandle(m_condition.m_gate);
266 if (m_condition.m_queue)
267 ::CloseHandle(m_condition.m_queue);
268 if (m_condition.m_mutex)
269 ::CloseHandle(m_condition.m_mutex);
270 }
271}
272
273ThreadCondition::~ThreadCondition()
274{
275 ::CloseHandle(m_condition.m_gate);
276 ::CloseHandle(m_condition.m_queue);
277 ::CloseHandle(m_condition.m_mutex);
278}
279
280void ThreadCondition::wait(Mutex& mutex)
281{
282 PlatformMutex& cs = mutex.impl();
283
284 // Enter the wait state.
285 DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
286 ASSERT(res == WAIT_OBJECT_0);
287 ++m_condition.m_blocked;
288 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
289 ASSERT(res);
290
291 ::LeaveCriticalSection(&cs.m_internalMutex);
292
293 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
294 ASSERT(res == WAIT_OBJECT_0);
295
296 res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
297 ASSERT(res == WAIT_OBJECT_0);
298 size_t wasWaiting = m_condition.m_waitingForRemoval;
299 size_t wasTimedOut = m_condition.m_timedOut;
300 if (wasWaiting != 0) {
301 if (--m_condition.m_waitingForRemoval == 0) {
302 if (m_condition.m_blocked != 0) {
303 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate
304 ASSERT(res);
305 wasWaiting = 0;
306 }
307 else if (m_condition.m_timedOut != 0)
308 m_condition.m_timedOut = 0;
309 }
310 } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) {
311 // timeout occured, normalize the m_condition.m_timedOut count
312 // this may occur if many calls to wait with a timeout are made and
313 // no call to notify_* is made
314 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
315 ASSERT(res == WAIT_OBJECT_0);
316 m_condition.m_blocked -= m_condition.m_timedOut;
317 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
318 ASSERT(res);
319 m_condition.m_timedOut = 0;
320 }
321 res = ::ReleaseMutex(m_condition.m_mutex);
322 ASSERT(res);
323
324 if (wasWaiting == 1) {
325 for (/**/ ; wasTimedOut; --wasTimedOut) {
326 // better now than spurious later
327 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
328 ASSERT(res == WAIT_OBJECT_0);
329 }
330 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
331 ASSERT(res);
332 }
333
334 ::EnterCriticalSection (&cs.m_internalMutex);
335}
336
337bool ThreadCondition::timedWait(Mutex& mutex, double interval)
338{
339 // Empty for now
340 ASSERT(false);
341 return false;
342}
343
344void ThreadCondition::signal()
345{
346 unsigned signals = 0;
347
348 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
349 ASSERT(res == WAIT_OBJECT_0);
350
351 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
352 if (m_condition.m_blocked == 0) {
353 res = ::ReleaseMutex(m_condition.m_mutex);
354 ASSERT(res);
355 return;
356 }
357
358 ++m_condition.m_waitingForRemoval;
359 --m_condition.m_blocked;
360
361 signals = 1;
362 } else {
363 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
364 ASSERT(res == WAIT_OBJECT_0);
365 if (m_condition.m_blocked > m_condition.m_timedOut) {
366 if (m_condition.m_timedOut != 0) {
367 m_condition.m_blocked -= m_condition.m_timedOut;
368 m_condition.m_timedOut = 0;
369 }
370 signals = m_condition.m_waitingForRemoval = 1;
371 --m_condition.m_blocked;
372 } else {
373 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
374 ASSERT(res);
375 }
376 }
377
378 res =::ReleaseMutex(m_condition.m_mutex);
379 ASSERT(res);
380
381 if (signals) {
382 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
383 ASSERT(res);
384 }
385}
386
387void ThreadCondition::broadcast()
388{
389 unsigned signals = 0;
390
391 WORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
392 ASSERT(res == WAIT_OBJECT_0);
393
394 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
395 if (m_condition.m_blocked == 0) {
396 res = ::ReleaseMutex(m_condition.m_mutex);
397 ASSERT(res);
398 return;
399 }
400
401 m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked);
402 m_condition.m_blocked = 0;
403 } else {
404 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
405 ASSERT(res == WAIT_OBJECT_0);
406 if (m_condition.m_blocked > m_condition.m_timedOut) {
407 if (m_condition.m_timedOut != 0) {
408 m_condition.m_blocked -= m_condition.m_timedOut;
409 m_condition.m_timedOut = 0;
410 }
411 signals = m_condition.m_waitingForRemoval = m_condition.m_blocked;
412 m_condition.m_blocked = 0;
413 } else {
414 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
415 ASSERT(res);
416 }
417 }
418
419 res = ::ReleaseMutex(m_condition.m_mutex);
420 ASSERT(res);
421
422 if (signals) {
423 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
424 ASSERT(res);
425 }
426}
427
428} // namespace WTF
Note: See TracBrowser for help on using the repository browser.