X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/b80e619319b1def83d1e8b4f84042b661be1be7f..14957cd040308e3eeec43d26bae5d76da13fcd85:/heap/MachineStackMarker.cpp diff --git a/heap/MachineStackMarker.cpp b/heap/MachineStackMarker.cpp new file mode 100644 index 0000000..73a3db6 --- /dev/null +++ b/heap/MachineStackMarker.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel + * Copyright (C) 2009 Acision BV. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "MachineStackMarker.h" + +#include "ConservativeRoots.h" +#include "Heap.h" +#include "JSArray.h" +#include "JSGlobalData.h" +#include +#include +#include + +#if USE(PTHREAD_BASED_QT) && !defined(WTF_USE_PTHREADS) +#define WTF_USE_PTHREADS 1 +#endif + +#if OS(DARWIN) + +#include +#include +#include +#include +#include + +#elif OS(WINDOWS) + +#include +#include + +#elif OS(HAIKU) + +#include + +#elif OS(UNIX) + +#include +#if !OS(HAIKU) +#include +#endif +#include + +#if OS(SOLARIS) +#include +#else +#include +#endif + +#if HAVE(PTHREAD_NP_H) +#include +#endif + +#if OS(QNX) +#include +#include +#include +#include +#endif + +#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) +#include +#ifndef SA_RESTART +#error MachineThreads requires SA_RESTART +#endif +#endif + +#endif + +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; +#elif USE(PTHREADS) +typedef pthread_t PlatformThread; +static const int SigThreadSuspendResume = SIGUSR2; + +static void pthreadSignalHandlerSuspendResume(int signo) +{ + sigset_t signalSet; + sigemptyset(&signalSet); + sigaddset(&signalSet, SigThreadSuspendResume); + sigsuspend(&signalSet); +} +#endif + +class MachineThreads::Thread { +public: + Thread(pthread_t pthread, const PlatformThread& platThread, void* base) + : posixThread(pthread) + , platformThread(platThread) + , stackBase(base) + { +#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) + struct sigaction action; + action.sa_handler = pthreadSignalHandlerSuspendResume; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_RESTART; + sigaction(SigThreadSuspendResume, &action, 0); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SigThreadSuspendResume); + pthread_sigmask(SIG_UNBLOCK, &mask, 0); +#endif + } + + Thread* next; + pthread_t posixThread; + PlatformThread platformThread; + void* stackBase; +}; + +#endif + +MachineThreads::MachineThreads(Heap* heap) + : m_heap(heap) +#if ENABLE(JSC_MULTIPLE_THREADS) + , m_registeredThreads(0) + , m_threadSpecific(0) +#endif +{ +} + +MachineThreads::~MachineThreads() +{ +#if ENABLE(JSC_MULTIPLE_THREADS) + if (m_threadSpecific) { + int error = pthread_key_delete(m_threadSpecific); + ASSERT_UNUSED(error, !error); + } + + MutexLocker registeredThreadsLock(m_registeredThreadsMutex); + for (Thread* t = m_registeredThreads; t;) { + Thread* next = t->next; + delete t; + t = next; + } +#endif +} + +#if ENABLE(JSC_MULTIPLE_THREADS) + +static inline PlatformThread getCurrentPlatformThread() +{ +#if OS(DARWIN) + return pthread_mach_thread_np(pthread_self()); +#elif OS(WINDOWS) + return pthread_getw32threadhandle_np(pthread_self()); +#elif USE(PTHREADS) + return pthread_self(); +#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()); + + if (!m_threadSpecific || pthread_getspecific(m_threadSpecific)) + return; + + pthread_setspecific(m_threadSpecific, this); + Thread* thread = new Thread(pthread_self(), getCurrentPlatformThread(), m_heap->globalData()->stack().origin()); + + MutexLocker lock(m_registeredThreadsMutex); + + thread->next = m_registeredThreads; + m_registeredThreads = thread; +} + +void MachineThreads::removeThread(void* p) +{ + if (p) + static_cast(p)->removeCurrentThread(); +} + +void MachineThreads::removeCurrentThread() +{ + pthread_t currentPosixThread = pthread_self(); + + MutexLocker lock(m_registeredThreadsMutex); + + if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) { + Thread* t = m_registeredThreads; + 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)) { + 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) +{ + // 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(roundUpToMultipleOf(reinterpret_cast(®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); +} + +#if ENABLE(JSC_MULTIPLE_THREADS) + +static inline void suspendThread(const PlatformThread& platformThread) +{ +#if OS(DARWIN) + thread_suspend(platformThread); +#elif OS(WINDOWS) + SuspendThread(platformThread); +#elif USE(PTHREADS) + pthread_kill(platformThread, SigThreadSuspendResume); +#else +#error Need a way to suspend threads on this platform +#endif +} + +static inline void resumeThread(const PlatformThread& platformThread) +{ +#if OS(DARWIN) + thread_resume(platformThread); +#elif OS(WINDOWS) + ResumeThread(platformThread); +#elif USE(PTHREADS) + pthread_kill(platformThread, SigThreadSuspendResume); +#else +#error Need a way to resume threads on this platform +#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) +{ +#if OS(DARWIN) + +#if CPU(X86) + unsigned user_count = sizeof(regs)/sizeof(int); + thread_state_flavor_t flavor = i386_THREAD_STATE; +#elif CPU(X86_64) + unsigned user_count = x86_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = x86_THREAD_STATE64; +#elif CPU(PPC) + unsigned user_count = PPC_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE; +#elif CPU(PPC64) + unsigned user_count = PPC_THREAD_STATE64_COUNT; + thread_state_flavor_t flavor = PPC_THREAD_STATE64; +#elif CPU(ARM) + unsigned user_count = ARM_THREAD_STATE_COUNT; + thread_state_flavor_t flavor = ARM_THREAD_STATE; +#else +#error Unknown Architecture +#endif + + kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); + if (result != KERN_SUCCESS) { + WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, + "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); +// end OS(DARWIN) + +#elif OS(WINDOWS) + regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; + GetThreadContext(platformThread, ®s); + return sizeof(CONTEXT); +#elif USE(PTHREADS) + pthread_attr_init(®s); +#if HAVE(PTHREAD_NP_H) || OS(NETBSD) + // e.g. on FreeBSD 5.4, neundorf@kde.org + pthread_attr_get_np(platformThread, ®s); +#else + // FIXME: this function is non-portable; other POSIX systems may have different np alternatives + pthread_getattr_np(platformThread, ®s); +#endif + return 0; +#else +#error Need a way to get thread registers on this platform +#endif +} + +static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) +{ +#if OS(DARWIN) + +#if __DARWIN_UNIX03 + +#if CPU(X86) + return reinterpret_cast(regs.__esp); +#elif CPU(X86_64) + return reinterpret_cast(regs.__rsp); +#elif CPU(PPC) || CPU(PPC64) + return reinterpret_cast(regs.__r1); +#elif CPU(ARM) + return reinterpret_cast(regs.__sp); +#else +#error Unknown Architecture +#endif + +#else // !__DARWIN_UNIX03 + +#if CPU(X86) + return reinterpret_cast(regs.esp); +#elif CPU(X86_64) + return reinterpret_cast(regs.rsp); +#elif CPU(PPC) || CPU(PPC64) + return reinterpret_cast(regs.r1); +#else +#error Unknown Architecture +#endif + +#endif // __DARWIN_UNIX03 + +// end OS(DARWIN) +#elif CPU(X86) && OS(WINDOWS) + return reinterpret_cast((uintptr_t) regs.Esp); +#elif CPU(X86_64) && OS(WINDOWS) + return reinterpret_cast((uintptr_t) regs.Rsp); +#elif USE(PTHREADS) + void* stackBase = 0; + size_t stackSize = 0; + int rc = pthread_attr_getstack(®s, &stackBase, &stackSize); + (void)rc; // FIXME: Deal with error code somehow? Seems fatal. + ASSERT(stackBase); + return static_cast(stackBase) + stackSize; +#else +#error Need a way to get the stack pointer for another thread on this platform +#endif +} + +static void freePlatformThreadRegisters(PlatformThreadRegisters& regs) +{ +#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) + pthread_attr_destroy(®s); +#else + UNUSED_PARAM(regs); +#endif +} + +void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread) +{ + suspendThread(thread->platformThread); + + PlatformThreadRegisters regs; + size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); + + conservativeRoots.add(static_cast(®s), static_cast(reinterpret_cast(®s) + regSize)); + + void* stackPointer = otherThreadStackPointer(regs); + void* stackBase = thread->stackBase; + swapIfBackwards(stackPointer, stackBase); + conservativeRoots.add(stackPointer, stackBase); + + resumeThread(thread->platformThread); + + freePlatformThreadRegisters(regs); +} + +#endif + +void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent) +{ + gatherFromCurrentThread(conservativeRoots, stackCurrent); + +#if ENABLE(JSC_MULTIPLE_THREADS) + + if (m_threadSpecific) { + + MutexLocker lock(m_registeredThreadsMutex); + +#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 +} + +} // namespace JSC