JavaScriptCore/wtf/ThreadingWin.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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@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 #if !USE(PTHREADS) && OS(WINDOWS)
       
    91 #include "ThreadSpecific.h"
       
    92 #endif
       
    93 #if !OS(WINCE)
       
    94 #include <process.h>
       
    95 #endif
       
    96 #if HAVE(ERRNO_H)
       
    97 #include <errno.h>
       
    98 #else
       
    99 #define NO_ERRNO
       
   100 #endif
       
   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 
       
   109 // MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadNameInternal all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>.
       
   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 
       
   121 void initializeCurrentThreadInternal(const char* szThreadName)
       
   122 {
       
   123     THREADNAME_INFO info;
       
   124     info.dwType = 0x1000;
       
   125     info.szName = szThreadName;
       
   126     info.dwThreadID = GetCurrentThreadId();
       
   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 
       
   148 static Mutex& threadMapMutex()
       
   149 {
       
   150     static Mutex mutex;
       
   151     return mutex;
       
   152 }
       
   153 
       
   154 void initializeThreading()
       
   155 {
       
   156     if (atomicallyInitializedStaticMutex)
       
   157         return;
       
   158 
       
   159     atomicallyInitializedStaticMutex = new Mutex;
       
   160     threadMapMutex();
       
   161     initializeRandomNumberGenerator();
       
   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 
       
   204 #if !USE(PTHREADS) && OS(WINDOWS)
       
   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);
       
   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
       
   222     HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier));
       
   223 #endif
       
   224     if (!threadHandle) {
       
   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
       
   230         LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno);
       
   231 #endif
       
   232         return 0;
       
   233     }
       
   234 
       
   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);
       
   262 
       
   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 
       
   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 
       
   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     }
       
   469 
       
   470     double intervalMilliseconds = (absoluteTime - currentTime) * 1000.0;
       
   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