]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
3 | * Copyright (C) 2009 Google Inc. All rights reserved. | |
f9bf01c6 | 4 | * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. |
9dae56ea A |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
16 | * its contributors may be used to endorse or promote products derived | |
17 | * from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | ||
31 | /* | |
32 | * There are numerous academic and practical works on how to implement pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast | |
33 | * functions on Win32. Here is one example: http://www.cs.wustl.edu/~schmidt/win32-cv-1.html which is widely credited as a 'starting point' | |
34 | * of modern attempts. There are several more or less proven implementations, one in Boost C++ library (http://www.boost.org) and another | |
35 | * in pthreads-win32 (http://sourceware.org/pthreads-win32/). | |
36 | * | |
37 | * The number of articles and discussions is the evidence of significant difficulties in implementing these primitives correctly. | |
38 | * The brief search of revisions, ChangeLog entries, discussions in comp.programming.threads and other places clearly documents | |
39 | * numerous pitfalls and performance problems the authors had to overcome to arrive to the suitable implementations. | |
40 | * Optimally, WebKit would use one of those supported/tested libraries directly. To roll out our own implementation is impractical, | |
41 | * if even for the lack of sufficient testing. However, a faithful reproduction of the code from one of the popular supported | |
42 | * libraries seems to be a good compromise. | |
43 | * | |
44 | * The early Boost implementation (http://www.boxbackup.org/trac/browser/box/nick/win/lib/win32/boost_1_32_0/libs/thread/src/condition.cpp?rev=30) | |
45 | * is identical to pthreads-win32 (http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32). | |
46 | * Current Boost uses yet another (although seemingly equivalent) algorithm which came from their 'thread rewrite' effort. | |
47 | * | |
48 | * This file includes timedWait/signal/broadcast implementations translated to WebKit coding style from the latest algorithm by | |
49 | * Alexander Terekhov and Louis Thomas, as captured here: http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32 | |
50 | * It replaces the implementation of their previous algorithm, also documented in the same source above. | |
51 | * The naming and comments are left very close to original to enable easy cross-check. | |
52 | * | |
53 | * The corresponding Pthreads-win32 License is included below, and CONTRIBUTORS file which it refers to is added to | |
54 | * source directory (as CONTRIBUTORS.pthreads-win32). | |
55 | */ | |
56 | ||
57 | /* | |
58 | * Pthreads-win32 - POSIX Threads Library for Win32 | |
59 | * Copyright(C) 1998 John E. Bossom | |
60 | * Copyright(C) 1999,2005 Pthreads-win32 contributors | |
61 | * | |
62 | * Contact Email: rpj@callisto.canberra.edu.au | |
63 | * | |
64 | * The current list of contributors is contained | |
65 | * in the file CONTRIBUTORS included with the source | |
66 | * code distribution. The list can also be seen at the | |
67 | * following World Wide Web location: | |
68 | * http://sources.redhat.com/pthreads-win32/contributors.html | |
69 | * | |
70 | * This library is free software; you can redistribute it and/or | |
71 | * modify it under the terms of the GNU Lesser General Public | |
72 | * License as published by the Free Software Foundation; either | |
73 | * version 2 of the License, or (at your option) any later version. | |
74 | * | |
75 | * This library is distributed in the hope that it will be useful, | |
76 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
77 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
78 | * Lesser General Public License for more details. | |
79 | * | |
80 | * You should have received a copy of the GNU Lesser General Public | |
81 | * License along with this library in the file COPYING.LIB; | |
82 | * if not, write to the Free Software Foundation, Inc., | |
83 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | |
84 | */ | |
85 | ||
86 | #include "config.h" | |
87 | #include "Threading.h" | |
88 | ||
89 | #include "MainThread.h" | |
f9bf01c6 | 90 | #if !USE(PTHREADS) && OS(WINDOWS) |
9dae56ea A |
91 | #include "ThreadSpecific.h" |
92 | #endif | |
f9bf01c6 | 93 | #if !OS(WINCE) |
9dae56ea | 94 | #include <process.h> |
f9bf01c6 A |
95 | #endif |
96 | #if HAVE(ERRNO_H) | |
97 | #include <errno.h> | |
98 | #else | |
99 | #define NO_ERRNO | |
100 | #endif | |
9dae56ea A |
101 | #include <windows.h> |
102 | #include <wtf/CurrentTime.h> | |
103 | #include <wtf/HashMap.h> | |
104 | #include <wtf/MathExtras.h> | |
105 | #include <wtf/RandomNumberSeed.h> | |
106 | ||
107 | namespace WTF { | |
108 | ||
ba379fdc | 109 | // MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadNameInternal all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>. |
9dae56ea A |
110 | static const DWORD MS_VC_EXCEPTION = 0x406D1388; |
111 | ||
112 | #pragma pack(push, 8) | |
113 | typedef struct tagTHREADNAME_INFO { | |
114 | DWORD dwType; // must be 0x1000 | |
115 | LPCSTR szName; // pointer to name (in user addr space) | |
116 | DWORD dwThreadID; // thread ID (-1=caller thread) | |
117 | DWORD dwFlags; // reserved for future use, must be zero | |
118 | } THREADNAME_INFO; | |
119 | #pragma pack(pop) | |
120 | ||
f9bf01c6 | 121 | void initializeCurrentThreadInternal(const char* szThreadName) |
9dae56ea | 122 | { |
9dae56ea A |
123 | THREADNAME_INFO info; |
124 | info.dwType = 0x1000; | |
125 | info.szName = szThreadName; | |
ba379fdc | 126 | info.dwThreadID = GetCurrentThreadId(); |
9dae56ea A |
127 | info.dwFlags = 0; |
128 | ||
129 | __try { | |
130 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info)); | |
131 | } __except (EXCEPTION_CONTINUE_EXECUTION) { | |
132 | } | |
133 | } | |
134 | ||
135 | static Mutex* atomicallyInitializedStaticMutex; | |
136 | ||
137 | void lockAtomicallyInitializedStaticMutex() | |
138 | { | |
139 | ASSERT(atomicallyInitializedStaticMutex); | |
140 | atomicallyInitializedStaticMutex->lock(); | |
141 | } | |
142 | ||
143 | void unlockAtomicallyInitializedStaticMutex() | |
144 | { | |
145 | atomicallyInitializedStaticMutex->unlock(); | |
146 | } | |
147 | ||
9dae56ea A |
148 | static Mutex& threadMapMutex() |
149 | { | |
150 | static Mutex mutex; | |
151 | return mutex; | |
152 | } | |
153 | ||
154 | void initializeThreading() | |
155 | { | |
4e4e5a6f A |
156 | if (atomicallyInitializedStaticMutex) |
157 | return; | |
158 | ||
159 | atomicallyInitializedStaticMutex = new Mutex; | |
160 | threadMapMutex(); | |
161 | initializeRandomNumberGenerator(); | |
9dae56ea A |
162 | } |
163 | ||
164 | static HashMap<DWORD, HANDLE>& threadMap() | |
165 | { | |
166 | static HashMap<DWORD, HANDLE> map; | |
167 | return map; | |
168 | } | |
169 | ||
170 | static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle) | |
171 | { | |
172 | MutexLocker locker(threadMapMutex()); | |
173 | ASSERT(!threadMap().contains(threadID)); | |
174 | threadMap().add(threadID, threadHandle); | |
175 | } | |
176 | ||
177 | static HANDLE threadHandleForIdentifier(ThreadIdentifier id) | |
178 | { | |
179 | MutexLocker locker(threadMapMutex()); | |
180 | return threadMap().get(id); | |
181 | } | |
182 | ||
183 | static void clearThreadHandleForIdentifier(ThreadIdentifier id) | |
184 | { | |
185 | MutexLocker locker(threadMapMutex()); | |
186 | ASSERT(threadMap().contains(id)); | |
187 | threadMap().remove(id); | |
188 | } | |
189 | ||
190 | struct ThreadFunctionInvocation { | |
191 | ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {} | |
192 | ||
193 | ThreadFunction function; | |
194 | void* data; | |
195 | }; | |
196 | ||
197 | static unsigned __stdcall wtfThreadEntryPoint(void* param) | |
198 | { | |
199 | ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param); | |
200 | delete static_cast<ThreadFunctionInvocation*>(param); | |
201 | ||
202 | void* result = invocation.function(invocation.data); | |
203 | ||
f9bf01c6 | 204 | #if !USE(PTHREADS) && OS(WINDOWS) |
9dae56ea A |
205 | // Do the TLS cleanup. |
206 | ThreadSpecificThreadExit(); | |
207 | #endif | |
208 | ||
209 | return reinterpret_cast<unsigned>(result); | |
210 | } | |
211 | ||
212 | ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char* threadName) | |
213 | { | |
214 | unsigned threadIdentifier = 0; | |
215 | ThreadIdentifier threadID = 0; | |
216 | ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data); | |
f9bf01c6 A |
217 | #if OS(WINCE) |
218 | // This is safe on WINCE, since CRT is in the core and innately multithreaded. | |
219 | // On desktop Windows, need to use _beginthreadex (not available on WinCE) if using any CRT functions | |
220 | HANDLE threadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)wtfThreadEntryPoint, invocation, 0, (LPDWORD)&threadIdentifier); | |
221 | #else | |
9dae56ea | 222 | HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier)); |
f9bf01c6 | 223 | #endif |
9dae56ea | 224 | if (!threadHandle) { |
f9bf01c6 A |
225 | #if OS(WINCE) |
226 | LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, ::GetLastError()); | |
227 | #elif defined(NO_ERRNO) | |
228 | LOG_ERROR("Failed to create thread at entry point %p with data %p.", entryPoint, data); | |
229 | #else | |
9dae56ea | 230 | LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno); |
f9bf01c6 | 231 | #endif |
9dae56ea A |
232 | return 0; |
233 | } | |
234 | ||
9dae56ea A |
235 | threadID = static_cast<ThreadIdentifier>(threadIdentifier); |
236 | storeThreadHandleByIdentifier(threadIdentifier, threadHandle); | |
237 | ||
238 | return threadID; | |
239 | } | |
240 | ||
241 | int waitForThreadCompletion(ThreadIdentifier threadID, void** result) | |
242 | { | |
243 | ASSERT(threadID); | |
244 | ||
245 | HANDLE threadHandle = threadHandleForIdentifier(threadID); | |
246 | if (!threadHandle) | |
247 | LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID); | |
248 | ||
249 | DWORD joinResult = WaitForSingleObject(threadHandle, INFINITE); | |
250 | if (joinResult == WAIT_FAILED) | |
251 | LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID); | |
252 | ||
253 | CloseHandle(threadHandle); | |
254 | clearThreadHandleForIdentifier(threadID); | |
255 | ||
256 | return joinResult; | |
257 | } | |
258 | ||
259 | void detachThread(ThreadIdentifier threadID) | |
260 | { | |
261 | ASSERT(threadID); | |
ba379fdc | 262 | |
9dae56ea A |
263 | HANDLE threadHandle = threadHandleForIdentifier(threadID); |
264 | if (threadHandle) | |
265 | CloseHandle(threadHandle); | |
266 | clearThreadHandleForIdentifier(threadID); | |
267 | } | |
268 | ||
269 | ThreadIdentifier currentThread() | |
270 | { | |
271 | return static_cast<ThreadIdentifier>(GetCurrentThreadId()); | |
272 | } | |
273 | ||
9dae56ea A |
274 | Mutex::Mutex() |
275 | { | |
276 | m_mutex.m_recursionCount = 0; | |
277 | InitializeCriticalSection(&m_mutex.m_internalMutex); | |
278 | } | |
279 | ||
280 | Mutex::~Mutex() | |
281 | { | |
282 | DeleteCriticalSection(&m_mutex.m_internalMutex); | |
283 | } | |
284 | ||
285 | void Mutex::lock() | |
286 | { | |
287 | EnterCriticalSection(&m_mutex.m_internalMutex); | |
288 | ++m_mutex.m_recursionCount; | |
289 | } | |
290 | ||
291 | bool Mutex::tryLock() | |
292 | { | |
293 | // This method is modeled after the behavior of pthread_mutex_trylock, | |
294 | // which will return an error if the lock is already owned by the | |
295 | // current thread. Since the primitive Win32 'TryEnterCriticalSection' | |
296 | // treats this as a successful case, it changes the behavior of several | |
297 | // tests in WebKit that check to see if the current thread already | |
298 | // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord) | |
299 | DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex); | |
300 | ||
301 | if (result != 0) { // We got the lock | |
302 | // If this thread already had the lock, we must unlock and | |
303 | // return false so that we mimic the behavior of POSIX's | |
304 | // pthread_mutex_trylock: | |
305 | if (m_mutex.m_recursionCount > 0) { | |
306 | LeaveCriticalSection(&m_mutex.m_internalMutex); | |
307 | return false; | |
308 | } | |
309 | ||
310 | ++m_mutex.m_recursionCount; | |
311 | return true; | |
312 | } | |
313 | ||
314 | return false; | |
315 | } | |
316 | ||
317 | void Mutex::unlock() | |
318 | { | |
319 | --m_mutex.m_recursionCount; | |
320 | LeaveCriticalSection(&m_mutex.m_internalMutex); | |
321 | } | |
322 | ||
323 | bool PlatformCondition::timedWait(PlatformMutex& mutex, DWORD durationMilliseconds) | |
324 | { | |
325 | // Enter the wait state. | |
326 | DWORD res = WaitForSingleObject(m_blockLock, INFINITE); | |
327 | ASSERT(res == WAIT_OBJECT_0); | |
328 | ++m_waitersBlocked; | |
329 | res = ReleaseSemaphore(m_blockLock, 1, 0); | |
330 | ASSERT(res); | |
331 | ||
332 | LeaveCriticalSection(&mutex.m_internalMutex); | |
333 | ||
334 | // Main wait - use timeout. | |
335 | bool timedOut = (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT); | |
336 | ||
337 | res = WaitForSingleObject(m_unblockLock, INFINITE); | |
338 | ASSERT(res == WAIT_OBJECT_0); | |
339 | ||
340 | int signalsLeft = m_waitersToUnblock; | |
341 | ||
342 | if (m_waitersToUnblock) | |
343 | --m_waitersToUnblock; | |
344 | else if (++m_waitersGone == (INT_MAX / 2)) { // timeout/canceled or spurious semaphore | |
345 | // timeout or spurious wakeup occured, normalize the m_waitersGone count | |
346 | // this may occur if many calls to wait with a timeout are made and | |
347 | // no call to notify_* is made | |
348 | res = WaitForSingleObject(m_blockLock, INFINITE); | |
349 | ASSERT(res == WAIT_OBJECT_0); | |
350 | m_waitersBlocked -= m_waitersGone; | |
351 | res = ReleaseSemaphore(m_blockLock, 1, 0); | |
352 | ASSERT(res); | |
353 | m_waitersGone = 0; | |
354 | } | |
355 | ||
356 | res = ReleaseMutex(m_unblockLock); | |
357 | ASSERT(res); | |
358 | ||
359 | if (signalsLeft == 1) { | |
360 | res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate. | |
361 | ASSERT(res); | |
362 | } | |
363 | ||
364 | EnterCriticalSection (&mutex.m_internalMutex); | |
365 | ||
366 | return !timedOut; | |
367 | } | |
368 | ||
369 | void PlatformCondition::signal(bool unblockAll) | |
370 | { | |
371 | unsigned signalsToIssue = 0; | |
372 | ||
373 | DWORD res = WaitForSingleObject(m_unblockLock, INFINITE); | |
374 | ASSERT(res == WAIT_OBJECT_0); | |
375 | ||
376 | if (m_waitersToUnblock) { // the gate is already closed | |
377 | if (!m_waitersBlocked) { // no-op | |
378 | res = ReleaseMutex(m_unblockLock); | |
379 | ASSERT(res); | |
380 | return; | |
381 | } | |
382 | ||
383 | if (unblockAll) { | |
384 | signalsToIssue = m_waitersBlocked; | |
385 | m_waitersToUnblock += m_waitersBlocked; | |
386 | m_waitersBlocked = 0; | |
387 | } else { | |
388 | signalsToIssue = 1; | |
389 | ++m_waitersToUnblock; | |
390 | --m_waitersBlocked; | |
391 | } | |
392 | } else if (m_waitersBlocked > m_waitersGone) { | |
393 | res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate. | |
394 | ASSERT(res == WAIT_OBJECT_0); | |
395 | if (m_waitersGone != 0) { | |
396 | m_waitersBlocked -= m_waitersGone; | |
397 | m_waitersGone = 0; | |
398 | } | |
399 | if (unblockAll) { | |
400 | signalsToIssue = m_waitersBlocked; | |
401 | m_waitersToUnblock = m_waitersBlocked; | |
402 | m_waitersBlocked = 0; | |
403 | } else { | |
404 | signalsToIssue = 1; | |
405 | m_waitersToUnblock = 1; | |
406 | --m_waitersBlocked; | |
407 | } | |
408 | } else { // No-op. | |
409 | res = ReleaseMutex(m_unblockLock); | |
410 | ASSERT(res); | |
411 | return; | |
412 | } | |
413 | ||
414 | res = ReleaseMutex(m_unblockLock); | |
415 | ASSERT(res); | |
416 | ||
417 | if (signalsToIssue) { | |
418 | res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0); | |
419 | ASSERT(res); | |
420 | } | |
421 | } | |
422 | ||
423 | static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1); | |
424 | ||
425 | ThreadCondition::ThreadCondition() | |
426 | { | |
427 | m_condition.m_waitersGone = 0; | |
428 | m_condition.m_waitersBlocked = 0; | |
429 | m_condition.m_waitersToUnblock = 0; | |
430 | m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0); | |
431 | m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0); | |
432 | m_condition.m_unblockLock = CreateMutex(0, 0, 0); | |
433 | ||
434 | if (!m_condition.m_blockLock || !m_condition.m_blockQueue || !m_condition.m_unblockLock) { | |
435 | if (m_condition.m_blockLock) | |
436 | CloseHandle(m_condition.m_blockLock); | |
437 | if (m_condition.m_blockQueue) | |
438 | CloseHandle(m_condition.m_blockQueue); | |
439 | if (m_condition.m_unblockLock) | |
440 | CloseHandle(m_condition.m_unblockLock); | |
441 | } | |
442 | } | |
443 | ||
444 | ThreadCondition::~ThreadCondition() | |
445 | { | |
446 | CloseHandle(m_condition.m_blockLock); | |
447 | CloseHandle(m_condition.m_blockQueue); | |
448 | CloseHandle(m_condition.m_unblockLock); | |
449 | } | |
450 | ||
451 | void ThreadCondition::wait(Mutex& mutex) | |
452 | { | |
453 | m_condition.timedWait(mutex.impl(), INFINITE); | |
454 | } | |
455 | ||
456 | bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime) | |
457 | { | |
458 | double currentTime = WTF::currentTime(); | |
459 | ||
460 | // Time is in the past - return immediately. | |
461 | if (absoluteTime < currentTime) | |
462 | return false; | |
463 | ||
ba379fdc A |
464 | // Time is too far in the future (and would overflow unsigned long) - wait forever. |
465 | if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) { | |
466 | wait(mutex); | |
467 | return true; | |
468 | } | |
9dae56ea | 469 | |
ba379fdc | 470 | double intervalMilliseconds = (absoluteTime - currentTime) * 1000.0; |
9dae56ea A |
471 | return m_condition.timedWait(mutex.impl(), static_cast<unsigned long>(intervalMilliseconds)); |
472 | } | |
473 | ||
474 | void ThreadCondition::signal() | |
475 | { | |
476 | m_condition.signal(false); // Unblock only 1 thread. | |
477 | } | |
478 | ||
479 | void ThreadCondition::broadcast() | |
480 | { | |
481 | m_condition.signal(true); // Unblock all threads. | |
482 | } | |
483 | ||
484 | } // namespace WTF |