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

Last change on this file since 37333 was 35420, checked in by Adam Roben, 17 years ago

Windows build fix

  • wtf/ThreadingWin.cpp: (WTF::setThreadName): Move a misplaced assertion to here... (WTF::createThread): ...from here.
  • Property svn:eol-style set to native
File size: 15.5 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 <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
110Mutex* atomicallyInitializedStaticMutex;
111
112static ThreadIdentifier mainThreadIdentifier;
113
114static Mutex& threadMapMutex()
115{
116 static Mutex mutex;
117 return mutex;
118}
119
120void initializeThreading()
121{
122 if (!atomicallyInitializedStaticMutex) {
123 atomicallyInitializedStaticMutex = new Mutex;
124 threadMapMutex();
125 wtf_random_init();
126 initializeMainThread();
127 mainThreadIdentifier = currentThread();
128 setThreadName(mainThreadIdentifier, "Main Thread");
129 }
130}
131
132static HashMap<DWORD, HANDLE>& threadMap()
133{
134 static HashMap<DWORD, HANDLE> map;
135 return map;
136}
137
138static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
139{
140 MutexLocker locker(threadMapMutex());
141 threadMap().add(threadID, threadHandle);
142}
143
144static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
145{
146 MutexLocker locker(threadMapMutex());
147 return threadMap().get(id);
148}
149
150static void clearThreadHandleForIdentifier(ThreadIdentifier id)
151{
152 MutexLocker locker(threadMapMutex());
153 ASSERT(threadMap().contains(id));
154 threadMap().remove(id);
155}
156
157struct ThreadFunctionInvocation {
158 ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {}
159
160 ThreadFunction function;
161 void* data;
162};
163
164static unsigned __stdcall wtfThreadEntryPoint(void* param)
165{
166 ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param);
167 delete static_cast<ThreadFunctionInvocation*>(param);
168
169 void* result = invocation.function(invocation.data);
170
171#if PLATFORM(WIN)
172 // pthreads-win32 knows how to work with threads created with Win32 or CRT functions, so it's OK to mix APIs.
173 pthread_exit(result);
174#endif
175
176 return reinterpret_cast<unsigned>(result);
177}
178
179ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char* threadName)
180{
181 unsigned threadIdentifier = 0;
182 ThreadIdentifier threadID = 0;
183 ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data);
184 HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier));
185 if (!threadHandle) {
186 LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
187 return 0;
188 }
189
190 if (threadName)
191 setThreadName(threadIdentifier, threadName);
192
193 threadID = static_cast<ThreadIdentifier>(threadIdentifier);
194 storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
195
196 return threadID;
197}
198
199// This function is deprecated but needs to be kept around for backward
200// compatibility. Use the 3-argument version of createThread above.
201ThreadIdentifier createThread(ThreadFunction entryPoint, void* data)
202{
203 return createThread(entryPoint, data, 0);
204}
205
206int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
207{
208 ASSERT(threadID);
209
210 HANDLE threadHandle = threadHandleForIdentifier(threadID);
211 if (!threadHandle)
212 LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
213
214 DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE);
215 if (joinResult == WAIT_FAILED)
216 LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
217
218 ::CloseHandle(threadHandle);
219 clearThreadHandleForIdentifier(threadID);
220
221 return joinResult;
222}
223
224void detachThread(ThreadIdentifier threadID)
225{
226 ASSERT(threadID);
227
228 HANDLE threadHandle = threadHandleForIdentifier(threadID);
229 if (threadHandle)
230 ::CloseHandle(threadHandle);
231 clearThreadHandleForIdentifier(threadID);
232}
233
234ThreadIdentifier currentThread()
235{
236 return static_cast<ThreadIdentifier>(::GetCurrentThreadId());
237}
238
239bool isMainThread()
240{
241 return currentThread() == mainThreadIdentifier;
242}
243
244Mutex::Mutex()
245{
246 m_mutex.m_recursionCount = 0;
247 ::InitializeCriticalSection(&m_mutex.m_internalMutex);
248}
249
250Mutex::~Mutex()
251{
252 ::DeleteCriticalSection(&m_mutex.m_internalMutex);
253}
254
255void Mutex::lock()
256{
257 ::EnterCriticalSection(&m_mutex.m_internalMutex);
258 ++m_mutex.m_recursionCount;
259}
260
261bool Mutex::tryLock()
262{
263 // This method is modeled after the behavior of pthread_mutex_trylock,
264 // which will return an error if the lock is already owned by the
265 // current thread. Since the primitive Win32 'TryEnterCriticalSection'
266 // treats this as a successful case, it changes the behavior of several
267 // tests in WebKit that check to see if the current thread already
268 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
269 DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex);
270
271 if (result != 0) { // We got the lock
272 // If this thread already had the lock, we must unlock and
273 // return false so that we mimic the behavior of POSIX's
274 // pthread_mutex_trylock:
275 if (m_mutex.m_recursionCount > 0) {
276 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
277 return false;
278 }
279
280 ++m_mutex.m_recursionCount;
281 return true;
282 }
283
284 return false;
285}
286
287void Mutex::unlock()
288{
289 --m_mutex.m_recursionCount;
290 ::LeaveCriticalSection(&m_mutex.m_internalMutex);
291}
292
293static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
294
295ThreadCondition::ThreadCondition()
296{
297 m_condition.m_timedOut = 0;
298 m_condition.m_blocked = 0;
299 m_condition.m_waitingForRemoval = 0;
300 m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0);
301 m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
302 m_condition.m_mutex = ::CreateMutex(0, 0, 0);
303
304 if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) {
305 if (m_condition.m_gate)
306 ::CloseHandle(m_condition.m_gate);
307 if (m_condition.m_queue)
308 ::CloseHandle(m_condition.m_queue);
309 if (m_condition.m_mutex)
310 ::CloseHandle(m_condition.m_mutex);
311 }
312}
313
314ThreadCondition::~ThreadCondition()
315{
316 ::CloseHandle(m_condition.m_gate);
317 ::CloseHandle(m_condition.m_queue);
318 ::CloseHandle(m_condition.m_mutex);
319}
320
321void ThreadCondition::wait(Mutex& mutex)
322{
323 PlatformMutex& cs = mutex.impl();
324
325 // Enter the wait state.
326 DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
327 ASSERT(res == WAIT_OBJECT_0);
328 ++m_condition.m_blocked;
329 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
330 ASSERT(res);
331
332 ::LeaveCriticalSection(&cs.m_internalMutex);
333
334 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
335 ASSERT(res == WAIT_OBJECT_0);
336
337 res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
338 ASSERT(res == WAIT_OBJECT_0);
339 size_t wasWaiting = m_condition.m_waitingForRemoval;
340 size_t wasTimedOut = m_condition.m_timedOut;
341 if (wasWaiting != 0) {
342 if (--m_condition.m_waitingForRemoval == 0) {
343 if (m_condition.m_blocked != 0) {
344 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate
345 ASSERT(res);
346 wasWaiting = 0;
347 }
348 else if (m_condition.m_timedOut != 0)
349 m_condition.m_timedOut = 0;
350 }
351 } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) {
352 // timeout occured, normalize the m_condition.m_timedOut count
353 // this may occur if many calls to wait with a timeout are made and
354 // no call to notify_* is made
355 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
356 ASSERT(res == WAIT_OBJECT_0);
357 m_condition.m_blocked -= m_condition.m_timedOut;
358 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
359 ASSERT(res);
360 m_condition.m_timedOut = 0;
361 }
362 res = ::ReleaseMutex(m_condition.m_mutex);
363 ASSERT(res);
364
365 if (wasWaiting == 1) {
366 for (/**/ ; wasTimedOut; --wasTimedOut) {
367 // better now than spurious later
368 res = ::WaitForSingleObject(m_condition.m_queue, INFINITE);
369 ASSERT(res == WAIT_OBJECT_0);
370 }
371 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
372 ASSERT(res);
373 }
374
375 ::EnterCriticalSection (&cs.m_internalMutex);
376}
377
378bool ThreadCondition::timedWait(Mutex& mutex, double interval)
379{
380 // Empty for now
381 ASSERT(false);
382 return false;
383}
384
385void ThreadCondition::signal()
386{
387 unsigned signals = 0;
388
389 DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
390 ASSERT(res == WAIT_OBJECT_0);
391
392 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
393 if (m_condition.m_blocked == 0) {
394 res = ::ReleaseMutex(m_condition.m_mutex);
395 ASSERT(res);
396 return;
397 }
398
399 ++m_condition.m_waitingForRemoval;
400 --m_condition.m_blocked;
401
402 signals = 1;
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 = 1;
412 --m_condition.m_blocked;
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
428void ThreadCondition::broadcast()
429{
430 unsigned signals = 0;
431
432 WORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE);
433 ASSERT(res == WAIT_OBJECT_0);
434
435 if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed
436 if (m_condition.m_blocked == 0) {
437 res = ::ReleaseMutex(m_condition.m_mutex);
438 ASSERT(res);
439 return;
440 }
441
442 m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked);
443 m_condition.m_blocked = 0;
444 } else {
445 res = ::WaitForSingleObject(m_condition.m_gate, INFINITE);
446 ASSERT(res == WAIT_OBJECT_0);
447 if (m_condition.m_blocked > m_condition.m_timedOut) {
448 if (m_condition.m_timedOut != 0) {
449 m_condition.m_blocked -= m_condition.m_timedOut;
450 m_condition.m_timedOut = 0;
451 }
452 signals = m_condition.m_waitingForRemoval = m_condition.m_blocked;
453 m_condition.m_blocked = 0;
454 } else {
455 res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0);
456 ASSERT(res);
457 }
458 }
459
460 res = ::ReleaseMutex(m_condition.m_mutex);
461 ASSERT(res);
462
463 if (signals) {
464 res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0);
465 ASSERT(res);
466 }
467}
468
469} // namespace WTF
Note: See TracBrowser for help on using the repository browser.