/* * 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) && CPU(X86) 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) && CPU(X86) 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 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