1/* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Eric Seidel <eric@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 "JSCInlines.h" 29#include "VM.h" 30#include <setjmp.h> 31#include <stdlib.h> 32#include <wtf/StdLibExtras.h> 33 34#if OS(DARWIN) 35 36#include <mach/mach_init.h> 37#include <mach/mach_port.h> 38#include <mach/task.h> 39#include <mach/thread_act.h> 40#include <mach/vm_map.h> 41 42#elif OS(WINDOWS) 43 44#include <windows.h> 45#include <malloc.h> 46 47#elif OS(UNIX) 48 49#include <sys/mman.h> 50#include <unistd.h> 51 52#if OS(SOLARIS) 53#include <thread.h> 54#else 55#include <pthread.h> 56#endif 57 58#if HAVE(PTHREAD_NP_H) 59#include <pthread_np.h> 60#endif 61 62#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) 63#include <signal.h> 64#endif 65 66#endif 67 68using namespace WTF; 69 70namespace JSC { 71 72static inline void swapIfBackwards(void*& begin, void*& end) 73{ 74#if OS(WINCE) 75 if (begin <= end) 76 return; 77 std::swap(begin, end); 78#else 79UNUSED_PARAM(begin); 80UNUSED_PARAM(end); 81#endif 82} 83 84#if OS(DARWIN) 85typedef mach_port_t PlatformThread; 86#elif OS(WINDOWS) 87typedef HANDLE PlatformThread; 88#elif USE(PTHREADS) 89typedef pthread_t PlatformThread; 90static const int SigThreadSuspendResume = SIGUSR2; 91 92#if defined(SA_RESTART) 93static void pthreadSignalHandlerSuspendResume(int) 94{ 95 sigset_t signalSet; 96 sigemptyset(&signalSet); 97 sigaddset(&signalSet, SigThreadSuspendResume); 98 sigsuspend(&signalSet); 99} 100#endif 101#endif 102 103class MachineThreads::Thread { 104 WTF_MAKE_FAST_ALLOCATED; 105public: 106 Thread(const PlatformThread& platThread, void* base) 107 : platformThread(platThread) 108 , stackBase(base) 109 { 110#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART) 111 // if we have SA_RESTART, enable SIGUSR2 debugging mechanism 112 struct sigaction action; 113 action.sa_handler = pthreadSignalHandlerSuspendResume; 114 sigemptyset(&action.sa_mask); 115 action.sa_flags = SA_RESTART; 116 sigaction(SigThreadSuspendResume, &action, 0); 117 118 sigset_t mask; 119 sigemptyset(&mask); 120 sigaddset(&mask, SigThreadSuspendResume); 121 pthread_sigmask(SIG_UNBLOCK, &mask, 0); 122#endif 123 } 124 125 Thread* next; 126 PlatformThread platformThread; 127 void* stackBase; 128}; 129 130MachineThreads::MachineThreads(Heap* heap) 131 : m_registeredThreads(0) 132 , m_threadSpecific(0) 133#if !ASSERT_DISABLED 134 , m_heap(heap) 135#endif 136{ 137 UNUSED_PARAM(heap); 138} 139 140MachineThreads::~MachineThreads() 141{ 142 if (m_threadSpecific) 143 threadSpecificKeyDelete(m_threadSpecific); 144 145 MutexLocker registeredThreadsLock(m_registeredThreadsMutex); 146 for (Thread* t = m_registeredThreads; t;) { 147 Thread* next = t->next; 148 delete t; 149 t = next; 150 } 151} 152 153static inline PlatformThread getCurrentPlatformThread() 154{ 155#if OS(DARWIN) 156 return pthread_mach_thread_np(pthread_self()); 157#elif OS(WINDOWS) 158 return GetCurrentThread(); 159#elif USE(PTHREADS) 160 return pthread_self(); 161#endif 162} 163 164static inline bool equalThread(const PlatformThread& first, const PlatformThread& second) 165{ 166#if OS(DARWIN) || OS(WINDOWS) 167 return first == second; 168#elif USE(PTHREADS) 169 return !!pthread_equal(first, second); 170#else 171#error Need a way to compare threads on this platform 172#endif 173} 174 175void MachineThreads::makeUsableFromMultipleThreads() 176{ 177 if (m_threadSpecific) 178 return; 179 180 threadSpecificKeyCreate(&m_threadSpecific, removeThread); 181} 182 183void MachineThreads::addCurrentThread() 184{ 185 ASSERT(!m_heap->vm()->hasExclusiveThread() || m_heap->vm()->exclusiveThread() == std::this_thread::get_id()); 186 187 if (!m_threadSpecific || threadSpecificGet(m_threadSpecific)) 188 return; 189 190 threadSpecificSet(m_threadSpecific, this); 191 Thread* thread = new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin()); 192 193 MutexLocker lock(m_registeredThreadsMutex); 194 195 thread->next = m_registeredThreads; 196 m_registeredThreads = thread; 197} 198 199void MachineThreads::removeThread(void* p) 200{ 201 if (p) 202 static_cast<MachineThreads*>(p)->removeCurrentThread(); 203} 204 205void MachineThreads::removeCurrentThread() 206{ 207 PlatformThread currentPlatformThread = getCurrentPlatformThread(); 208 209 MutexLocker lock(m_registeredThreadsMutex); 210 211 if (equalThread(currentPlatformThread, m_registeredThreads->platformThread)) { 212 Thread* t = m_registeredThreads; 213 m_registeredThreads = m_registeredThreads->next; 214 delete t; 215 } else { 216 Thread* last = m_registeredThreads; 217 Thread* t; 218 for (t = m_registeredThreads->next; t; t = t->next) { 219 if (equalThread(t->platformThread, currentPlatformThread)) { 220 last->next = t->next; 221 break; 222 } 223 last = t; 224 } 225 ASSERT(t); // If t is NULL, we never found ourselves in the list. 226 delete t; 227 } 228} 229 230#if COMPILER(GCC) 231#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) 232#else 233#define REGISTER_BUFFER_ALIGNMENT 234#endif 235 236void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent) 237{ 238 // setjmp forces volatile registers onto the stack 239 jmp_buf registers REGISTER_BUFFER_ALIGNMENT; 240#if COMPILER(MSVC) 241#pragma warning(push) 242#pragma warning(disable: 4611) 243#endif 244 setjmp(registers); 245#if COMPILER(MSVC) 246#pragma warning(pop) 247#endif 248 249 void* registersBegin = ®isters; 250 void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters + 1))); 251 swapIfBackwards(registersBegin, registersEnd); 252 conservativeRoots.add(registersBegin, registersEnd, jitStubRoutines, codeBlocks); 253 254 void* stackBegin = stackCurrent; 255 void* stackEnd = wtfThreadData().stack().origin(); 256 swapIfBackwards(stackBegin, stackEnd); 257 conservativeRoots.add(stackBegin, stackEnd, jitStubRoutines, codeBlocks); 258} 259 260static inline void suspendThread(const PlatformThread& platformThread) 261{ 262#if OS(DARWIN) 263 thread_suspend(platformThread); 264#elif OS(WINDOWS) 265 SuspendThread(platformThread); 266#elif USE(PTHREADS) 267 pthread_kill(platformThread, SigThreadSuspendResume); 268#else 269#error Need a way to suspend threads on this platform 270#endif 271} 272 273static inline void resumeThread(const PlatformThread& platformThread) 274{ 275#if OS(DARWIN) 276 thread_resume(platformThread); 277#elif OS(WINDOWS) 278 ResumeThread(platformThread); 279#elif USE(PTHREADS) 280 pthread_kill(platformThread, SigThreadSuspendResume); 281#else 282#error Need a way to resume threads on this platform 283#endif 284} 285 286typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit 287 288#if OS(DARWIN) 289 290#if CPU(X86) 291typedef i386_thread_state_t PlatformThreadRegisters; 292#elif CPU(X86_64) 293typedef x86_thread_state64_t PlatformThreadRegisters; 294#elif CPU(PPC) 295typedef ppc_thread_state_t PlatformThreadRegisters; 296#elif CPU(PPC64) 297typedef ppc_thread_state64_t PlatformThreadRegisters; 298#elif CPU(ARM) 299typedef arm_thread_state_t PlatformThreadRegisters; 300#elif CPU(ARM64) 301typedef arm_thread_state64_t PlatformThreadRegisters; 302#else 303#error Unknown Architecture 304#endif 305 306#elif OS(WINDOWS) 307typedef CONTEXT PlatformThreadRegisters; 308#elif USE(PTHREADS) 309typedef pthread_attr_t PlatformThreadRegisters; 310#else 311#error Need a thread register struct for this platform 312#endif 313 314static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs) 315{ 316#if OS(DARWIN) 317 318#if CPU(X86) 319 unsigned user_count = sizeof(regs)/sizeof(int); 320 thread_state_flavor_t flavor = i386_THREAD_STATE; 321#elif CPU(X86_64) 322 unsigned user_count = x86_THREAD_STATE64_COUNT; 323 thread_state_flavor_t flavor = x86_THREAD_STATE64; 324#elif CPU(PPC) 325 unsigned user_count = PPC_THREAD_STATE_COUNT; 326 thread_state_flavor_t flavor = PPC_THREAD_STATE; 327#elif CPU(PPC64) 328 unsigned user_count = PPC_THREAD_STATE64_COUNT; 329 thread_state_flavor_t flavor = PPC_THREAD_STATE64; 330#elif CPU(ARM) 331 unsigned user_count = ARM_THREAD_STATE_COUNT; 332 thread_state_flavor_t flavor = ARM_THREAD_STATE; 333#elif CPU(ARM64) 334 unsigned user_count = ARM_THREAD_STATE64_COUNT; 335 thread_state_flavor_t flavor = ARM_THREAD_STATE64; 336#else 337#error Unknown Architecture 338#endif 339 340 kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count); 341 if (result != KERN_SUCCESS) { 342 WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 343 "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); 344 CRASH(); 345 } 346 return user_count * sizeof(usword_t); 347// end OS(DARWIN) 348 349#elif OS(WINDOWS) 350 regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; 351 GetThreadContext(platformThread, ®s); 352 return sizeof(CONTEXT); 353#elif USE(PTHREADS) 354 pthread_attr_init(®s); 355#if HAVE(PTHREAD_NP_H) || OS(NETBSD) 356#if !OS(OPENBSD) 357 // e.g. on FreeBSD 5.4, neundorf@kde.org 358 pthread_attr_get_np(platformThread, ®s); 359#endif 360#else 361 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives 362 pthread_getattr_np(platformThread, ®s); 363#endif 364 return 0; 365#else 366#error Need a way to get thread registers on this platform 367#endif 368} 369 370static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) 371{ 372#if OS(DARWIN) 373 374#if __DARWIN_UNIX03 375 376#if CPU(X86) 377 return reinterpret_cast<void*>(regs.__esp); 378#elif CPU(X86_64) 379 return reinterpret_cast<void*>(regs.__rsp); 380#elif CPU(PPC) || CPU(PPC64) 381 return reinterpret_cast<void*>(regs.__r1); 382#elif CPU(ARM) 383 return reinterpret_cast<void*>(regs.__sp); 384#elif CPU(ARM64) 385 return reinterpret_cast<void*>(regs.__sp); 386#else 387#error Unknown Architecture 388#endif 389 390#else // !__DARWIN_UNIX03 391 392#if CPU(X86) 393 return reinterpret_cast<void*>(regs.esp); 394#elif CPU(X86_64) 395 return reinterpret_cast<void*>(regs.rsp); 396#elif CPU(PPC) || CPU(PPC64) 397 return reinterpret_cast<void*>(regs.r1); 398#else 399#error Unknown Architecture 400#endif 401 402#endif // __DARWIN_UNIX03 403 404// end OS(DARWIN) 405#elif OS(WINDOWS) 406 407#if CPU(ARM) 408 return reinterpret_cast<void*>((uintptr_t) regs.Sp); 409#elif CPU(MIPS) 410 return reinterpret_cast<void*>((uintptr_t) regs.IntSp); 411#elif CPU(X86) 412 return reinterpret_cast<void*>((uintptr_t) regs.Esp); 413#elif CPU(X86_64) 414 return reinterpret_cast<void*>((uintptr_t) regs.Rsp); 415#else 416#error Unknown Architecture 417#endif 418 419#elif USE(PTHREADS) 420 void* stackBase = 0; 421 size_t stackSize = 0; 422#if OS(OPENBSD) 423 stack_t ss; 424 int rc = pthread_stackseg_np(pthread_self(), &ss); 425 stackBase = (void*)((size_t) ss.ss_sp - ss.ss_size); 426 stackSize = ss.ss_size; 427#else 428 int rc = pthread_attr_getstack(®s, &stackBase, &stackSize); 429#endif 430 (void)rc; // FIXME: Deal with error code somehow? Seems fatal. 431 ASSERT(stackBase); 432 return static_cast<char*>(stackBase) + stackSize; 433#else 434#error Need a way to get the stack pointer for another thread on this platform 435#endif 436} 437 438static void freePlatformThreadRegisters(PlatformThreadRegisters& regs) 439{ 440#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) 441 pthread_attr_destroy(®s); 442#else 443 UNUSED_PARAM(regs); 444#endif 445} 446 447void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks) 448{ 449 PlatformThreadRegisters regs; 450 size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); 451 452 conservativeRoots.add(static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize), jitStubRoutines, codeBlocks); 453 454 void* stackPointer = otherThreadStackPointer(regs); 455 void* stackBase = thread->stackBase; 456 swapIfBackwards(stackPointer, stackBase); 457 stackPointer = reinterpret_cast<void*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackPointer))); 458 conservativeRoots.add(stackPointer, stackBase, jitStubRoutines, codeBlocks); 459 460 freePlatformThreadRegisters(regs); 461} 462 463void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent) 464{ 465 gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackCurrent); 466 467 if (m_threadSpecific) { 468 PlatformThread currentPlatformThread = getCurrentPlatformThread(); 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 for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { 479 if (!equalThread(thread->platformThread, currentPlatformThread)) 480 suspendThread(thread->platformThread); 481 } 482 483 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held, 484 // and since this is a shared heap, they are real locks. 485 for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { 486 if (!equalThread(thread->platformThread, currentPlatformThread)) 487 gatherFromOtherThread(conservativeRoots, thread, jitStubRoutines, codeBlocks); 488 } 489 490 for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { 491 if (!equalThread(thread->platformThread, currentPlatformThread)) 492 resumeThread(thread->platformThread); 493 } 494 495#ifndef NDEBUG 496 fastMallocAllow(); 497#endif 498 } 499} 500 501} // namespace JSC 502