diff options
author | tc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-01 22:31:35 +0000 |
---|---|---|
committer | tc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-01 22:31:35 +0000 |
commit | de56f378336660dcc848763c80267a5e063ae47d (patch) | |
tree | 7f551b88923b35bc4022ce6ab3a3f602fb60d91c /webkit/pending/wtf/ThreadingWin.cpp | |
parent | dc4f63c80cb90efe594131030aad6776e5945fcc (diff) | |
download | chromium_src-de56f378336660dcc848763c80267a5e063ae47d.zip chromium_src-de56f378336660dcc848763c80267a5e063ae47d.tar.gz chromium_src-de56f378336660dcc848763c80267a5e063ae47d.tar.bz2 |
Merge the chrome_webkit_merge_branch back on to trunk. This brings us
up to webkit@36102.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2778 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/pending/wtf/ThreadingWin.cpp')
-rw-r--r-- | webkit/pending/wtf/ThreadingWin.cpp | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/webkit/pending/wtf/ThreadingWin.cpp b/webkit/pending/wtf/ThreadingWin.cpp new file mode 100644 index 0000000..d2da60f --- /dev/null +++ b/webkit/pending/wtf/ThreadingWin.cpp @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ============================================================================= + * Note: The implementation of condition variables under the Windows + * plaform was based on that of the excellent BOOST C++ library. It + * has been rewritten to fit in with the WebKit architecture and to + * use its coding conventions. + * ============================================================================= + * + * The Boost license is virtually identical to the Apple variation at the + * top of this file, but is included here for completeness: + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" +#include "Threading.h" + +#include "MainThread.h" +#include <process.h> +#include <windows.h> +#include <wtf/HashMap.h> +#include <wtf/MathExtras.h> + +#if PLATFORM(WIN) && USE(PTHREADS) +// Currently, Apple's Windows port uses a mixture of native and pthreads functions in FastMalloc. +// To ensure that thread-specific data is properly destroyed, we need to end each thread with pthread_exit(). +#include <pthread.h> +#endif + +namespace WTF { + +// MS_VC_EXCEPTION, THREADNAME_INFO, and setThreadName all come from <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>. +static const DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +} THREADNAME_INFO; +#pragma pack(pop) + +static void setThreadName(DWORD dwThreadID, LPCSTR szThreadName) +{ + // Visual Studio has a 31-character limit on thread names. Longer names will + // be truncated silently, but we'd like callers to know about the limit. + ASSERT_ARG(szThreadName, strlen(szThreadName) <= 31); + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info)); + } __except (EXCEPTION_CONTINUE_EXECUTION) { + } +} + +Mutex* atomicallyInitializedStaticMutex; + +static ThreadIdentifier mainThreadIdentifier; + +static Mutex& threadMapMutex() +{ + static Mutex mutex; + return mutex; +} + +void initializeThreading() +{ + if (!atomicallyInitializedStaticMutex) { + atomicallyInitializedStaticMutex = new Mutex; + threadMapMutex(); + wtf_random_init(); + initializeMainThread(); + mainThreadIdentifier = currentThread(); + setThreadName(mainThreadIdentifier, "Main Thread"); + } +} + +static HashMap<DWORD, HANDLE>& threadMap() +{ + static HashMap<DWORD, HANDLE> map; + return map; +} + +static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle) +{ + MutexLocker locker(threadMapMutex()); + threadMap().add(threadID, threadHandle); +} + +static HANDLE threadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + return threadMap().get(id); +} + +static void clearThreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + ASSERT(threadMap().contains(id)); + threadMap().remove(id); +} + +struct ThreadFunctionInvocation { + ThreadFunctionInvocation(ThreadFunction function, void* data) : function(function), data(data) {} + + ThreadFunction function; + void* data; +}; + +static unsigned __stdcall wtfThreadEntryPoint(void* param) +{ + ThreadFunctionInvocation invocation = *static_cast<ThreadFunctionInvocation*>(param); + delete static_cast<ThreadFunctionInvocation*>(param); + + void* result = invocation.function(invocation.data); + +#if PLATFORM(WIN) && USE(PTHREADS) + // pthreads-win32 knows how to work with threads created with Win32 or CRT functions, so it's OK to mix APIs. + pthread_exit(result); +#endif + + return reinterpret_cast<unsigned>(result); +} + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char* threadName) +{ + unsigned threadIdentifier = 0; + ThreadIdentifier threadID = 0; + ThreadFunctionInvocation* invocation = new ThreadFunctionInvocation(entryPoint, data); + HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, wtfThreadEntryPoint, invocation, 0, &threadIdentifier)); + if (!threadHandle) { + LOG_ERROR("Failed to create thread at entry point %p with data %p: %ld", entryPoint, data, errno); + return 0; + } + + if (threadName) + setThreadName(threadIdentifier, threadName); + + threadID = static_cast<ThreadIdentifier>(threadIdentifier); + storeThreadHandleByIdentifier(threadIdentifier, threadHandle); + + return threadID; +} + +// This function is deprecated but needs to be kept around for backward +// compatibility. Use the 3-argument version of createThread above. +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data) +{ + return createThread(entryPoint, data, 0); +} + +int waitForThreadCompletion(ThreadIdentifier threadID, void** result) +{ + ASSERT(threadID); + + HANDLE threadHandle = threadHandleForIdentifier(threadID); + if (!threadHandle) + LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID); + + DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE); + if (joinResult == WAIT_FAILED) + LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID); + + ::CloseHandle(threadHandle); + clearThreadHandleForIdentifier(threadID); + + return joinResult; +} + +void detachThread(ThreadIdentifier threadID) +{ + ASSERT(threadID); + + HANDLE threadHandle = threadHandleForIdentifier(threadID); + if (threadHandle) + ::CloseHandle(threadHandle); + clearThreadHandleForIdentifier(threadID); +} + +ThreadIdentifier currentThread() +{ + return static_cast<ThreadIdentifier>(::GetCurrentThreadId()); +} + +bool isMainThread() +{ + return currentThread() == mainThreadIdentifier; +} + +Mutex::Mutex() +{ + m_mutex.m_recursionCount = 0; + ::InitializeCriticalSection(&m_mutex.m_internalMutex); +} + +Mutex::~Mutex() +{ + ::DeleteCriticalSection(&m_mutex.m_internalMutex); +} + +void Mutex::lock() +{ + ::EnterCriticalSection(&m_mutex.m_internalMutex); + ++m_mutex.m_recursionCount; +} + +bool Mutex::tryLock() +{ + // This method is modeled after the behavior of pthread_mutex_trylock, + // which will return an error if the lock is already owned by the + // current thread. Since the primitive Win32 'TryEnterCriticalSection' + // treats this as a successful case, it changes the behavior of several + // tests in WebKit that check to see if the current thread already + // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord) + DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex); + + if (result != 0) { // We got the lock + // If this thread already had the lock, we must unlock and + // return false so that we mimic the behavior of POSIX's + // pthread_mutex_trylock: + if (m_mutex.m_recursionCount > 0) { + ::LeaveCriticalSection(&m_mutex.m_internalMutex); + return false; + } + + ++m_mutex.m_recursionCount; + return true; + } + + return false; +} + +void Mutex::unlock() +{ + --m_mutex.m_recursionCount; + ::LeaveCriticalSection(&m_mutex.m_internalMutex); +} + +static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1); + +ThreadCondition::ThreadCondition() +{ + m_condition.m_timedOut = 0; + m_condition.m_blocked = 0; + m_condition.m_waitingForRemoval = 0; + m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0); + m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0); + m_condition.m_mutex = ::CreateMutex(0, 0, 0); + + if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) { + if (m_condition.m_gate) + ::CloseHandle(m_condition.m_gate); + if (m_condition.m_queue) + ::CloseHandle(m_condition.m_queue); + if (m_condition.m_mutex) + ::CloseHandle(m_condition.m_mutex); + } +} + +ThreadCondition::~ThreadCondition() +{ + ::CloseHandle(m_condition.m_gate); + ::CloseHandle(m_condition.m_queue); + ::CloseHandle(m_condition.m_mutex); +} + +void ThreadCondition::wait(Mutex& mutex) +{ + PlatformMutex& cs = mutex.impl(); + + // Enter the wait state. + DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + ++m_condition.m_blocked; + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + + ::LeaveCriticalSection(&cs.m_internalMutex); + + res = ::WaitForSingleObject(m_condition.m_queue, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + size_t wasWaiting = m_condition.m_waitingForRemoval; + size_t wasTimedOut = m_condition.m_timedOut; + if (wasWaiting != 0) { + if (--m_condition.m_waitingForRemoval == 0) { + if (m_condition.m_blocked != 0) { + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate + ASSERT(res); + wasWaiting = 0; + } + else if (m_condition.m_timedOut != 0) + m_condition.m_timedOut = 0; + } + } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) { + // timeout occured, normalize the m_condition.m_timedOut count + // this may occur if many calls to wait with a timeout are made and + // no call to notify_* is made + res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + m_condition.m_blocked -= m_condition.m_timedOut; + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + m_condition.m_timedOut = 0; + } + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + + if (wasWaiting == 1) { + for (/**/ ; wasTimedOut; --wasTimedOut) { + // better now than spurious later + res = ::WaitForSingleObject(m_condition.m_queue, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + } + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + } + + ::EnterCriticalSection (&cs.m_internalMutex); +} + +bool ThreadCondition::timedWait(Mutex& mutex, double interval) +{ + // Empty for now + ASSERT(false); + return false; +} + +void ThreadCondition::signal() +{ + unsigned signals = 0; + + DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed + if (m_condition.m_blocked == 0) { + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + return; + } + + ++m_condition.m_waitingForRemoval; + --m_condition.m_blocked; + + signals = 1; + } else { + res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + if (m_condition.m_blocked > m_condition.m_timedOut) { + if (m_condition.m_timedOut != 0) { + m_condition.m_blocked -= m_condition.m_timedOut; + m_condition.m_timedOut = 0; + } + signals = m_condition.m_waitingForRemoval = 1; + --m_condition.m_blocked; + } else { + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + } + } + + res =::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + + if (signals) { + res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0); + ASSERT(res); + } +} + +void ThreadCondition::broadcast() +{ + unsigned signals = 0; + + WORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed + if (m_condition.m_blocked == 0) { + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + return; + } + + m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked); + m_condition.m_blocked = 0; + } else { + res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + if (m_condition.m_blocked > m_condition.m_timedOut) { + if (m_condition.m_timedOut != 0) { + m_condition.m_blocked -= m_condition.m_timedOut; + m_condition.m_timedOut = 0; + } + signals = m_condition.m_waitingForRemoval = m_condition.m_blocked; + m_condition.m_blocked = 0; + } else { + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + } + } + + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + + if (signals) { + res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0); + ASSERT(res); + } +} + +} // namespace WTF |