OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2009 Google Inc. All rights reserved. | |
4 * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. | |
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 | |
33 * pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast | |
34 * functions on Win32. Here is one example: | |
35 * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html which is widely credited as | |
36 * a 'starting point' of modern attempts. There are several more or less proven | |
37 * implementations, one in Boost C++ library (http://www.boost.org) and another | |
38 * in pthreads-win32 (http://sourceware.org/pthreads-win32/). | |
39 * | |
40 * The number of articles and discussions is the evidence of significant | |
41 * difficulties in implementing these primitives correctly. The brief search | |
42 * of revisions, ChangeLog entries, discussions in comp.programming.threads and | |
43 * other places clearly documents numerous pitfalls and performance problems | |
44 * the authors had to overcome to arrive to the suitable implementations. | |
45 * Optimally, WebKit would use one of those supported/tested libraries | |
46 * directly. To roll out our own implementation is impractical, if even for | |
47 * the lack of sufficient testing. However, a faithful reproduction of the code | |
48 * from one of the popular supported libraries seems to be a good compromise. | |
49 * | |
50 * The early Boost implementation | |
51 * (http://www.boxbackup.org/trac/browser/box/nick/win/lib/win32/boost_1_32_0/li
bs/thread/src/condition.cpp?rev=30) | |
52 * is identical to pthreads-win32 | |
53 * (http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.
10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32). | |
54 * Current Boost uses yet another (although seemingly equivalent) algorithm | |
55 * which came from their 'thread rewrite' effort. | |
56 * | |
57 * This file includes timedWait/signal/broadcast implementations translated to | |
58 * WebKit coding style from the latest algorithm by Alexander Terekhov and | |
59 * Louis Thomas, as captured here: | |
60 * http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.1
0&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32 | |
61 * It replaces the implementation of their previous algorithm, also documented | |
62 * in the same source above. The naming and comments are left very close to | |
63 * original to enable easy cross-check. | |
64 * | |
65 * The corresponding Pthreads-win32 License is included below, and CONTRIBUTORS | |
66 * file which it refers to is added to source directory (as | |
67 * CONTRIBUTORS.pthreads-win32). | |
68 */ | |
69 | |
70 /* | |
71 * Pthreads-win32 - POSIX Threads Library for Win32 | |
72 * Copyright(C) 1998 John E. Bossom | |
73 * Copyright(C) 1999,2005 Pthreads-win32 contributors | |
74 * | |
75 * Contact Email: rpj@callisto.canberra.edu.au | |
76 * | |
77 * The current list of contributors is contained | |
78 * in the file CONTRIBUTORS included with the source | |
79 * code distribution. The list can also be seen at the | |
80 * following World Wide Web location: | |
81 * http://sources.redhat.com/pthreads-win32/contributors.html | |
82 * | |
83 * This library is free software; you can redistribute it and/or | |
84 * modify it under the terms of the GNU Lesser General Public | |
85 * License as published by the Free Software Foundation; either | |
86 * version 2 of the License, or (at your option) any later version. | |
87 * | |
88 * This library is distributed in the hope that it will be useful, | |
89 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
90 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
91 * Lesser General Public License for more details. | |
92 * | |
93 * You should have received a copy of the GNU Lesser General Public | |
94 * License along with this library in the file COPYING.LIB; | |
95 * if not, write to the Free Software Foundation, Inc., | |
96 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | |
97 */ | |
98 | |
99 #include "wtf/Threading.h" | |
100 | |
101 #if OS(WIN) | |
102 | |
103 #include "wtf/CurrentTime.h" | |
104 #include "wtf/DateMath.h" | |
105 #include "wtf/HashMap.h" | |
106 #include "wtf/MathExtras.h" | |
107 #include "wtf/ThreadSpecific.h" | |
108 #include "wtf/ThreadingPrimitives.h" | |
109 #include "wtf/WTFThreadData.h" | |
110 #include "wtf/dtoa/double-conversion.h" | |
111 #include <errno.h> | |
112 #include <process.h> | |
113 #include <windows.h> | |
114 | |
115 namespace WTF { | |
116 | |
117 // THREADNAME_INFO comes from | |
118 // <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>. | |
119 #pragma pack(push, 8) | |
120 typedef struct tagTHREADNAME_INFO { | |
121 DWORD dwType; // must be 0x1000 | |
122 LPCSTR szName; // pointer to name (in user addr space) | |
123 DWORD dwThreadID; // thread ID (-1=caller thread) | |
124 DWORD dwFlags; // reserved for future use, must be zero | |
125 } THREADNAME_INFO; | |
126 #pragma pack(pop) | |
127 | |
128 namespace internal { | |
129 | |
130 ThreadIdentifier currentThreadSyscall() { | |
131 return static_cast<ThreadIdentifier>(GetCurrentThreadId()); | |
132 } | |
133 | |
134 } // namespace internal | |
135 | |
136 void initializeThreading() { | |
137 // This should only be called once. | |
138 WTFThreadData::initialize(); | |
139 | |
140 initializeDates(); | |
141 // Force initialization of static DoubleToStringConverter converter variable | |
142 // inside EcmaScriptConverter function while we are in single thread mode. | |
143 double_conversion::DoubleToStringConverter::EcmaScriptConverter(); | |
144 } | |
145 | |
146 namespace { | |
147 ThreadSpecificKey s_currentThreadKey; | |
148 bool s_currentThreadKeyInitialized = false; | |
149 } // namespace | |
150 | |
151 void initializeCurrentThread() { | |
152 DCHECK(!s_currentThreadKeyInitialized); | |
153 threadSpecificKeyCreate(&s_currentThreadKey, [](void*) {}); | |
154 s_currentThreadKeyInitialized = true; | |
155 } | |
156 | |
157 ThreadIdentifier currentThread() { | |
158 // This doesn't use WTF::ThreadSpecific (e.g. WTFThreadData) because | |
159 // ThreadSpecific now depends on currentThread. It is necessary to avoid this | |
160 // or a similar loop: | |
161 // | |
162 // currentThread | |
163 // -> wtfThreadData | |
164 // -> ThreadSpecific::operator* | |
165 // -> isMainThread | |
166 // -> currentThread | |
167 static_assert(sizeof(ThreadIdentifier) <= sizeof(void*), | |
168 "ThreadIdentifier must fit in a void*."); | |
169 DCHECK(s_currentThreadKeyInitialized); | |
170 void* value = threadSpecificGet(s_currentThreadKey); | |
171 if (UNLIKELY(!value)) { | |
172 value = reinterpret_cast<void*>(internal::currentThreadSyscall()); | |
173 DCHECK(value); | |
174 threadSpecificSet(s_currentThreadKey, value); | |
175 } | |
176 return reinterpret_cast<intptr_t>(threadSpecificGet(s_currentThreadKey)); | |
177 } | |
178 | |
179 MutexBase::MutexBase(bool recursive) { | |
180 m_mutex.m_recursionCount = 0; | |
181 InitializeCriticalSection(&m_mutex.m_internalMutex); | |
182 } | |
183 | |
184 MutexBase::~MutexBase() { | |
185 DeleteCriticalSection(&m_mutex.m_internalMutex); | |
186 } | |
187 | |
188 void MutexBase::lock() { | |
189 EnterCriticalSection(&m_mutex.m_internalMutex); | |
190 ++m_mutex.m_recursionCount; | |
191 } | |
192 | |
193 void MutexBase::unlock() { | |
194 DCHECK(m_mutex.m_recursionCount); | |
195 --m_mutex.m_recursionCount; | |
196 LeaveCriticalSection(&m_mutex.m_internalMutex); | |
197 } | |
198 | |
199 bool Mutex::tryLock() { | |
200 // This method is modeled after the behavior of pthread_mutex_trylock, | |
201 // which will return an error if the lock is already owned by the | |
202 // current thread. Since the primitive Win32 'TryEnterCriticalSection' | |
203 // treats this as a successful case, it changes the behavior of several | |
204 // tests in WebKit that check to see if the current thread already | |
205 // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord) | |
206 DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex); | |
207 | |
208 if (result != 0) { // We got the lock | |
209 // If this thread already had the lock, we must unlock and return | |
210 // false since this is a non-recursive mutex. This is to mimic the | |
211 // behavior of POSIX's pthread_mutex_trylock. We don't do this | |
212 // check in the lock method (presumably due to performance?). This | |
213 // means lock() will succeed even if the current thread has already | |
214 // entered the critical section. | |
215 if (m_mutex.m_recursionCount > 0) { | |
216 LeaveCriticalSection(&m_mutex.m_internalMutex); | |
217 return false; | |
218 } | |
219 ++m_mutex.m_recursionCount; | |
220 return true; | |
221 } | |
222 | |
223 return false; | |
224 } | |
225 | |
226 bool RecursiveMutex::tryLock() { | |
227 // CRITICAL_SECTION is recursive/reentrant so TryEnterCriticalSection will | |
228 // succeed if the current thread is already in the critical section. | |
229 DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex); | |
230 if (result == 0) { // We didn't get the lock. | |
231 return false; | |
232 } | |
233 ++m_mutex.m_recursionCount; | |
234 return true; | |
235 } | |
236 | |
237 bool PlatformCondition::timedWait(PlatformMutex& mutex, | |
238 DWORD durationMilliseconds) { | |
239 // Enter the wait state. | |
240 DWORD res = WaitForSingleObject(m_blockLock, INFINITE); | |
241 DCHECK_EQ(res, WAIT_OBJECT_0); | |
242 ++m_waitersBlocked; | |
243 res = ReleaseSemaphore(m_blockLock, 1, 0); | |
244 DCHECK(res); | |
245 | |
246 --mutex.m_recursionCount; | |
247 LeaveCriticalSection(&mutex.m_internalMutex); | |
248 | |
249 // Main wait - use timeout. | |
250 bool timedOut = | |
251 (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT); | |
252 | |
253 res = WaitForSingleObject(m_unblockLock, INFINITE); | |
254 DCHECK_EQ(res, WAIT_OBJECT_0); | |
255 | |
256 int signalsLeft = m_waitersToUnblock; | |
257 | |
258 if (m_waitersToUnblock) { | |
259 --m_waitersToUnblock; | |
260 } else if (++m_waitersGone == (INT_MAX / 2)) { | |
261 // timeout/canceled or spurious semaphore timeout or spurious wakeup | |
262 // occured, normalize the m_waitersGone count this may occur if many | |
263 // calls to wait with a timeout are made and no call to notify_* is made | |
264 res = WaitForSingleObject(m_blockLock, INFINITE); | |
265 DCHECK_EQ(res, WAIT_OBJECT_0); | |
266 m_waitersBlocked -= m_waitersGone; | |
267 res = ReleaseSemaphore(m_blockLock, 1, 0); | |
268 DCHECK(res); | |
269 m_waitersGone = 0; | |
270 } | |
271 | |
272 res = ReleaseMutex(m_unblockLock); | |
273 DCHECK(res); | |
274 | |
275 if (signalsLeft == 1) { | |
276 res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate. | |
277 DCHECK(res); | |
278 } | |
279 | |
280 EnterCriticalSection(&mutex.m_internalMutex); | |
281 ++mutex.m_recursionCount; | |
282 | |
283 return !timedOut; | |
284 } | |
285 | |
286 void PlatformCondition::signal(bool unblockAll) { | |
287 unsigned signalsToIssue = 0; | |
288 | |
289 DWORD res = WaitForSingleObject(m_unblockLock, INFINITE); | |
290 DCHECK_EQ(res, WAIT_OBJECT_0); | |
291 | |
292 if (m_waitersToUnblock) { // the gate is already closed | |
293 if (!m_waitersBlocked) { // no-op | |
294 res = ReleaseMutex(m_unblockLock); | |
295 DCHECK(res); | |
296 return; | |
297 } | |
298 | |
299 if (unblockAll) { | |
300 signalsToIssue = m_waitersBlocked; | |
301 m_waitersToUnblock += m_waitersBlocked; | |
302 m_waitersBlocked = 0; | |
303 } else { | |
304 signalsToIssue = 1; | |
305 ++m_waitersToUnblock; | |
306 --m_waitersBlocked; | |
307 } | |
308 } else if (m_waitersBlocked > m_waitersGone) { | |
309 res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate. | |
310 DCHECK_EQ(res, WAIT_OBJECT_0); | |
311 if (m_waitersGone != 0) { | |
312 m_waitersBlocked -= m_waitersGone; | |
313 m_waitersGone = 0; | |
314 } | |
315 if (unblockAll) { | |
316 signalsToIssue = m_waitersBlocked; | |
317 m_waitersToUnblock = m_waitersBlocked; | |
318 m_waitersBlocked = 0; | |
319 } else { | |
320 signalsToIssue = 1; | |
321 m_waitersToUnblock = 1; | |
322 --m_waitersBlocked; | |
323 } | |
324 } else { // No-op. | |
325 res = ReleaseMutex(m_unblockLock); | |
326 DCHECK(res); | |
327 return; | |
328 } | |
329 | |
330 res = ReleaseMutex(m_unblockLock); | |
331 DCHECK(res); | |
332 | |
333 if (signalsToIssue) { | |
334 res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0); | |
335 DCHECK(res); | |
336 } | |
337 } | |
338 | |
339 static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1); | |
340 | |
341 ThreadCondition::ThreadCondition() { | |
342 m_condition.m_waitersGone = 0; | |
343 m_condition.m_waitersBlocked = 0; | |
344 m_condition.m_waitersToUnblock = 0; | |
345 m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0); | |
346 m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0); | |
347 m_condition.m_unblockLock = CreateMutex(0, 0, 0); | |
348 | |
349 if (!m_condition.m_blockLock || !m_condition.m_blockQueue || | |
350 !m_condition.m_unblockLock) { | |
351 if (m_condition.m_blockLock) | |
352 CloseHandle(m_condition.m_blockLock); | |
353 if (m_condition.m_blockQueue) | |
354 CloseHandle(m_condition.m_blockQueue); | |
355 if (m_condition.m_unblockLock) | |
356 CloseHandle(m_condition.m_unblockLock); | |
357 | |
358 m_condition.m_blockLock = nullptr; | |
359 m_condition.m_blockQueue = nullptr; | |
360 m_condition.m_unblockLock = nullptr; | |
361 } | |
362 } | |
363 | |
364 ThreadCondition::~ThreadCondition() { | |
365 if (m_condition.m_blockLock) | |
366 CloseHandle(m_condition.m_blockLock); | |
367 if (m_condition.m_blockQueue) | |
368 CloseHandle(m_condition.m_blockQueue); | |
369 if (m_condition.m_unblockLock) | |
370 CloseHandle(m_condition.m_unblockLock); | |
371 } | |
372 | |
373 void ThreadCondition::wait(MutexBase& mutex) { | |
374 m_condition.timedWait(mutex.impl(), INFINITE); | |
375 } | |
376 | |
377 bool ThreadCondition::timedWait(MutexBase& mutex, double absoluteTime) { | |
378 DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime); | |
379 | |
380 if (!interval) { | |
381 // Consider the wait to have timed out, even if our condition has already | |
382 // been signaled, to match the pthreads implementation. | |
383 return false; | |
384 } | |
385 | |
386 return m_condition.timedWait(mutex.impl(), interval); | |
387 } | |
388 | |
389 void ThreadCondition::signal() { | |
390 m_condition.signal(false); // Unblock only 1 thread. | |
391 } | |
392 | |
393 void ThreadCondition::broadcast() { | |
394 m_condition.signal(true); // Unblock all threads. | |
395 } | |
396 | |
397 DWORD absoluteTimeToWaitTimeoutInterval(double absoluteTime) { | |
398 double currentTime = WTF::currentTime(); | |
399 | |
400 // Time is in the past - return immediately. | |
401 if (absoluteTime < currentTime) | |
402 return 0; | |
403 | |
404 // Time is too far in the future (and would overflow unsigned long) - wait | |
405 // forever. | |
406 if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0) | |
407 return INFINITE; | |
408 | |
409 return static_cast<DWORD>((absoluteTime - currentTime) * 1000.0); | |
410 } | |
411 | |
412 #if DCHECK_IS_ON() | |
413 static bool s_threadCreated = false; | |
414 | |
415 bool isBeforeThreadCreated() { | |
416 return !s_threadCreated; | |
417 } | |
418 | |
419 void willCreateThread() { | |
420 s_threadCreated = true; | |
421 } | |
422 #endif | |
423 | |
424 } // namespace WTF | |
425 | |
426 #endif // OS(WIN) | |
OLD | NEW |