* wtf/Platform.h: Added ENABLE(COMPUTED_GOTO_OPCODES).
+2012-08-21 Mark Hahnenberg <mhahnenberg@apple.com>
+
+ WTF Threading leaks kernel objects on platforms that use pthreads
+ https://bugs.webkit.org/show_bug.cgi?id=94636
+
+ Reviewed by Geoffrey Garen.
+
+ Creating lots of Web workers on Mac platforms leaks lots of Mach ports. Eventually, the
+ process can exhaust its allocation of Mach ports from the kernel, which can then cause
+ all sorts of badness, including the inability to allocate new virtual memory from the
+ kernel. ThreadingPthreads.cpp and ThreadIdentifierDataPthreads.cpp need to be refactored
+ so that we do not leak these kernel resources. I would assume that we also leak kernel
+ resources on other pthreads platforms as well.
+
+ * wtf/ThreadIdentifierDataPthreads.cpp:
+ (WTF):
+ (WTF::ThreadIdentifierData::~ThreadIdentifierData): Now calls the event threadDidExit, which
+ handles all relevant tear-down of the thread metadata in the thread map.
+ * wtf/ThreadingPthreads.cpp: Added a new class called PthreadState that encapsulates the
+ state of a thread and the possible transitions between those states.
+ (PthreadState):
+ (WTF::PthreadState::PthreadState):
+ (WTF::PthreadState::joinableState): Returns the current state of the pthread.
+ (WTF::PthreadState::pthreadHandle): Returns the pthread_t for this particular thread. This needs to
+ remain valid even after the thread has exited because somebody could come along at any time in the
+ future and call join on the thread.
+ (WTF::PthreadState::didBecomeDetached): Signals that the thread was detached.
+ (WTF::PthreadState::didExit): Signals that the thread's exit destructor (~ThreadIdentifierData) has run.
+ (WTF::PthreadState::didJoin): Signals that the thread has been joined on successfully.
+ (WTF::PthreadState::hasExited): Returns whether or not the thread's exit destructor has run.
+ (WTF):
+ (WTF::identifierByPthreadHandle): Changed to also check hasExited() on the threads it finds in the map. We
+ should only have one valid pthread_t in the map, but there are other pthread_t's that need to remain in the
+ thread map for when somebody joins on that thread id later.
+ (WTF::establishIdentifierForPthreadHandle): Changed to put the allocate the new PthreadState data structure and
+ put it in the map.
+ (WTF::pthreadHandleForIdentifierWithLockAlreadyHeld):
+ (WTF::wtfThreadEntryPoint):
+ (WTF::waitForThreadCompletion): We now do the relevant cleanup after the specified thread has been
+ successfully joined on depending on if the joined thread has already exited.
+ (WTF::detachThread): Performs relevant cleanup if the thread has already exited. Otherwise signals to the
+ PthreadState that the thread has been detached.
+ (WTF::threadDidExit): Function called by ~ThreadIdentifierData to indicate that the thread has exited.
+ Only cleans up after itself if the thread isn't Joinable (i.e. Joined or Detached).
+
2012-07-28 Patrick Gansterer <paroga@webkit.org>
[WIN] Add missing export macro to friend decleration.
namespace WTF {
-typedef HashMap<ThreadIdentifier, pthread_t> ThreadMap;
+class PthreadState {
+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 clearPthreadHandleForIdentifier(ThreadIdentifier);
+void unsafeThreadWasDetached(ThreadIdentifier);
+void threadDidExit(ThreadIdentifier);
+void threadWasJoined(ThreadIdentifier);
static Mutex& threadMapMutex()
{
ThreadMap::iterator i = threadMap().begin();
for (; i != threadMap().end(); ++i) {
- if (pthread_equal(i->second, pthreadHandle))
+ if (pthread_equal(i->second->pthreadHandle(), pthreadHandle) && !i->second->hasExited())
return i->first;
}
static ThreadIdentifier establishIdentifierForPthreadHandle(const pthread_t& pthreadHandle)
{
ASSERT(!identifierByPthreadHandle(pthreadHandle));
-
MutexLocker locker(threadMapMutex());
-
static ThreadIdentifier identifierCount = 1;
-
- threadMap().add(identifierCount, pthreadHandle);
-
+ threadMap().add(identifierCount, adoptPtr(new PthreadState(pthreadHandle)));
return identifierCount++;
}
-static pthread_t pthreadHandleForIdentifier(ThreadIdentifier id)
+static pthread_t pthreadHandleForIdentifierWithLockAlreadyHeld(ThreadIdentifier id)
{
- MutexLocker locker(threadMapMutex());
-
- return threadMap().get(id);
-}
-
-void clearPthreadHandleForIdentifier(ThreadIdentifier id)
-{
- MutexLocker locker(threadMapMutex());
-
- ASSERT(threadMap().contains(id));
-
- threadMap().remove(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;
}
int waitForThreadCompletion(ThreadIdentifier threadID)
{
+ pthread_t pthreadHandle;
ASSERT(threadID);
- pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID);
- if (!pthreadHandle)
- return 0;
+ {
+ // 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)
LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID);
+ else if (joinResult)
+ 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;
}
{
ASSERT(threadID);
- pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID);
- if (!pthreadHandle)
- return;
+ MutexLocker locker(threadMapMutex());
+ pthread_t pthreadHandle = pthreadHandleForIdentifierWithLockAlreadyHeld(threadID);
+ ASSERT(pthreadHandle);
+
+ int detachResult = pthread_detach(pthreadHandle);
+ if (detachResult)
+ 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();
- pthread_detach(pthreadHandle);
+ if (state->joinableState() != PthreadState::Joinable)
+ threadMap().remove(threadID);
}
void yield()