#include "ConservativeRoots.h"
#include "Heap.h"
#include "JSArray.h"
+#include "JSCInlines.h"
#include "VM.h"
#include <setjmp.h>
#include <stdlib.h>
#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>
#endif
void MachineThreads::addCurrentThread()
{
- ASSERT(!m_heap->vm()->exclusiveThread || m_heap->vm()->exclusiveThread == currentThread());
+ ASSERT(!m_heap->vm()->hasExclusiveThread() || m_heap->vm()->exclusiveThread() == std::this_thread::get_id());
if (!m_threadSpecific || threadSpecificGet(m_threadSpecific))
return;
}
}
-#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* stackCurrent, RegisterState& registers)
{
- // 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 = ®isters;
void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters + 1)));
swapIfBackwards(registersBegin, registersEnd);
- conservativeRoots.add(registersBegin, registersEnd);
+ conservativeRoots.add(registersBegin, registersEnd, jitStubRoutines, codeBlocks);
void* stackBegin = stackCurrent;
void* stackEnd = wtfThreadData().stack().origin();
swapIfBackwards(stackBegin, stackEnd);
- conservativeRoots.add(stackBegin, stackEnd);
+ conservativeRoots.add(stackBegin, stackEnd, jitStubRoutines, codeBlocks);
}
-static inline void suspendThread(const PlatformThread& platformThread)
+static inline bool suspendThread(const PlatformThread& platformThread)
{
#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(platformThread) != (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
#elif OS(WINDOWS)
typedef CONTEXT PlatformThreadRegisters;
-#elif OS(QNX)
-typedef struct _debug_thread_info PlatformThreadRegisters;
#elif USE(PTHREADS)
typedef pthread_attr_t PlatformThreadRegisters;
#else
regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
GetThreadContext(platformThread, ®s);
return sizeof(CONTEXT);
-#elif OS(QNX)
- memset(®s, 0, sizeof(regs));
- regs.tid = platformThread;
- // FIXME: If we find this hurts performance, we can consider caching the fd and keeping it open.
- int fd = open("/proc/self/as", O_RDONLY);
- if (fd == -1) {
- LOG_ERROR("Unable to open /proc/self/as (errno: %d)", errno);
- CRASH();
- }
- int rc = devctl(fd, DCMD_PROC_TIDSTATUS, ®s, sizeof(regs), 0);
- if (rc != EOK) {
- LOG_ERROR("devctl(DCMD_PROC_TIDSTATUS) failed (error: %d)", rc);
- CRASH();
- }
- close(fd);
- return sizeof(struct _debug_thread_info);
#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);
#error Unknown Architecture
#endif
-#elif OS(QNX)
- return reinterpret_cast<void*>((uintptr_t) regs.sp);
-
#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;
static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
{
-#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && !OS(QNX)
+#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
pthread_attr_destroy(®s);
#else
UNUSED_PARAM(regs);
#endif
}
-void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread)
+void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks)
{
PlatformThreadRegisters regs;
size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
- conservativeRoots.add(static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize));
+ conservativeRoots.add(static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize), jitStubRoutines, codeBlocks);
void* stackPointer = otherThreadStackPointer(regs);
void* stackBase = thread->stackBase;
swapIfBackwards(stackPointer, stackBase);
- conservativeRoots.add(stackPointer, stackBase);
+ stackPointer = reinterpret_cast<void*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackPointer)));
+ conservativeRoots.add(stackPointer, stackBase, jitStubRoutines, codeBlocks);
freePlatformThreadRegisters(regs);
}
-void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent)
+void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent, RegisterState& registers)
{
- gatherFromCurrentThread(conservativeRoots, stackCurrent);
+ gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackCurrent, registers);
if (m_threadSpecific) {
PlatformThread currentPlatformThread = getCurrentPlatformThread();
MutexLocker lock(m_registeredThreadsMutex);
+ Thread* threadsToBeDeleted = nullptr;
+
#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
- for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
- if (!equalThread(thread->platformThread, currentPlatformThread))
- suspendThread(thread->platformThread);
+ int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet.
+ int index = 1;
+ Thread* previousThread = nullptr;
+ for (Thread* thread = m_registeredThreads; thread; index++) {
+ if (!equalThread(thread->platformThread, currentPlatformThread)) {
+ bool success = suspendThread(thread->platformThread);
+#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);
+ ASSERT_UNUSED(success, success);
+#endif
+ }
+ previousThread = thread;
+ thread = thread->next;
}
// 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 (!equalThread(thread->platformThread, currentPlatformThread))
- gatherFromOtherThread(conservativeRoots, thread);
+ gatherFromOtherThread(conservativeRoots, thread, jitStubRoutines, codeBlocks);
}
for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
#ifndef NDEBUG
fastMallocAllow();
#endif
+ for (Thread* thread = threadsToBeDeleted; thread; ) {
+ Thread* nextThread = thread->next;
+ delete thread;
+ thread = nextThread;
+ }
}
}