root/Source/wtf/ThreadingPthreads.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. m_pthreadHandle
  2. joinableState
  3. pthreadHandle
  4. didBecomeDetached
  5. didExit
  6. didJoin
  7. hasExited
  8. threadMapMutex
  9. initializeThreading
  10. lockAtomicallyInitializedStaticMutex
  11. unlockAtomicallyInitializedStaticMutex
  12. threadMap
  13. identifierByPthreadHandle
  14. establishIdentifierForPthreadHandle
  15. pthreadHandleForIdentifierWithLockAlreadyHeld
  16. wtfThreadEntryPoint
  17. createThreadInternal
  18. initializeCurrentThreadInternal
  19. waitForThreadCompletion
  20. detachThread
  21. threadDidExit
  22. yield
  23. currentThread
  24. lock
  25. tryLock
  26. unlock
  27. wait
  28. timedWait
  29. signal
  30. broadcast

/*
 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
 * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
 * Copyright (C) 2011 Research In Motion Limited. 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.
 */

#include "config.h"
#include "wtf/Threading.h"

#if USE(PTHREADS)

#include "wtf/DateMath.h"
#include "wtf/HashMap.h"
#include "wtf/OwnPtr.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/StdLibExtras.h"
#include "wtf/ThreadFunctionInvocation.h"
#include "wtf/ThreadIdentifierDataPthreads.h"
#include "wtf/ThreadSpecific.h"
#include "wtf/ThreadingPrimitives.h"
#include "wtf/WTFThreadData.h"
#include "wtf/dtoa.h"
#include "wtf/dtoa/cached-powers.h"
#include <errno.h>

#if !COMPILER(MSVC)
#include <limits.h>
#include <sched.h>
#include <sys/time.h>
#endif

#if OS(MACOSX)
#include <objc/objc-auto.h>
#endif

namespace WTF {

class PthreadState {
    WTF_MAKE_FAST_ALLOCATED;
public:
    enum JoinableState {
        Joinable, // The default thread state. The thread can be joined on.

        Joined, // Somebody waited on this thread to exit and this thread finally exited. This state is here because there can be a
                // period of time between when the thread exits (which causes pthread_join to return and the remainder of waitOnThreadCompletion to run)
                // and when threadDidExit is called. We need threadDidExit to take charge and delete the thread data since there's
                // nobody else to pick up the slack in this case (since waitOnThreadCompletion has already returned).

        Detached // The thread has been detached and can no longer be joined on. At this point, the thread must take care of cleaning up after itself.
    };

    // Currently all threads created by WTF start out as joinable.
    PthreadState(pthread_t handle)
        : m_joinableState(Joinable)
        , m_didExit(false)
        , m_pthreadHandle(handle)
    {
    }

    JoinableState joinableState() { return m_joinableState; }
    pthread_t pthreadHandle() { return m_pthreadHandle; }
    void didBecomeDetached() { m_joinableState = Detached; }
    void didExit() { m_didExit = true; }
    void didJoin() { m_joinableState = Joined; }
    bool hasExited() { return m_didExit; }

private:
    JoinableState m_joinableState;
    bool m_didExit;
    pthread_t m_pthreadHandle;
};

typedef HashMap<ThreadIdentifier, OwnPtr<PthreadState> > ThreadMap;

static Mutex* atomicallyInitializedStaticMutex;

void unsafeThreadWasDetached(ThreadIdentifier);
void threadDidExit(ThreadIdentifier);
void threadWasJoined(ThreadIdentifier);

static Mutex& threadMapMutex()
{
    DEFINE_STATIC_LOCAL(Mutex, mutex, ());
    return mutex;
}

void initializeThreading()
{
    // This should only be called once.
    ASSERT(!atomicallyInitializedStaticMutex);

    // StringImpl::empty() does not construct its static string in a threadsafe fashion,
    // so ensure it has been initialized from here.
    StringImpl::empty();
    atomicallyInitializedStaticMutex = new Mutex;
    threadMapMutex();
    ThreadIdentifierData::initializeOnce();
    wtfThreadData();
    s_dtoaP5Mutex = new Mutex;
    initializeDates();
}

void lockAtomicallyInitializedStaticMutex()
{
    ASSERT(atomicallyInitializedStaticMutex);
    atomicallyInitializedStaticMutex->lock();
}

void unlockAtomicallyInitializedStaticMutex()
{
    atomicallyInitializedStaticMutex->unlock();
}

static ThreadMap& threadMap()
{
    DEFINE_STATIC_LOCAL(ThreadMap, map, ());
    return map;
}

static ThreadIdentifier identifierByPthreadHandle(const pthread_t& pthreadHandle)
{
    MutexLocker locker(threadMapMutex());

    ThreadMap::iterator i = threadMap().begin();
    for (; i != threadMap().end(); ++i) {
        if (pthread_equal(i->value->pthreadHandle(), pthreadHandle) && !i->value->hasExited())
            return i->key;
    }

    return 0;
}

static ThreadIdentifier establishIdentifierForPthreadHandle(const pthread_t& pthreadHandle)
{
    ASSERT(!identifierByPthreadHandle(pthreadHandle));
    MutexLocker locker(threadMapMutex());
    static ThreadIdentifier identifierCount = 1;
    threadMap().add(identifierCount, adoptPtr(new PthreadState(pthreadHandle)));
    return identifierCount++;
}

static pthread_t pthreadHandleForIdentifierWithLockAlreadyHeld(ThreadIdentifier id)
{
    return threadMap().get(id)->pthreadHandle();
}

static void* wtfThreadEntryPoint(void* param)
{
    // Balanced by .leakPtr() in createThreadInternal.
    OwnPtr<ThreadFunctionInvocation> invocation = adoptPtr(static_cast<ThreadFunctionInvocation*>(param));
    invocation->function(invocation->data);
    return 0;
}

ThreadIdentifier createThreadInternal(ThreadFunction entryPoint, void* data, const char*)
{
    OwnPtr<ThreadFunctionInvocation> invocation = adoptPtr(new ThreadFunctionInvocation(entryPoint, data));
    pthread_t threadHandle;
    if (pthread_create(&threadHandle, 0, wtfThreadEntryPoint, invocation.get())) {
        WTF_LOG_ERROR("Failed to create pthread at entry point %p with data %p", wtfThreadEntryPoint, invocation.get());
        return 0;
    }

    // Balanced by adoptPtr() in wtfThreadEntryPoint.
    ThreadFunctionInvocation* ALLOW_UNUSED leakedInvocation = invocation.leakPtr();

    return establishIdentifierForPthreadHandle(threadHandle);
}

void initializeCurrentThreadInternal(const char* threadName)
{
#if HAVE(PTHREAD_SETNAME_NP)
    pthread_setname_np(threadName);
#endif

#if OS(MACOSX)
    // All threads that potentially use APIs above the BSD layer must be registered with the Objective-C
    // garbage collector in case API implementations use garbage-collected memory.
    objc_registerThreadWithCollector();
#endif

    ThreadIdentifier id = identifierByPthreadHandle(pthread_self());
    ASSERT(id);
    ThreadIdentifierData::initialize(id);
}

int waitForThreadCompletion(ThreadIdentifier threadID)
{
    pthread_t pthreadHandle;
    ASSERT(threadID);

    {
        // We don't want to lock across the call to join, since that can block our thread and cause deadlock.
        MutexLocker locker(threadMapMutex());
        pthreadHandle = pthreadHandleForIdentifierWithLockAlreadyHeld(threadID);
        ASSERT(pthreadHandle);
    }

    int joinResult = pthread_join(pthreadHandle, 0);

    if (joinResult == EDEADLK)
        WTF_LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
    else if (joinResult)
        WTF_LOG_ERROR("ThreadIdentifier %u was unable to be joined.\n", threadID);

    MutexLocker locker(threadMapMutex());
    PthreadState* state = threadMap().get(threadID);
    ASSERT(state);
    ASSERT(state->joinableState() == PthreadState::Joinable);

    // The thread has already exited, so clean up after it.
    if (state->hasExited())
        threadMap().remove(threadID);
    // The thread hasn't exited yet, so don't clean anything up. Just signal that we've already joined on it so that it will clean up after itself.
    else
        state->didJoin();

    return joinResult;
}

void detachThread(ThreadIdentifier threadID)
{
    ASSERT(threadID);

    MutexLocker locker(threadMapMutex());
    pthread_t pthreadHandle = pthreadHandleForIdentifierWithLockAlreadyHeld(threadID);
    ASSERT(pthreadHandle);

    int detachResult = pthread_detach(pthreadHandle);
    if (detachResult)
        WTF_LOG_ERROR("ThreadIdentifier %u was unable to be detached\n", threadID);

    PthreadState* state = threadMap().get(threadID);
    ASSERT(state);
    if (state->hasExited())
        threadMap().remove(threadID);
    else
        threadMap().get(threadID)->didBecomeDetached();
}

void threadDidExit(ThreadIdentifier threadID)
{
    MutexLocker locker(threadMapMutex());
    PthreadState* state = threadMap().get(threadID);
    ASSERT(state);

    state->didExit();

    if (state->joinableState() != PthreadState::Joinable)
        threadMap().remove(threadID);
}

void yield()
{
    sched_yield();
}

ThreadIdentifier currentThread()
{
    ThreadIdentifier id = ThreadIdentifierData::identifier();
    if (id)
        return id;

    // Not a WTF-created thread, ThreadIdentifier is not established yet.
    id = establishIdentifierForPthreadHandle(pthread_self());
    ThreadIdentifierData::initialize(id);
    return id;
}

Mutex::Mutex()
{
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);

    int result = pthread_mutex_init(&m_mutex, &attr);
    ASSERT_UNUSED(result, !result);

    pthread_mutexattr_destroy(&attr);
}

Mutex::~Mutex()
{
    int result = pthread_mutex_destroy(&m_mutex);
    ASSERT_UNUSED(result, !result);
}

void Mutex::lock()
{
    int result = pthread_mutex_lock(&m_mutex);
    ASSERT_UNUSED(result, !result);
}

bool Mutex::tryLock()
{
    int result = pthread_mutex_trylock(&m_mutex);

    if (result == 0)
        return true;
    if (result == EBUSY)
        return false;

    ASSERT_NOT_REACHED();
    return false;
}

void Mutex::unlock()
{
    int result = pthread_mutex_unlock(&m_mutex);
    ASSERT_UNUSED(result, !result);
}

ThreadCondition::ThreadCondition()
{
    pthread_cond_init(&m_condition, NULL);
}

ThreadCondition::~ThreadCondition()
{
    pthread_cond_destroy(&m_condition);
}

void ThreadCondition::wait(Mutex& mutex)
{
    int result = pthread_cond_wait(&m_condition, &mutex.impl());
    ASSERT_UNUSED(result, !result);
}

bool ThreadCondition::timedWait(Mutex& mutex, double absoluteTime)
{
    if (absoluteTime < currentTime())
        return false;

    if (absoluteTime > INT_MAX) {
        wait(mutex);
        return true;
    }

    int timeSeconds = static_cast<int>(absoluteTime);
    int timeNanoseconds = static_cast<int>((absoluteTime - timeSeconds) * 1E9);

    timespec targetTime;
    targetTime.tv_sec = timeSeconds;
    targetTime.tv_nsec = timeNanoseconds;

    return pthread_cond_timedwait(&m_condition, &mutex.impl(), &targetTime) == 0;
}

void ThreadCondition::signal()
{
    int result = pthread_cond_signal(&m_condition);
    ASSERT_UNUSED(result, !result);
}

void ThreadCondition::broadcast()
{
    int result = pthread_cond_broadcast(&m_condition);
    ASSERT_UNUSED(result, !result);
}

} // namespace WTF

#endif // USE(PTHREADS)

/* [<][>][^][v][top][bottom][index][help] */