blob: de1bb554f0eead29b5b529dd68deeaf04992102e [file] [log] [blame]
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
* Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
*
* 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.
*/
#include "config.h"
#include "Threading.h"
#include "StdLibExtras.h"
#if USE(PTHREADS)
#include "HashMap.h"
#include "MainThread.h"
#include "MathExtras.h"
#include <errno.h>
#include <sys/time.h>
namespace WTF {
typedef HashMap<ThreadIdentifier, pthread_t> ThreadMap;
static Mutex* atomicallyInitializedStaticMutex;
#if !PLATFORM(DARWIN)
static ThreadIdentifier mainThreadIdentifier; // The thread that was the first to call initializeThreading(), which must be the main thread.
#endif
static Mutex& threadMapMutex()
{
DEFINE_STATIC_LOCAL(Mutex, mutex, ());
return mutex;
}
void initializeThreading()
{
if (!atomicallyInitializedStaticMutex) {
atomicallyInitializedStaticMutex = new Mutex;
threadMapMutex();
wtf_random_init();
#if !PLATFORM(DARWIN)
mainThreadIdentifier = currentThread();
#endif
initializeMainThread();
}
}
void lockAtomicallyInitializedStaticMutex()
{
ASSERT(atomicallyInitializedStaticMutex);
atomicallyInitializedStaticMutex->lock();
}
void unlockAtomicallyInitializedStaticMutex()
{
atomicallyInitializedStaticMutex->unlock();
}
static ThreadMap& threadMap()
{
DEFINE_STATIC_LOCAL(ThreadMap, map, ());
return map;
}
static ThreadIdentifier establishIdentifierForPthreadHandle(pthread_t& pthreadHandle)
{
MutexLocker locker(threadMapMutex());
static ThreadIdentifier identifierCount = 1;
threadMap().add(identifierCount, pthreadHandle);
return identifierCount++;
}
static ThreadIdentifier identifierByPthreadHandle(const pthread_t& pthreadHandle)
{
MutexLocker locker(threadMapMutex());
ThreadMap::iterator i = threadMap().begin();
for (; i != threadMap().end(); ++i) {
if (pthread_equal(i->second, pthreadHandle))
return i->first;
}
return 0;
}
static pthread_t pthreadHandleForIdentifier(ThreadIdentifier id)
{
MutexLocker locker(threadMapMutex());
return threadMap().get(id);
}
static void clearPthreadHandleForIdentifier(ThreadIdentifier id)
{
MutexLocker locker(threadMapMutex());
ASSERT(threadMap().contains(id));
threadMap().remove(id);
}
ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char*)
{
pthread_t threadHandle;
if (pthread_create(&threadHandle, NULL, entryPoint, data)) {
LOG_ERROR("Failed to create pthread at entry point %p with data %p", entryPoint, data);
return 0;
}
ThreadIdentifier threadID = establishIdentifierForPthreadHandle(threadHandle);
return threadID;
}
#if PLATFORM(MAC)
// This function is deprecated but needs to be kept around for backward
// compatibility. Use the 3-argument version of createThread above instead.
ThreadIdentifier createThread(ThreadFunction entryPoint, void* data)
{
return createThread(entryPoint, data, 0);
}
#endif
int waitForThreadCompletion(ThreadIdentifier threadID, void** result)
{
ASSERT(threadID);
pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID);
int joinResult = pthread_join(pthreadHandle, result);
if (joinResult == EDEADLK)
LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
clearPthreadHandleForIdentifier(threadID);
return joinResult;
}
void detachThread(ThreadIdentifier threadID)
{
ASSERT(threadID);
pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID);
pthread_detach(pthreadHandle);
clearPthreadHandleForIdentifier(threadID);
}
ThreadIdentifier currentThread()
{
pthread_t currentThread = pthread_self();
if (ThreadIdentifier id = identifierByPthreadHandle(currentThread))
return id;
return establishIdentifierForPthreadHandle(currentThread);
}
bool isMainThread()
{
#if PLATFORM(DARWIN)
return pthread_main_np();
#else
return currentThread() == mainThreadIdentifier;
#endif
}
Mutex::Mutex()
{
pthread_mutex_init(&m_mutex, NULL);
}
Mutex::~Mutex()
{
pthread_mutex_destroy(&m_mutex);
}
void Mutex::lock()
{
if (pthread_mutex_lock(&m_mutex) != 0)
ASSERT(false);
}
bool Mutex::tryLock()
{
int result = pthread_mutex_trylock(&m_mutex);
if (result == 0)
return true;
else if (result == EBUSY)
return false;
ASSERT(false);
return false;
}
void Mutex::unlock()
{
if (pthread_mutex_unlock(&m_mutex) != 0)
ASSERT(false);
}
ThreadCondition::ThreadCondition()
{
pthread_cond_init(&m_condition, NULL);
}
ThreadCondition::~ThreadCondition()
{
pthread_cond_destroy(&m_condition);
}
void ThreadCondition::wait(Mutex& mutex)
{
if (pthread_cond_wait(&m_condition, &mutex.impl()) != 0)
ASSERT(false);
}
bool ThreadCondition::timedWait(Mutex& mutex, double secondsToWait)
{
if (secondsToWait < 0.0) {
wait(mutex);
return true;
}
int intervalSeconds = static_cast<int>(secondsToWait);
int intervalMicroseconds = static_cast<int>((secondsToWait - intervalSeconds) * 1000000.0);
// Current time comes in sec/microsec
timeval currentTime;
gettimeofday(&currentTime, NULL);
// Target time comes in sec/nanosec
timespec targetTime;
targetTime.tv_sec = currentTime.tv_sec + intervalSeconds;
targetTime.tv_nsec = (currentTime.tv_usec + intervalMicroseconds) * 1000;
if (targetTime.tv_nsec > 1000000000) {
targetTime.tv_nsec -= 1000000000;
targetTime.tv_sec++;
}
return pthread_cond_timedwait(&m_condition, &mutex.impl(), &targetTime) == 0;
}
void ThreadCondition::signal()
{
if (pthread_cond_signal(&m_condition) != 0)
ASSERT(false);
}
void ThreadCondition::broadcast()
{
if (pthread_cond_broadcast(&m_condition) != 0)
ASSERT(false);
}
} // namespace WTF
#endif // USE(PTHREADS)