Home | History | Annotate | Download | only in heap
      1 /*
      2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      3  *  Copyright (C) 2007 Eric Seidel <eric (at) webkit.org>
      4  *  Copyright (C) 2009 Acision BV. All rights reserved.
      5  *
      6  *  This library is free software; you can redistribute it and/or
      7  *  modify it under the terms of the GNU Lesser General Public
      8  *  License as published by the Free Software Foundation; either
      9  *  version 2 of the License, or (at your option) any later version.
     10  *
     11  *  This library is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  *  Lesser General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU Lesser General Public
     17  *  License along with this library; if not, write to the Free Software
     18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "MachineStackMarker.h"
     24 
     25 #include "ConservativeRoots.h"
     26 #include "Heap.h"
     27 #include "JSArray.h"
     28 #include "JSGlobalData.h"
     29 #include <setjmp.h>
     30 #include <stdlib.h>
     31 #include <wtf/StdLibExtras.h>
     32 
     33 #if USE(PTHREAD_BASED_QT) && !defined(WTF_USE_PTHREADS)
     34 #define WTF_USE_PTHREADS 1
     35 #endif
     36 
     37 #if OS(DARWIN)
     38 
     39 #include <mach/mach_init.h>
     40 #include <mach/mach_port.h>
     41 #include <mach/task.h>
     42 #include <mach/thread_act.h>
     43 #include <mach/vm_map.h>
     44 
     45 #elif OS(WINDOWS)
     46 
     47 #include <windows.h>
     48 #include <malloc.h>
     49 
     50 #elif OS(HAIKU)
     51 
     52 #include <OS.h>
     53 
     54 #elif OS(UNIX)
     55 
     56 #include <stdlib.h>
     57 #if !OS(HAIKU)
     58 #include <sys/mman.h>
     59 #endif
     60 #include <unistd.h>
     61 
     62 #if OS(SOLARIS)
     63 #include <thread.h>
     64 #else
     65 #include <pthread.h>
     66 #endif
     67 
     68 #if HAVE(PTHREAD_NP_H)
     69 #include <pthread_np.h>
     70 #endif
     71 
     72 #if OS(QNX)
     73 #include <fcntl.h>
     74 #include <sys/procfs.h>
     75 #include <stdio.h>
     76 #include <errno.h>
     77 #endif
     78 
     79 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
     80 #include <signal.h>
     81 #ifndef SA_RESTART
     82 #error MachineThreads requires SA_RESTART
     83 #endif
     84 #endif
     85 
     86 #endif
     87 
     88 using namespace WTF;
     89 
     90 namespace JSC {
     91 
     92 static inline void swapIfBackwards(void*& begin, void*& end)
     93 {
     94 #if OS(WINCE)
     95     if (begin <= end)
     96         return;
     97     std::swap(begin, end);
     98 #else
     99 UNUSED_PARAM(begin);
    100 UNUSED_PARAM(end);
    101 #endif
    102 }
    103 
    104 #if ENABLE(JSC_MULTIPLE_THREADS)
    105 
    106 #if OS(DARWIN)
    107 typedef mach_port_t PlatformThread;
    108 #elif OS(WINDOWS)
    109 typedef HANDLE PlatformThread;
    110 #elif USE(PTHREADS)
    111 typedef pthread_t PlatformThread;
    112 static const int SigThreadSuspendResume = SIGUSR2;
    113 
    114 static void pthreadSignalHandlerSuspendResume(int signo)
    115 {
    116     sigset_t signalSet;
    117     sigemptyset(&signalSet);
    118     sigaddset(&signalSet, SigThreadSuspendResume);
    119     sigsuspend(&signalSet);
    120 }
    121 #endif
    122 
    123 class MachineThreads::Thread {
    124 public:
    125     Thread(pthread_t pthread, const PlatformThread& platThread, void* base)
    126         : posixThread(pthread)
    127         , platformThread(platThread)
    128         , stackBase(base)
    129     {
    130 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
    131         struct sigaction action;
    132         action.sa_handler = pthreadSignalHandlerSuspendResume;
    133         sigemptyset(&action.sa_mask);
    134         action.sa_flags = SA_RESTART;
    135         sigaction(SigThreadSuspendResume, &action, 0);
    136 
    137         sigset_t mask;
    138         sigemptyset(&mask);
    139         sigaddset(&mask, SigThreadSuspendResume);
    140         pthread_sigmask(SIG_UNBLOCK, &mask, 0);
    141 #endif
    142     }
    143 
    144     Thread* next;
    145     pthread_t posixThread;
    146     PlatformThread platformThread;
    147     void* stackBase;
    148 };
    149 
    150 #endif
    151 
    152 MachineThreads::MachineThreads(Heap* heap)
    153     : m_heap(heap)
    154 #if ENABLE(JSC_MULTIPLE_THREADS)
    155     , m_registeredThreads(0)
    156     , m_threadSpecific(0)
    157 #endif
    158 {
    159 }
    160 
    161 MachineThreads::~MachineThreads()
    162 {
    163 #if ENABLE(JSC_MULTIPLE_THREADS)
    164     if (m_threadSpecific) {
    165         int error = pthread_key_delete(m_threadSpecific);
    166         ASSERT_UNUSED(error, !error);
    167     }
    168 
    169     MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
    170     for (Thread* t = m_registeredThreads; t;) {
    171         Thread* next = t->next;
    172         delete t;
    173         t = next;
    174     }
    175 #endif
    176 }
    177 
    178 #if ENABLE(JSC_MULTIPLE_THREADS)
    179 
    180 static inline PlatformThread getCurrentPlatformThread()
    181 {
    182 #if OS(DARWIN)
    183     return pthread_mach_thread_np(pthread_self());
    184 #elif OS(WINDOWS)
    185     return pthread_getw32threadhandle_np(pthread_self());
    186 #elif USE(PTHREADS)
    187     return pthread_self();
    188 #endif
    189 }
    190 
    191 void MachineThreads::makeUsableFromMultipleThreads()
    192 {
    193     if (m_threadSpecific)
    194         return;
    195 
    196     int error = pthread_key_create(&m_threadSpecific, removeThread);
    197     if (error)
    198         CRASH();
    199 }
    200 
    201 void MachineThreads::addCurrentThread()
    202 {
    203     ASSERT(!m_heap->globalData()->exclusiveThread || m_heap->globalData()->exclusiveThread == currentThread());
    204 
    205     if (!m_threadSpecific || pthread_getspecific(m_threadSpecific))
    206         return;
    207 
    208     pthread_setspecific(m_threadSpecific, this);
    209     Thread* thread = new Thread(pthread_self(), getCurrentPlatformThread(), m_heap->globalData()->stack().origin());
    210 
    211     MutexLocker lock(m_registeredThreadsMutex);
    212 
    213     thread->next = m_registeredThreads;
    214     m_registeredThreads = thread;
    215 }
    216 
    217 void MachineThreads::removeThread(void* p)
    218 {
    219     if (p)
    220         static_cast<MachineThreads*>(p)->removeCurrentThread();
    221 }
    222 
    223 void MachineThreads::removeCurrentThread()
    224 {
    225     pthread_t currentPosixThread = pthread_self();
    226 
    227     MutexLocker lock(m_registeredThreadsMutex);
    228 
    229     if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) {
    230         Thread* t = m_registeredThreads;
    231         m_registeredThreads = m_registeredThreads->next;
    232         delete t;
    233     } else {
    234         Thread* last = m_registeredThreads;
    235         Thread* t;
    236         for (t = m_registeredThreads->next; t; t = t->next) {
    237             if (pthread_equal(t->posixThread, currentPosixThread)) {
    238                 last->next = t->next;
    239                 break;
    240             }
    241             last = t;
    242         }
    243         ASSERT(t); // If t is NULL, we never found ourselves in the list.
    244         delete t;
    245     }
    246 }
    247 
    248 #endif
    249 
    250 #if COMPILER(GCC)
    251 #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
    252 #else
    253 #define REGISTER_BUFFER_ALIGNMENT
    254 #endif
    255 
    256 void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, void* stackCurrent)
    257 {
    258     // setjmp forces volatile registers onto the stack
    259     jmp_buf registers REGISTER_BUFFER_ALIGNMENT;
    260 #if COMPILER(MSVC)
    261 #pragma warning(push)
    262 #pragma warning(disable: 4611)
    263 #endif
    264     setjmp(registers);
    265 #if COMPILER(MSVC)
    266 #pragma warning(pop)
    267 #endif
    268 
    269     void* registersBegin = &registers;
    270     void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&registers + 1)));
    271     swapIfBackwards(registersBegin, registersEnd);
    272     conservativeRoots.add(registersBegin, registersEnd);
    273 
    274     void* stackBegin = stackCurrent;
    275     void* stackEnd = m_heap->globalData()->stack().origin();
    276     swapIfBackwards(stackBegin, stackEnd);
    277     conservativeRoots.add(stackBegin, stackEnd);
    278 }
    279 
    280 #if ENABLE(JSC_MULTIPLE_THREADS)
    281 
    282 static inline void suspendThread(const PlatformThread& platformThread)
    283 {
    284 #if OS(DARWIN)
    285     thread_suspend(platformThread);
    286 #elif OS(WINDOWS)
    287     SuspendThread(platformThread);
    288 #elif USE(PTHREADS)
    289     pthread_kill(platformThread, SigThreadSuspendResume);
    290 #else
    291 #error Need a way to suspend threads on this platform
    292 #endif
    293 }
    294 
    295 static inline void resumeThread(const PlatformThread& platformThread)
    296 {
    297 #if OS(DARWIN)
    298     thread_resume(platformThread);
    299 #elif OS(WINDOWS)
    300     ResumeThread(platformThread);
    301 #elif USE(PTHREADS)
    302     pthread_kill(platformThread, SigThreadSuspendResume);
    303 #else
    304 #error Need a way to resume threads on this platform
    305 #endif
    306 }
    307 
    308 typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
    309 
    310 #if OS(DARWIN)
    311 
    312 #if CPU(X86)
    313 typedef i386_thread_state_t PlatformThreadRegisters;
    314 #elif CPU(X86_64)
    315 typedef x86_thread_state64_t PlatformThreadRegisters;
    316 #elif CPU(PPC)
    317 typedef ppc_thread_state_t PlatformThreadRegisters;
    318 #elif CPU(PPC64)
    319 typedef ppc_thread_state64_t PlatformThreadRegisters;
    320 #elif CPU(ARM)
    321 typedef arm_thread_state_t PlatformThreadRegisters;
    322 #else
    323 #error Unknown Architecture
    324 #endif
    325 
    326 #elif OS(WINDOWS) && CPU(X86)
    327 typedef CONTEXT PlatformThreadRegisters;
    328 #elif USE(PTHREADS)
    329 typedef pthread_attr_t PlatformThreadRegisters;
    330 #else
    331 #error Need a thread register struct for this platform
    332 #endif
    333 
    334 static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
    335 {
    336 #if OS(DARWIN)
    337 
    338 #if CPU(X86)
    339     unsigned user_count = sizeof(regs)/sizeof(int);
    340     thread_state_flavor_t flavor = i386_THREAD_STATE;
    341 #elif CPU(X86_64)
    342     unsigned user_count = x86_THREAD_STATE64_COUNT;
    343     thread_state_flavor_t flavor = x86_THREAD_STATE64;
    344 #elif CPU(PPC)
    345     unsigned user_count = PPC_THREAD_STATE_COUNT;
    346     thread_state_flavor_t flavor = PPC_THREAD_STATE;
    347 #elif CPU(PPC64)
    348     unsigned user_count = PPC_THREAD_STATE64_COUNT;
    349     thread_state_flavor_t flavor = PPC_THREAD_STATE64;
    350 #elif CPU(ARM)
    351     unsigned user_count = ARM_THREAD_STATE_COUNT;
    352     thread_state_flavor_t flavor = ARM_THREAD_STATE;
    353 #else
    354 #error Unknown Architecture
    355 #endif
    356 
    357     kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)&regs, &user_count);
    358     if (result != KERN_SUCCESS) {
    359         WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
    360                             "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);
    361         CRASH();
    362     }
    363     return user_count * sizeof(usword_t);
    364 // end OS(DARWIN)
    365 
    366 #elif OS(WINDOWS) && CPU(X86)
    367     regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS;
    368     GetThreadContext(platformThread, &regs);
    369     return sizeof(CONTEXT);
    370 #elif USE(PTHREADS)
    371     pthread_attr_init(&regs);
    372 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
    373     // e.g. on FreeBSD 5.4, neundorf (at) kde.org
    374     pthread_attr_get_np(platformThread, &regs);
    375 #else
    376     // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
    377     pthread_getattr_np(platformThread, &regs);
    378 #endif
    379     return 0;
    380 #else
    381 #error Need a way to get thread registers on this platform
    382 #endif
    383 }
    384 
    385 static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
    386 {
    387 #if OS(DARWIN)
    388 
    389 #if __DARWIN_UNIX03
    390 
    391 #if CPU(X86)
    392     return reinterpret_cast<void*>(regs.__esp);
    393 #elif CPU(X86_64)
    394     return reinterpret_cast<void*>(regs.__rsp);
    395 #elif CPU(PPC) || CPU(PPC64)
    396     return reinterpret_cast<void*>(regs.__r1);
    397 #elif CPU(ARM)
    398     return reinterpret_cast<void*>(regs.__sp);
    399 #else
    400 #error Unknown Architecture
    401 #endif
    402 
    403 #else // !__DARWIN_UNIX03
    404 
    405 #if CPU(X86)
    406     return reinterpret_cast<void*>(regs.esp);
    407 #elif CPU(X86_64)
    408     return reinterpret_cast<void*>(regs.rsp);
    409 #elif CPU(PPC) || CPU(PPC64)
    410     return reinterpret_cast<void*>(regs.r1);
    411 #else
    412 #error Unknown Architecture
    413 #endif
    414 
    415 #endif // __DARWIN_UNIX03
    416 
    417 // end OS(DARWIN)
    418 #elif CPU(X86) && OS(WINDOWS)
    419     return reinterpret_cast<void*>((uintptr_t) regs.Esp);
    420 #elif USE(PTHREADS)
    421     void* stackBase = 0;
    422     size_t stackSize = 0;
    423     int rc = pthread_attr_getstack(&regs, &stackBase, &stackSize);
    424     (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
    425     ASSERT(stackBase);
    426     return static_cast<char*>(stackBase) + stackSize;
    427 #else
    428 #error Need a way to get the stack pointer for another thread on this platform
    429 #endif
    430 }
    431 
    432 static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
    433 {
    434 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
    435     pthread_attr_destroy(&regs);
    436 #else
    437     UNUSED_PARAM(regs);
    438 #endif
    439 }
    440 
    441 void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread)
    442 {
    443     suspendThread(thread->platformThread);
    444 
    445     PlatformThreadRegisters regs;
    446     size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
    447 
    448     conservativeRoots.add(static_cast<void*>(&regs), static_cast<void*>(reinterpret_cast<char*>(&regs) + regSize));
    449 
    450     void* stackPointer = otherThreadStackPointer(regs);
    451     void* stackBase = thread->stackBase;
    452     swapIfBackwards(stackPointer, stackBase);
    453     conservativeRoots.add(stackPointer, stackBase);
    454 
    455     resumeThread(thread->platformThread);
    456 
    457     freePlatformThreadRegisters(regs);
    458 }
    459 
    460 #endif
    461 
    462 void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent)
    463 {
    464     gatherFromCurrentThread(conservativeRoots, stackCurrent);
    465 
    466 #if ENABLE(JSC_MULTIPLE_THREADS)
    467 
    468     if (m_threadSpecific) {
    469 
    470         MutexLocker lock(m_registeredThreadsMutex);
    471 
    472 #ifndef NDEBUG
    473         // Forbid malloc during the gather phase. The gather phase suspends
    474         // threads, so a malloc during gather would risk a deadlock with a
    475         // thread that had been suspended while holding the malloc lock.
    476         fastMallocForbid();
    477 #endif
    478         // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
    479         // and since this is a shared heap, they are real locks.
    480         for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
    481             if (!pthread_equal(thread->posixThread, pthread_self()))
    482                 gatherFromOtherThread(conservativeRoots, thread);
    483         }
    484 #ifndef NDEBUG
    485         fastMallocAllow();
    486 #endif
    487     }
    488 #endif
    489 }
    490 
    491 } // namespace JSC
    492