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 = ®isters; 270 void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters + 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)®s, &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, ®s); 369 return sizeof(CONTEXT); 370 #elif USE(PTHREADS) 371 pthread_attr_init(®s); 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, ®s); 375 #else 376 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives 377 pthread_getattr_np(platformThread, ®s); 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(®s, &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(®s); 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*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + 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