Home | History | Annotate | Download | only in wtf
      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 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 (at) 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"
     90 #include "ThreadFunctionInvocation.h"
     91 #include <windows.h>
     92 #include <wtf/CurrentTime.h>
     93 #include <wtf/HashMap.h>
     94 #include <wtf/MathExtras.h>
     95 #include <wtf/OwnPtr.h>
     96 #include <wtf/PassOwnPtr.h>
     97 #include <wtf/RandomNumberSeed.h>
     98 
     99 #if !USE(PTHREADS) && OS(WINDOWS)
    100 #include "ThreadSpecific.h"
    101 #endif
    102 
    103 #if !OS(WINCE)
    104 #include <process.h>
    105 #endif
    106 
    107 #if HAVE(ERRNO_H)
    108 #include <errno.h>
    109 #endif
    110 
    111 namespace WTF {
    112 
    113 // MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadNameInternal all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>.
    114 static const DWORD MS_VC_EXCEPTION = 0x406D1388;
    115 
    116 #pragma pack(push, 8)
    117 typedef struct tagTHREADNAME_INFO {
    118     DWORD dwType; // must be 0x1000
    119     LPCSTR szName; // pointer to name (in user addr space)
    120     DWORD dwThreadID; // thread ID (-1=caller thread)
    121     DWORD dwFlags; // reserved for future use, must be zero
    122 } THREADNAME_INFO;
    123 #pragma pack(pop)
    124 
    125 void initializeCurrentThreadInternal(const char* szThreadName)
    126 {
    127     THREADNAME_INFO info;
    128     info.dwType = 0x1000;
    129     info.szName = szThreadName;
    130     info.dwThreadID = GetCurrentThreadId();
    131     info.dwFlags = 0;
    132 
    133     __try {
    134         RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
    135     } __except (EXCEPTION_CONTINUE_EXECUTION) {
    136     }
    137 }
    138 
    139 static Mutex* atomicallyInitializedStaticMutex;
    140 
    141 void lockAtomicallyInitializedStaticMutex()
    142 {
    143     ASSERT(atomicallyInitializedStaticMutex);
    144     atomicallyInitializedStaticMutex->lock();
    145 }
    146 
    147 void unlockAtomicallyInitializedStaticMutex()
    148 {
    149     atomicallyInitializedStaticMutex->unlock();
    150 }
    151 
    152 static Mutex& threadMapMutex()
    153 {
    154     static Mutex mutex;
    155     return mutex;
    156 }
    157 
    158 void initializeThreading()
    159 {
    160     if (atomicallyInitializedStaticMutex)
    161         return;
    162 
    163     atomicallyInitializedStaticMutex = new Mutex;
    164     threadMapMutex();
    165     initializeRandomNumberGenerator();
    166 }
    167 
    168 static HashMap<DWORD, HANDLE>& threadMap()
    169 {
    170     static HashMap<DWORD, HANDLE> map;
    171     return map;
    172 }
    173 
    174 static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle)
    175 {
    176     MutexLocker locker(threadMapMutex());
    177     ASSERT(!threadMap().contains(threadID));
    178     threadMap().add(threadID, threadHandle);
    179 }
    180 
    181 static HANDLE threadHandleForIdentifier(ThreadIdentifier id)
    182 {
    183     MutexLocker locker(threadMapMutex());
    184     return threadMap().get(id);
    185 }
    186 
    187 static void clearThreadHandleForIdentifier(ThreadIdentifier id)
    188 {
    189     MutexLocker locker(threadMapMutex());
    190     ASSERT(threadMap().contains(id));
    191     threadMap().remove(id);
    192 }
    193 
    194 static unsigned __stdcall wtfThreadEntryPoint(void* param)
    195 {
    196     OwnPtr<ThreadFunctionInvocation> invocation = adoptPtr(static_cast<ThreadFunctionInvocation*>(param));
    197     void* result = invocation->function(invocation->data);
    198 
    199 #if !USE(PTHREADS) && OS(WINDOWS)
    200     // Do the TLS cleanup.
    201     ThreadSpecificThreadExit();
    202 #endif
    203 
    204     return reinterpret_cast<unsigned>(result);
    205 }
    206 
    207 ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char* threadName)
    208 {
    209     unsigned threadIdentifier = 0;
    210     ThreadIdentifier threadID = 0;
    211     OwnPtr<ThreadFunctionInvocation> invocation = adoptPtr(new ThreadFunctionInvocation(entryPoint, data));
    212 #if OS(WINCE)
    213     // This is safe on WINCE, since CRT is in the core and innately multithreaded.
    214     // On desktop Windows, need to use _beginthreadex (not available on WinCE) if using any CRT functions
    215     HANDLE threadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)wtfThreadEntryPoint, invocation.get(), 0, (LPDWORD)&threadIdentifier);
    216 #else
    217     HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation.get(), 0, &threadIdentifier));
    218 #endif
    219     if (!threadHandle) {
    220 #if OS(WINCE)
    221         LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, ::GetLastError());
    222 #elif !HAVE(ERRNO_H)
    223         LOG_ERROR("Failed to create thread at entry point %p with data %p.", entryPoint, data);
    224 #else
    225         LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
    226 #endif
    227         return 0;
    228     }
    229 
    230     // The thread will take ownership of invocation.
    231     invocation.leakPtr();
    232 
    233     threadID = static_cast<ThreadIdentifier>(threadIdentifier);
    234     storeThreadHandleByIdentifier(threadIdentifier, threadHandle);
    235 
    236     return threadID;
    237 }
    238 
    239 int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
    240 {
    241     ASSERT(threadID);
    242 
    243     HANDLE threadHandle = threadHandleForIdentifier(threadID);
    244     if (!threadHandle)
    245         LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID);
    246 
    247     DWORD joinResult = WaitForSingleObject(threadHandle, INFINITE);
    248     if (joinResult == WAIT_FAILED)
    249         LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
    250 
    251     CloseHandle(threadHandle);
    252     clearThreadHandleForIdentifier(threadID);
    253 
    254     return joinResult;
    255 }
    256 
    257 void detachThread(ThreadIdentifier threadID)
    258 {
    259     ASSERT(threadID);
    260 
    261     HANDLE threadHandle = threadHandleForIdentifier(threadID);
    262     if (threadHandle)
    263         CloseHandle(threadHandle);
    264     clearThreadHandleForIdentifier(threadID);
    265 }
    266 
    267 void yield()
    268 {
    269     ::Sleep(1);
    270 }
    271 
    272 ThreadIdentifier currentThread()
    273 {
    274     return static_cast<ThreadIdentifier>(GetCurrentThreadId());
    275 }
    276 
    277 Mutex::Mutex()
    278 {
    279     m_mutex.m_recursionCount = 0;
    280     InitializeCriticalSection(&m_mutex.m_internalMutex);
    281 }
    282 
    283 Mutex::~Mutex()
    284 {
    285     DeleteCriticalSection(&m_mutex.m_internalMutex);
    286 }
    287 
    288 void Mutex::lock()
    289 {
    290     EnterCriticalSection(&m_mutex.m_internalMutex);
    291     ++m_mutex.m_recursionCount;
    292 }
    293 
    294 bool Mutex::tryLock()
    295 {
    296     // This method is modeled after the behavior of pthread_mutex_trylock,
    297     // which will return an error if the lock is already owned by the
    298     // current thread.  Since the primitive Win32 'TryEnterCriticalSection'
    299     // treats this as a successful case, it changes the behavior of several
    300     // tests in WebKit that check to see if the current thread already
    301     // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
    302     DWORD result = TryEnterCriticalSection(&m_mutex.m_internalMutex);
    303 
    304     if (result != 0) {       // We got the lock
    305         // If this thread already had the lock, we must unlock and
    306         // return false so that we mimic the behavior of POSIX's
    307         // pthread_mutex_trylock:
    308         if (m_mutex.m_recursionCount > 0) {
    309             LeaveCriticalSection(&m_mutex.m_internalMutex);
    310             return false;
    311         }
    312 
    313         ++m_mutex.m_recursionCount;
    314         return true;
    315     }
    316 
    317     return false;
    318 }
    319 
    320 void Mutex::unlock()
    321 {
    322     --m_mutex.m_recursionCount;
    323     LeaveCriticalSection(&m_mutex.m_internalMutex);
    324 }
    325 
    326 bool PlatformCondition::timedWait(PlatformMutex& mutex, DWORD durationMilliseconds)
    327 {
    328     // Enter the wait state.
    329     DWORD res = WaitForSingleObject(m_blockLock, INFINITE);
    330     ASSERT(res == WAIT_OBJECT_0);
    331     ++m_waitersBlocked;
    332     res = ReleaseSemaphore(m_blockLock, 1, 0);
    333     ASSERT(res);
    334 
    335     --mutex.m_recursionCount;
    336     LeaveCriticalSection(&mutex.m_internalMutex);
    337 
    338     // Main wait - use timeout.
    339     bool timedOut = (WaitForSingleObject(m_blockQueue, durationMilliseconds) == WAIT_TIMEOUT);
    340 
    341     res = WaitForSingleObject(m_unblockLock, INFINITE);
    342     ASSERT(res == WAIT_OBJECT_0);
    343 
    344     int signalsLeft = m_waitersToUnblock;
    345 
    346     if (m_waitersToUnblock)
    347         --m_waitersToUnblock;
    348     else if (++m_waitersGone == (INT_MAX / 2)) { // timeout/canceled or spurious semaphore
    349         // timeout or spurious wakeup occured, normalize the m_waitersGone count
    350         // this may occur if many calls to wait with a timeout are made and
    351         // no call to notify_* is made
    352         res = WaitForSingleObject(m_blockLock, INFINITE);
    353         ASSERT(res == WAIT_OBJECT_0);
    354         m_waitersBlocked -= m_waitersGone;
    355         res = ReleaseSemaphore(m_blockLock, 1, 0);
    356         ASSERT(res);
    357         m_waitersGone = 0;
    358     }
    359 
    360     res = ReleaseMutex(m_unblockLock);
    361     ASSERT(res);
    362 
    363     if (signalsLeft == 1) {
    364         res = ReleaseSemaphore(m_blockLock, 1, 0); // Open the gate.
    365         ASSERT(res);
    366     }
    367 
    368     EnterCriticalSection (&mutex.m_internalMutex);
    369     ++mutex.m_recursionCount;
    370 
    371     return !timedOut;
    372 }
    373 
    374 void PlatformCondition::signal(bool unblockAll)
    375 {
    376     unsigned signalsToIssue = 0;
    377 
    378     DWORD res = WaitForSingleObject(m_unblockLock, INFINITE);
    379     ASSERT(res == WAIT_OBJECT_0);
    380 
    381     if (m_waitersToUnblock) { // the gate is already closed
    382         if (!m_waitersBlocked) { // no-op
    383             res = ReleaseMutex(m_unblockLock);
    384             ASSERT(res);
    385             return;
    386         }
    387 
    388         if (unblockAll) {
    389             signalsToIssue = m_waitersBlocked;
    390             m_waitersToUnblock += m_waitersBlocked;
    391             m_waitersBlocked = 0;
    392         } else {
    393             signalsToIssue = 1;
    394             ++m_waitersToUnblock;
    395             --m_waitersBlocked;
    396         }
    397     } else if (m_waitersBlocked > m_waitersGone) {
    398         res = WaitForSingleObject(m_blockLock, INFINITE); // Close the gate.
    399         ASSERT(res == WAIT_OBJECT_0);
    400         if (m_waitersGone != 0) {
    401             m_waitersBlocked -= m_waitersGone;
    402             m_waitersGone = 0;
    403         }
    404         if (unblockAll) {
    405             signalsToIssue = m_waitersBlocked;
    406             m_waitersToUnblock = m_waitersBlocked;
    407             m_waitersBlocked = 0;
    408         } else {
    409             signalsToIssue = 1;
    410             m_waitersToUnblock = 1;
    411             --m_waitersBlocked;
    412         }
    413     } else { // No-op.
    414         res = ReleaseMutex(m_unblockLock);
    415         ASSERT(res);
    416         return;
    417     }
    418 
    419     res = ReleaseMutex(m_unblockLock);
    420     ASSERT(res);
    421 
    422     if (signalsToIssue) {
    423         res = ReleaseSemaphore(m_blockQueue, signalsToIssue, 0);
    424         ASSERT(res);
    425     }
    426 }
    427 
    428 static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1);
    429 
    430 ThreadCondition::ThreadCondition()
    431 {
    432     m_condition.m_waitersGone = 0;
    433     m_condition.m_waitersBlocked = 0;
    434     m_condition.m_waitersToUnblock = 0;
    435     m_condition.m_blockLock = CreateSemaphore(0, 1, 1, 0);
    436     m_condition.m_blockQueue = CreateSemaphore(0, 0, MaxSemaphoreCount, 0);
    437     m_condition.m_unblockLock = CreateMutex(0, 0, 0);
    438 
    439     if (!m_condition.m_blockLock || !m_condition.m_blockQueue || !m_condition.m_unblockLock) {
    440         if (m_condition.m_blockLock)
    441             CloseHandle(m_condition.m_blockLock);
    442         if (m_condition.m_blockQueue)
    443             CloseHandle(m_condition.m_blockQueue);
    444         if (m_condition.m_unblockLock)
    445             CloseHandle(m_condition.m_unblockLock);
    446     }
    447 }
    448 
    449 ThreadCondition::~ThreadCondition()
    450 {
    451     CloseHandle(m_condition.m_blockLock);
    452     CloseHandle(m_condition.m_blockQueue);
    453     CloseHandle(m_condition.m_unblockLock);
    454 }
    455 
    456 void ThreadCondition::wait(Mutex& mutex)
    457 {
    458     m_condition.timedWait(mutex.impl(), INFINITE);
    459 }
    460 
    461 bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime)
    462 {
    463     DWORD interval = absoluteTimeToWaitTimeoutInterval(absoluteTime);
    464 
    465     if (!interval) {
    466         // Consider the wait to have timed out, even if our condition has already been signaled, to
    467         // match the pthreads implementation.
    468         return false;
    469     }
    470 
    471     return m_condition.timedWait(mutex.impl(), interval);
    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 DWORD absoluteTimeToWaitTimeoutInterval(double absoluteTime)
    485 {
    486     double currentTime = WTF::currentTime();
    487 
    488     // Time is in the past - return immediately.
    489     if (absoluteTime < currentTime)
    490         return 0;
    491 
    492     // Time is too far in the future (and would overflow unsigned long) - wait forever.
    493     if (absoluteTime - currentTime > static_cast<double>(INT_MAX) / 1000.0)
    494         return INFINITE;
    495 
    496     return static_cast<DWORD>((absoluteTime - currentTime) * 1000.0);
    497 }
    498 
    499 } // namespace WTF
    500