/*
- * 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
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);
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);
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;) {
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);
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 = ®isters;
- void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters + 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
#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;
#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
"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, ®s);
+ regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
+ GetThreadContext(platformThreadHandle, ®s);
return sizeof(CONTEXT);
#elif USE(PTHREADS)
pthread_attr_init(®s);
#if HAVE(PTHREAD_NP_H) || OS(NETBSD)
+#if !OS(OPENBSD)
// e.g. on FreeBSD 5.4, neundorf@kde.org
pthread_attr_get_np(platformThread, ®s);
+#endif
#else
// FIXME: this function is non-portable; other POSIX systems may have different np alternatives
pthread_getattr_np(platformThread, ®s);
#endif
}
-static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
+inline void* MachineThreads::Thread::Registers::stackPointer() const
{
#if OS(DARWIN)
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
#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(®s, &stackBase, &stackSize);
+#endif
(void)rc; // FIXME: Deal with error code somehow? Seems fatal.
ASSERT(stackBase);
return static_cast<char*>(stackBase) + stackSize;
#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(®s);
#else
#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*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + 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, ®isters, 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