]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - heap/MachineStackMarker.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / heap / MachineStackMarker.cpp
index 73a3db6778d2c4d2553dff142fe3241a1c3b2b81..4ae16a9cad9091ea7e178574ae9061ece62bee65 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003-2009, 2015 Apple Inc. All rights reserved.
  *  Copyright (C) 2007 Eric Seidel <eric@webkit.org>
  *  Copyright (C) 2009 Acision BV. All rights reserved.
  *
 #include "ConservativeRoots.h"
 #include "Heap.h"
 #include "JSArray.h"
-#include "JSGlobalData.h"
+#include "JSCInlines.h"
+#include "VM.h"
 #include <setjmp.h>
 #include <stdlib.h>
 #include <wtf/StdLibExtras.h>
 
-#if USE(PTHREAD_BASED_QT) && !defined(WTF_USE_PTHREADS)
-#define WTF_USE_PTHREADS 1
-#endif
-
 #if OS(DARWIN)
 
 #include <mach/mach_init.h>
 #include <windows.h>
 #include <malloc.h>
 
-#elif OS(HAIKU)
-
-#include <OS.h>
-
 #elif OS(UNIX)
 
-#include <stdlib.h>
-#if !OS(HAIKU)
 #include <sys/mman.h>
-#endif
 #include <unistd.h>
 
 #if OS(SOLARIS)
 #include <pthread_np.h>
 #endif
 
-#if OS(QNX)
-#include <fcntl.h>
-#include <sys/procfs.h>
-#include <stdio.h>
-#include <errno.h>
-#endif
-
 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
 #include <signal.h>
-#ifndef SA_RESTART
-#error MachineThreads requires SA_RESTART
-#endif
 #endif
 
 #endif
@@ -89,29 +69,16 @@ using namespace WTF;
 
 namespace JSC {
 
-static inline void swapIfBackwards(void*& begin, void*& end)
-{
-#if OS(WINCE)
-    if (begin <= end)
-        return;
-    std::swap(begin, end);
-#else
-UNUSED_PARAM(begin);
-UNUSED_PARAM(end);
-#endif
-}
-
-#if ENABLE(JSC_MULTIPLE_THREADS)
-
 #if OS(DARWIN)
 typedef mach_port_t PlatformThread;
 #elif OS(WINDOWS)
-typedef HANDLE PlatformThread;
+typedef DWORD PlatformThread;
 #elif USE(PTHREADS)
 typedef pthread_t PlatformThread;
 static const int SigThreadSuspendResume = SIGUSR2;
 
-static void pthreadSignalHandlerSuspendResume(int signo)
+#if defined(SA_RESTART)
+static void pthreadSignalHandlerSuspendResume(int)
 {
     sigset_t signalSet;
     sigemptyset(&signalSet);
@@ -119,15 +86,86 @@ static void pthreadSignalHandlerSuspendResume(int signo)
     sigsuspend(&signalSet);
 }
 #endif
+#endif
 
-class MachineThreads::Thread {
+class ActiveMachineThreadsManager;
+static ActiveMachineThreadsManager& activeMachineThreadsManager();
+
+class ActiveMachineThreadsManager {
+    WTF_MAKE_NONCOPYABLE(ActiveMachineThreadsManager);
 public:
-    Thread(pthread_t pthread, const PlatformThread& platThread, void* base) 
-        : posixThread(pthread)
-        , platformThread(platThread)
+
+    class Locker {
+    public:
+        Locker(ActiveMachineThreadsManager& manager)
+            : m_locker(manager.m_lock)
+        {
+        }
+
+    private:
+        MutexLocker m_locker;
+    };
+
+    void add(MachineThreads* machineThreads)
+    {
+        MutexLocker managerLock(m_lock);
+        m_set.add(machineThreads);
+    }
+
+    void remove(MachineThreads* machineThreads)
+    {
+        MutexLocker managerLock(m_lock);
+        auto recordedMachineThreads = m_set.take(machineThreads);
+        RELEASE_ASSERT(recordedMachineThreads = machineThreads);
+    }
+
+    bool contains(MachineThreads* machineThreads)
+    {
+        return m_set.contains(machineThreads);
+    }
+
+private:
+    typedef HashSet<MachineThreads*> MachineThreadsSet;
+
+    ActiveMachineThreadsManager() { }
+    
+    Mutex m_lock;
+    MachineThreadsSet m_set;
+
+    friend ActiveMachineThreadsManager& activeMachineThreadsManager();
+};
+
+static ActiveMachineThreadsManager& activeMachineThreadsManager()
+{
+    static std::once_flag initializeManagerOnceFlag;
+    static ActiveMachineThreadsManager* manager = nullptr;
+
+    std::call_once(initializeManagerOnceFlag, [] {
+        manager = new ActiveMachineThreadsManager();
+    });
+    return *manager;
+}
+    
+static inline PlatformThread getCurrentPlatformThread()
+{
+#if OS(DARWIN)
+    return pthread_mach_thread_np(pthread_self());
+#elif OS(WINDOWS)
+    return GetCurrentThreadId();
+#elif USE(PTHREADS)
+    return pthread_self();
+#endif
+}
+
+class MachineThreads::Thread {
+    WTF_MAKE_FAST_ALLOCATED;
+
+    Thread(const PlatformThread& platThread, void* base)
+        : platformThread(platThread)
         , stackBase(base)
     {
-#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
+#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART)
+        // if we have SA_RESTART, enable SIGUSR2 debugging mechanism
         struct sigaction action;
         action.sa_handler = pthreadSignalHandlerSuspendResume;
         sigemptyset(&action.sa_mask);
@@ -138,33 +176,92 @@ public:
         sigemptyset(&mask);
         sigaddset(&mask, SigThreadSuspendResume);
         pthread_sigmask(SIG_UNBLOCK, &mask, 0);
+#elif OS(WINDOWS)
+        ASSERT(platformThread == GetCurrentThreadId());
+        bool isSuccessful =
+            DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
+                &platformThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
+        RELEASE_ASSERT(isSuccessful);
 #endif
     }
 
+public:
+    ~Thread()
+    {
+#if OS(WINDOWS)
+        CloseHandle(platformThreadHandle);
+#endif
+    }
+
+    static Thread* createForCurrentThread()
+    {
+        return new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
+    }
+
+    struct Registers {
+        inline void* stackPointer() const;
+        
+#if OS(DARWIN)
+#if CPU(X86)
+        typedef i386_thread_state_t PlatformRegisters;
+#elif CPU(X86_64)
+        typedef x86_thread_state64_t PlatformRegisters;
+#elif CPU(PPC)
+        typedef ppc_thread_state_t PlatformRegisters;
+#elif CPU(PPC64)
+        typedef ppc_thread_state64_t PlatformRegisters;
+#elif CPU(ARM)
+        typedef arm_thread_state_t PlatformRegisters;
+#elif CPU(ARM64)
+        typedef arm_thread_state64_t PlatformRegisters;
+#else
+#error Unknown Architecture
+#endif
+        
+#elif OS(WINDOWS)
+        typedef CONTEXT PlatformRegisters;
+#elif USE(PTHREADS)
+        typedef pthread_attr_t PlatformRegisters;
+#else
+#error Need a thread register struct for this platform
+#endif
+        
+        PlatformRegisters regs;
+    };
+    
+    inline bool operator==(const PlatformThread& other) const;
+    inline bool operator!=(const PlatformThread& other) const { return !(*this == other); }
+
+    inline bool suspend();
+    inline void resume();
+    size_t getRegisters(Registers&);
+    void freeRegisters(Registers&);
+    std::pair<void*, size_t> captureStack(void* stackTop);
+
     Thread* next;
-    pthread_t posixThread;
     PlatformThread platformThread;
     void* stackBase;
-};
-
+#if OS(WINDOWS)
+    HANDLE platformThreadHandle;
 #endif
+};
 
 MachineThreads::MachineThreads(Heap* heap)
-    : m_heap(heap)
-#if ENABLE(JSC_MULTIPLE_THREADS)
-    , m_registeredThreads(0)
+    : m_registeredThreads(0)
     , m_threadSpecific(0)
+#if !ASSERT_DISABLED
+    , m_heap(heap)
 #endif
 {
+    UNUSED_PARAM(heap);
+    threadSpecificKeyCreate(&m_threadSpecific, removeThread);
+    activeMachineThreadsManager().add(this);
 }
 
 MachineThreads::~MachineThreads()
 {
-#if ENABLE(JSC_MULTIPLE_THREADS)
-    if (m_threadSpecific) {
-        int error = pthread_key_delete(m_threadSpecific);
-        ASSERT_UNUSED(error, !error);
-    }
+    activeMachineThreadsManager().remove(this);
+    threadSpecificKeyDelete(m_threadSpecific);
 
     MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
     for (Thread* t = m_registeredThreads; t;) {
@@ -172,41 +269,30 @@ MachineThreads::~MachineThreads()
         delete t;
         t = next;
     }
-#endif
 }
 
-#if ENABLE(JSC_MULTIPLE_THREADS)
-
-static inline PlatformThread getCurrentPlatformThread()
+inline bool MachineThreads::Thread::operator==(const PlatformThread& other) const
 {
-#if OS(DARWIN)
-    return pthread_mach_thread_np(pthread_self());
-#elif OS(WINDOWS)
-    return pthread_getw32threadhandle_np(pthread_self());
+#if OS(DARWIN) || OS(WINDOWS)
+    return platformThread == other;
 #elif USE(PTHREADS)
-    return pthread_self();
+    return !!pthread_equal(platformThread, other);
+#else
+#error Need a way to compare threads on this platform
 #endif
 }
 
-void MachineThreads::makeUsableFromMultipleThreads()
-{
-    if (m_threadSpecific)
-        return;
-
-    int error = pthread_key_create(&m_threadSpecific, removeThread);
-    if (error)
-        CRASH();
-}
-
 void MachineThreads::addCurrentThread()
 {
-    ASSERT(!m_heap->globalData()->exclusiveThread || m_heap->globalData()->exclusiveThread == currentThread());
+    ASSERT(!m_heap->vm()->hasExclusiveThread() || m_heap->vm()->exclusiveThread() == std::this_thread::get_id());
 
-    if (!m_threadSpecific || pthread_getspecific(m_threadSpecific))
+    if (threadSpecificGet(m_threadSpecific)) {
+        ASSERT(threadSpecificGet(m_threadSpecific) == this);
         return;
+    }
 
-    pthread_setspecific(m_threadSpecific, this);
-    Thread* thread = new Thread(pthread_self(), getCurrentPlatformThread(), m_heap->globalData()->stack().origin());
+    threadSpecificSet(m_threadSpecific, this);
+    Thread* thread = Thread::createForCurrentThread();
 
     MutexLocker lock(m_registeredThreadsMutex);
 
@@ -216,88 +302,72 @@ void MachineThreads::addCurrentThread()
 
 void MachineThreads::removeThread(void* p)
 {
-    if (p)
-        static_cast<MachineThreads*>(p)->removeCurrentThread();
+    auto& manager = activeMachineThreadsManager();
+    ActiveMachineThreadsManager::Locker lock(manager);
+    auto machineThreads = static_cast<MachineThreads*>(p);
+    if (manager.contains(machineThreads)) {
+        // There's a chance that the MachineThreads registry that this thread
+        // was registered with was already destructed, and another one happened
+        // to be instantiated at the same address. Hence, this thread may or
+        // may not be found in this MachineThreads registry. We only need to
+        // do a removal if this thread is found in it.
+        machineThreads->removeThreadIfFound(getCurrentPlatformThread());
+    }
 }
 
-void MachineThreads::removeCurrentThread()
+template<typename PlatformThread>
+void MachineThreads::removeThreadIfFound(PlatformThread platformThread)
 {
-    pthread_t currentPosixThread = pthread_self();
-
     MutexLocker lock(m_registeredThreadsMutex);
-
-    if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) {
-        Thread* t = m_registeredThreads;
+    Thread* t = m_registeredThreads;
+    if (*t == platformThread) {
         m_registeredThreads = m_registeredThreads->next;
         delete t;
     } else {
         Thread* last = m_registeredThreads;
-        Thread* t;
         for (t = m_registeredThreads->next; t; t = t->next) {
-            if (pthread_equal(t->posixThread, currentPosixThread)) {
+            if (*t == platformThread) {
                 last->next = t->next;
                 break;
             }
             last = t;
         }
-        ASSERT(t); // If t is NULL, we never found ourselves in the list.
         delete t;
     }
 }
-
-#endif
-
-#if COMPILER(GCC)
-#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
-#else
-#define REGISTER_BUFFER_ALIGNMENT
-#endif
-
-void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, void* stackCurrent)
+    
+void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackOrigin, void* stackTop, RegisterState& calleeSavedRegisters)
 {
-    // setjmp forces volatile registers onto the stack
-    jmp_buf registers REGISTER_BUFFER_ALIGNMENT;
-#if COMPILER(MSVC)
-#pragma warning(push)
-#pragma warning(disable: 4611)
-#endif
-    setjmp(registers);
-#if COMPILER(MSVC)
-#pragma warning(pop)
-#endif
+    void* registersBegin = &calleeSavedRegisters;
+    void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&calleeSavedRegisters + 1)));
+    conservativeRoots.add(registersBegin, registersEnd, jitStubRoutines, codeBlocks);
 
-    void* registersBegin = &registers;
-    void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&registers + 1)));
-    swapIfBackwards(registersBegin, registersEnd);
-    conservativeRoots.add(registersBegin, registersEnd);
-
-    void* stackBegin = stackCurrent;
-    void* stackEnd = m_heap->globalData()->stack().origin();
-    swapIfBackwards(stackBegin, stackEnd);
-    conservativeRoots.add(stackBegin, stackEnd);
+    conservativeRoots.add(stackTop, stackOrigin, jitStubRoutines, codeBlocks);
 }
 
-#if ENABLE(JSC_MULTIPLE_THREADS)
-
-static inline void suspendThread(const PlatformThread& platformThread)
+inline bool MachineThreads::Thread::suspend()
 {
 #if OS(DARWIN)
-    thread_suspend(platformThread);
+    kern_return_t result = thread_suspend(platformThread);
+    return result == KERN_SUCCESS;
 #elif OS(WINDOWS)
-    SuspendThread(platformThread);
+    bool threadIsSuspended = (SuspendThread(platformThreadHandle) != (DWORD)-1);
+    ASSERT(threadIsSuspended);
+    return threadIsSuspended;
 #elif USE(PTHREADS)
     pthread_kill(platformThread, SigThreadSuspendResume);
+    return true;
 #else
 #error Need a way to suspend threads on this platform
 #endif
 }
 
-static inline void resumeThread(const PlatformThread& platformThread)
+inline void MachineThreads::Thread::resume()
 {
 #if OS(DARWIN)
     thread_resume(platformThread);
 #elif OS(WINDOWS)
-    ResumeThread(platformThread);
+    ResumeThread(platformThreadHandle);
 #elif USE(PTHREADS)
     pthread_kill(platformThread, SigThreadSuspendResume);
 #else
@@ -305,36 +375,10 @@ static inline void resumeThread(const PlatformThread& platformThread)
 #endif
 }
 
-typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
-
-#if OS(DARWIN)
-
-#if CPU(X86)
-typedef i386_thread_state_t PlatformThreadRegisters;
-#elif CPU(X86_64)
-typedef x86_thread_state64_t PlatformThreadRegisters;
-#elif CPU(PPC)
-typedef ppc_thread_state_t PlatformThreadRegisters;
-#elif CPU(PPC64)
-typedef ppc_thread_state64_t PlatformThreadRegisters;
-#elif CPU(ARM)
-typedef arm_thread_state_t PlatformThreadRegisters;
-#else
-#error Unknown Architecture
-#endif
-
-#elif OS(WINDOWS)
-typedef CONTEXT PlatformThreadRegisters;
-#elif USE(PTHREADS)
-typedef pthread_attr_t PlatformThreadRegisters;
-#else
-#error Need a thread register struct for this platform
-#endif
-
-static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
+size_t MachineThreads::Thread::getRegisters(MachineThreads::Thread::Registers& registers)
 {
+    Thread::Registers::PlatformRegisters& regs = registers.regs;
 #if OS(DARWIN)
-
 #if CPU(X86)
     unsigned user_count = sizeof(regs)/sizeof(int);
     thread_state_flavor_t flavor = i386_THREAD_STATE;
@@ -350,6 +394,9 @@ static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, P
 #elif CPU(ARM)
     unsigned user_count = ARM_THREAD_STATE_COUNT;
     thread_state_flavor_t flavor = ARM_THREAD_STATE;
+#elif CPU(ARM64)
+    unsigned user_count = ARM_THREAD_STATE64_COUNT;
+    thread_state_flavor_t flavor = ARM_THREAD_STATE64;
 #else
 #error Unknown Architecture
 #endif
@@ -360,18 +407,20 @@ static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, P
                             "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result);
         CRASH();
     }
-    return user_count * sizeof(usword_t);
+    return user_count * sizeof(uintptr_t);
 // end OS(DARWIN)
 
 #elif OS(WINDOWS)
-    regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS;
-    GetThreadContext(platformThread, &regs);
+    regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
+    GetThreadContext(platformThreadHandle, &regs);
     return sizeof(CONTEXT);
 #elif USE(PTHREADS)
     pthread_attr_init(&regs);
 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
+#if !OS(OPENBSD)
     // e.g. on FreeBSD 5.4, neundorf@kde.org
     pthread_attr_get_np(platformThread, &regs);
+#endif
 #else
     // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
     pthread_getattr_np(platformThread, &regs);
@@ -382,7 +431,7 @@ static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, P
 #endif
 }
 
-static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
+inline void* MachineThreads::Thread::Registers::stackPointer() const
 {
 #if OS(DARWIN)
 
@@ -396,6 +445,8 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
     return reinterpret_cast<void*>(regs.__r1);
 #elif CPU(ARM)
     return reinterpret_cast<void*>(regs.__sp);
+#elif CPU(ARM64)
+    return reinterpret_cast<void*>(regs.__sp);
 #else
 #error Unknown Architecture
 #endif
@@ -415,14 +466,31 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
 #endif // __DARWIN_UNIX03
 
 // end OS(DARWIN)
-#elif CPU(X86) && OS(WINDOWS)
+#elif OS(WINDOWS)
+
+#if CPU(ARM)
+    return reinterpret_cast<void*>((uintptr_t) regs.Sp);
+#elif CPU(MIPS)
+    return reinterpret_cast<void*>((uintptr_t) regs.IntSp);
+#elif CPU(X86)
     return reinterpret_cast<void*>((uintptr_t) regs.Esp);
-#elif CPU(X86_64) && OS(WINDOWS)
+#elif CPU(X86_64)
     return reinterpret_cast<void*>((uintptr_t) regs.Rsp);
+#else
+#error Unknown Architecture
+#endif
+
 #elif USE(PTHREADS)
     void* stackBase = 0;
     size_t stackSize = 0;
+#if OS(OPENBSD)
+    stack_t ss;
+    int rc = pthread_stackseg_np(pthread_self(), &ss);
+    stackBase = (void*)((size_t) ss.ss_sp - ss.ss_size);
+    stackSize = ss.ss_size;
+#else
     int rc = pthread_attr_getstack(&regs, &stackBase, &stackSize);
+#endif
     (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
     ASSERT(stackBase);
     return static_cast<char*>(stackBase) + stackSize;
@@ -431,8 +499,9 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
 #endif
 }
 
-static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
+void MachineThreads::Thread::freeRegisters(MachineThreads::Thread::Registers& registers)
 {
+    Thread::Registers::PlatformRegisters& regs = registers.regs;
 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
     pthread_attr_destroy(&regs);
 #else
@@ -440,54 +509,164 @@ static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
 #endif
 }
 
-void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread)
+std::pair<void*, size_t> MachineThreads::Thread::captureStack(void* stackTop)
 {
-    suspendThread(thread->platformThread);
+    void* begin = stackBase;
+    void* end = reinterpret_cast<void*>(
+        WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop)));
+    if (begin > end)
+        std::swap(begin, end);
+    return std::make_pair(begin, static_cast<char*>(end) - static_cast<char*>(begin));
+}
 
-    PlatformThreadRegisters regs;
-    size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
+static void copyMemory(void* dst, const void* src, size_t size)
+{
+    size_t dstAsSize = reinterpret_cast<size_t>(dst);
+    size_t srcAsSize = reinterpret_cast<size_t>(src);
+    RELEASE_ASSERT(dstAsSize == WTF::roundUpToMultipleOf<sizeof(intptr_t)>(dstAsSize));
+    RELEASE_ASSERT(srcAsSize == WTF::roundUpToMultipleOf<sizeof(intptr_t)>(srcAsSize));
+    RELEASE_ASSERT(size == WTF::roundUpToMultipleOf<sizeof(intptr_t)>(size));
+
+    intptr_t* dstPtr = reinterpret_cast<intptr_t*>(dst);
+    const intptr_t* srcPtr = reinterpret_cast<const intptr_t*>(src);
+    size /= sizeof(intptr_t);
+    while (size--)
+        *dstPtr++ = *srcPtr++;
+}
+    
+
+
+// This function must not call malloc(), free(), or any other function that might
+// acquire a lock. Since 'thread' is suspended, trying to acquire a lock
+// will deadlock if 'thread' holds that lock.
+// This function, specifically the memory copying, was causing problems with Address Sanitizer in
+// apps. Since we cannot blacklist the system memcpy we must use our own naive implementation,
+// copyMemory, for ASan to work on either instrumented or non-instrumented builds. This is not a
+// significant performance loss as tryCopyOtherThreadStack is only called as part of an O(heapsize)
+// operation. As the heap is generally much larger than the stack the performance hit is minimal.
+// See: https://bugs.webkit.org/show_bug.cgi?id=146297
+void MachineThreads::tryCopyOtherThreadStack(Thread* thread, void* buffer, size_t capacity, size_t* size)
+{
+    Thread::Registers registers;
+    size_t registersSize = thread->getRegisters(registers);
+    std::pair<void*, size_t> stack = thread->captureStack(registers.stackPointer());
 
-    conservativeRoots.add(static_cast<void*>(&regs), static_cast<void*>(reinterpret_cast<char*>(&regs) + regSize));
+    bool canCopy = *size + registersSize + stack.second <= capacity;
 
-    void* stackPointer = otherThreadStackPointer(regs);
-    void* stackBase = thread->stackBase;
-    swapIfBackwards(stackPointer, stackBase);
-    conservativeRoots.add(stackPointer, stackBase);
+    if (canCopy)
+        copyMemory(static_cast<char*>(buffer) + *size, &registers, registersSize);
+    *size += registersSize;
 
-    resumeThread(thread->platformThread);
+    if (canCopy)
+        copyMemory(static_cast<char*>(buffer) + *size, stack.first, stack.second);
+    *size += stack.second;
 
-    freePlatformThreadRegisters(regs);
+    thread->freeRegisters(registers);
 }
 
+bool MachineThreads::tryCopyOtherThreadStacks(MutexLocker&, void* buffer, size_t capacity, size_t* size)
+{
+    // Prevent two VMs from suspending each other's threads at the same time,
+    // which can cause deadlock: <rdar://problem/20300842>.
+    static StaticSpinLock mutex;
+    std::lock_guard<StaticSpinLock> lock(mutex);
+
+    *size = 0;
+
+    PlatformThread currentPlatformThread = getCurrentPlatformThread();
+    int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet.
+    int index = 1;
+    Thread* threadsToBeDeleted = nullptr;
+
+    Thread* previousThread = nullptr;
+    for (Thread* thread = m_registeredThreads; thread; index++) {
+        if (*thread != currentPlatformThread) {
+            bool success = thread->suspend();
+#if OS(DARWIN)
+            if (!success) {
+                if (!numberOfThreads) {
+                    for (Thread* countedThread = m_registeredThreads; countedThread; countedThread = countedThread->next)
+                        numberOfThreads++;
+                }
+                
+                // Re-do the suspension to get the actual failure result for logging.
+                kern_return_t error = thread_suspend(thread->platformThread);
+                ASSERT(error != KERN_SUCCESS);
+
+                WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
+                    "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] platformThread %p.",
+                    error, index, numberOfThreads, thread, reinterpret_cast<void*>(thread->platformThread));
+
+                // Put the invalid thread on the threadsToBeDeleted list.
+                // We can't just delete it here because we have suspended other
+                // threads, and they may still be holding the C heap lock which
+                // we need for deleting the invalid thread. Hence, we need to
+                // defer the deletion till after we have resumed all threads.
+                Thread* nextThread = thread->next;
+                thread->next = threadsToBeDeleted;
+                threadsToBeDeleted = thread;
+
+                if (previousThread)
+                    previousThread->next = nextThread;
+                else
+                    m_registeredThreads = nextThread;
+                thread = nextThread;
+                continue;
+            }
+#else
+            UNUSED_PARAM(numberOfThreads);
+            UNUSED_PARAM(previousThread);
+            ASSERT_UNUSED(success, success);
 #endif
+        }
+        previousThread = thread;
+        thread = thread->next;
+    }
+
+    for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
+        if (*thread != currentPlatformThread)
+            tryCopyOtherThreadStack(thread, buffer, capacity, size);
+    }
+
+    for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
+        if (*thread != currentPlatformThread)
+            thread->resume();
+    }
+
+    for (Thread* thread = threadsToBeDeleted; thread; ) {
+        Thread* nextThread = thread->next;
+        delete thread;
+        thread = nextThread;
+    }
+    
+    return *size <= capacity;
+}
 
-void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent)
+static void growBuffer(size_t size, void** buffer, size_t* capacity)
 {
-    gatherFromCurrentThread(conservativeRoots, stackCurrent);
+    if (*buffer)
+        fastFree(*buffer);
 
-#if ENABLE(JSC_MULTIPLE_THREADS)
+    *capacity = WTF::roundUpToMultipleOf(WTF::pageSize(), size * 2);
+    *buffer = fastMalloc(*capacity);
+}
 
-    if (m_threadSpecific) {
+void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackOrigin, void* stackTop, RegisterState& calleeSavedRegisters)
+{
+    gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackOrigin, stackTop, calleeSavedRegisters);
 
-        MutexLocker lock(m_registeredThreadsMutex);
+    size_t size;
+    size_t capacity = 0;
+    void* buffer = nullptr;
+    MutexLocker lock(m_registeredThreadsMutex);
+    while (!tryCopyOtherThreadStacks(lock, buffer, capacity, &size))
+        growBuffer(size, &buffer, &capacity);
 
-#ifndef NDEBUG
-        // Forbid malloc during the gather phase. The gather phase suspends
-        // threads, so a malloc during gather would risk a deadlock with a
-        // thread that had been suspended while holding the malloc lock.
-        fastMallocForbid();
-#endif
-        // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
-        // and since this is a shared heap, they are real locks.
-        for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
-            if (!pthread_equal(thread->posixThread, pthread_self()))
-                gatherFromOtherThread(conservativeRoots, thread);
-        }
-#ifndef NDEBUG
-        fastMallocAllow();
-#endif
-    }
-#endif
+    if (!buffer)
+        return;
+
+    conservativeRoots.add(buffer, static_cast<char*>(buffer) + size, jitStubRoutines, codeBlocks);
+    fastFree(buffer);
 }
 
 } // namespace JSC