| 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 |