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.
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.
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.
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
23 #include "MachineStackMarker.h"
25 #include "ConservativeRoots.h"
28 #include "JSCInlines.h"
32 #include <wtf/StdLibExtras.h>
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>
58 #if HAVE(PTHREAD_NP_H)
59 #include <pthread_np.h>
62 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
72 static inline void swapIfBackwards(void*& begin
, void*& end
)
77 std::swap(begin
, end
);
85 typedef mach_port_t PlatformThread
;
87 typedef HANDLE PlatformThread
;
89 typedef pthread_t PlatformThread
;
90 static const int SigThreadSuspendResume
= SIGUSR2
;
92 #if defined(SA_RESTART)
93 static void pthreadSignalHandlerSuspendResume(int)
96 sigemptyset(&signalSet
);
97 sigaddset(&signalSet
, SigThreadSuspendResume
);
98 sigsuspend(&signalSet
);
103 class MachineThreads::Thread
{
104 WTF_MAKE_FAST_ALLOCATED
;
106 Thread(const PlatformThread
& platThread
, void* base
)
107 : platformThread(platThread
)
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);
120 sigaddset(&mask
, SigThreadSuspendResume
);
121 pthread_sigmask(SIG_UNBLOCK
, &mask
, 0);
126 PlatformThread platformThread
;
130 MachineThreads::MachineThreads(Heap
* heap
)
131 : m_registeredThreads(0)
132 , m_threadSpecific(0)
140 MachineThreads::~MachineThreads()
142 if (m_threadSpecific
)
143 threadSpecificKeyDelete(m_threadSpecific
);
145 MutexLocker
registeredThreadsLock(m_registeredThreadsMutex
);
146 for (Thread
* t
= m_registeredThreads
; t
;) {
147 Thread
* next
= t
->next
;
153 static inline PlatformThread
getCurrentPlatformThread()
156 return pthread_mach_thread_np(pthread_self());
158 return GetCurrentThread();
160 return pthread_self();
164 static inline bool equalThread(const PlatformThread
& first
, const PlatformThread
& second
)
166 #if OS(DARWIN) || OS(WINDOWS)
167 return first
== second
;
169 return !!pthread_equal(first
, second
);
171 #error Need a way to compare threads on this platform
175 void MachineThreads::makeUsableFromMultipleThreads()
177 if (m_threadSpecific
)
180 threadSpecificKeyCreate(&m_threadSpecific
, removeThread
);
183 void MachineThreads::addCurrentThread()
185 ASSERT(!m_heap
->vm()->hasExclusiveThread() || m_heap
->vm()->exclusiveThread() == std::this_thread::get_id());
187 if (!m_threadSpecific
|| threadSpecificGet(m_threadSpecific
))
190 threadSpecificSet(m_threadSpecific
, this);
191 Thread
* thread
= new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
193 MutexLocker
lock(m_registeredThreadsMutex
);
195 thread
->next
= m_registeredThreads
;
196 m_registeredThreads
= thread
;
199 void MachineThreads::removeThread(void* p
)
202 static_cast<MachineThreads
*>(p
)->removeCurrentThread();
205 void MachineThreads::removeCurrentThread()
207 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
209 MutexLocker
lock(m_registeredThreadsMutex
);
211 if (equalThread(currentPlatformThread
, m_registeredThreads
->platformThread
)) {
212 Thread
* t
= m_registeredThreads
;
213 m_registeredThreads
= m_registeredThreads
->next
;
216 Thread
* last
= m_registeredThreads
;
218 for (t
= m_registeredThreads
->next
; t
; t
= t
->next
) {
219 if (equalThread(t
->platformThread
, currentPlatformThread
)) {
220 last
->next
= t
->next
;
225 ASSERT(t
); // If t is NULL, we never found ourselves in the list.
230 void MachineThreads::gatherFromCurrentThread(ConservativeRoots
& conservativeRoots
, JITStubRoutineSet
& jitStubRoutines
, CodeBlockSet
& codeBlocks
, void* stackCurrent
, RegisterState
& registers
)
232 void* registersBegin
= ®isters
;
233 void* registersEnd
= reinterpret_cast<void*>(roundUpToMultipleOf
<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters
+ 1)));
234 swapIfBackwards(registersBegin
, registersEnd
);
235 conservativeRoots
.add(registersBegin
, registersEnd
, jitStubRoutines
, codeBlocks
);
237 void* stackBegin
= stackCurrent
;
238 void* stackEnd
= wtfThreadData().stack().origin();
239 swapIfBackwards(stackBegin
, stackEnd
);
240 conservativeRoots
.add(stackBegin
, stackEnd
, jitStubRoutines
, codeBlocks
);
243 static inline bool suspendThread(const PlatformThread
& platformThread
)
246 kern_return_t result
= thread_suspend(platformThread
);
247 return result
== KERN_SUCCESS
;
249 bool threadIsSuspended
= (SuspendThread(platformThread
) != (DWORD
)-1);
250 ASSERT(threadIsSuspended
);
251 return threadIsSuspended
;
253 pthread_kill(platformThread
, SigThreadSuspendResume
);
256 #error Need a way to suspend threads on this platform
260 static inline void resumeThread(const PlatformThread
& platformThread
)
263 thread_resume(platformThread
);
265 ResumeThread(platformThread
);
267 pthread_kill(platformThread
, SigThreadSuspendResume
);
269 #error Need a way to resume threads on this platform
273 typedef unsigned long usword_t
; // word size, assumed to be either 32 or 64 bit
278 typedef i386_thread_state_t PlatformThreadRegisters
;
280 typedef x86_thread_state64_t PlatformThreadRegisters
;
282 typedef ppc_thread_state_t PlatformThreadRegisters
;
284 typedef ppc_thread_state64_t PlatformThreadRegisters
;
286 typedef arm_thread_state_t PlatformThreadRegisters
;
288 typedef arm_thread_state64_t PlatformThreadRegisters
;
290 #error Unknown Architecture
294 typedef CONTEXT PlatformThreadRegisters
;
296 typedef pthread_attr_t PlatformThreadRegisters
;
298 #error Need a thread register struct for this platform
301 static size_t getPlatformThreadRegisters(const PlatformThread
& platformThread
, PlatformThreadRegisters
& regs
)
306 unsigned user_count
= sizeof(regs
)/sizeof(int);
307 thread_state_flavor_t flavor
= i386_THREAD_STATE
;
309 unsigned user_count
= x86_THREAD_STATE64_COUNT
;
310 thread_state_flavor_t flavor
= x86_THREAD_STATE64
;
312 unsigned user_count
= PPC_THREAD_STATE_COUNT
;
313 thread_state_flavor_t flavor
= PPC_THREAD_STATE
;
315 unsigned user_count
= PPC_THREAD_STATE64_COUNT
;
316 thread_state_flavor_t flavor
= PPC_THREAD_STATE64
;
318 unsigned user_count
= ARM_THREAD_STATE_COUNT
;
319 thread_state_flavor_t flavor
= ARM_THREAD_STATE
;
321 unsigned user_count
= ARM_THREAD_STATE64_COUNT
;
322 thread_state_flavor_t flavor
= ARM_THREAD_STATE64
;
324 #error Unknown Architecture
327 kern_return_t result
= thread_get_state(platformThread
, flavor
, (thread_state_t
)®s
, &user_count
);
328 if (result
!= KERN_SUCCESS
) {
329 WTFReportFatalError(__FILE__
, __LINE__
, WTF_PRETTY_FUNCTION
,
330 "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
);
333 return user_count
* sizeof(usword_t
);
337 regs
.ContextFlags
= CONTEXT_INTEGER
| CONTEXT_CONTROL
;
338 GetThreadContext(platformThread
, ®s
);
339 return sizeof(CONTEXT
);
341 pthread_attr_init(®s
);
342 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
344 // e.g. on FreeBSD 5.4, neundorf@kde.org
345 pthread_attr_get_np(platformThread
, ®s
);
348 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
349 pthread_getattr_np(platformThread
, ®s
);
353 #error Need a way to get thread registers on this platform
357 static inline void* otherThreadStackPointer(const PlatformThreadRegisters
& regs
)
364 return reinterpret_cast<void*>(regs
.__esp
);
366 return reinterpret_cast<void*>(regs
.__rsp
);
367 #elif CPU(PPC) || CPU(PPC64)
368 return reinterpret_cast<void*>(regs
.__r1
);
370 return reinterpret_cast<void*>(regs
.__sp
);
372 return reinterpret_cast<void*>(regs
.__sp
);
374 #error Unknown Architecture
377 #else // !__DARWIN_UNIX03
380 return reinterpret_cast<void*>(regs
.esp
);
382 return reinterpret_cast<void*>(regs
.rsp
);
383 #elif CPU(PPC) || CPU(PPC64)
384 return reinterpret_cast<void*>(regs
.r1
);
386 #error Unknown Architecture
389 #endif // __DARWIN_UNIX03
395 return reinterpret_cast<void*>((uintptr_t) regs
.Sp
);
397 return reinterpret_cast<void*>((uintptr_t) regs
.IntSp
);
399 return reinterpret_cast<void*>((uintptr_t) regs
.Esp
);
401 return reinterpret_cast<void*>((uintptr_t) regs
.Rsp
);
403 #error Unknown Architecture
408 size_t stackSize
= 0;
411 int rc
= pthread_stackseg_np(pthread_self(), &ss
);
412 stackBase
= (void*)((size_t) ss
.ss_sp
- ss
.ss_size
);
413 stackSize
= ss
.ss_size
;
415 int rc
= pthread_attr_getstack(®s
, &stackBase
, &stackSize
);
417 (void)rc
; // FIXME: Deal with error code somehow? Seems fatal.
419 return static_cast<char*>(stackBase
) + stackSize
;
421 #error Need a way to get the stack pointer for another thread on this platform
425 static void freePlatformThreadRegisters(PlatformThreadRegisters
& regs
)
427 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
428 pthread_attr_destroy(®s
);
434 void MachineThreads::gatherFromOtherThread(ConservativeRoots
& conservativeRoots
, Thread
* thread
, JITStubRoutineSet
& jitStubRoutines
, CodeBlockSet
& codeBlocks
)
436 PlatformThreadRegisters regs
;
437 size_t regSize
= getPlatformThreadRegisters(thread
->platformThread
, regs
);
439 conservativeRoots
.add(static_cast<void*>(®s
), static_cast<void*>(reinterpret_cast<char*>(®s
) + regSize
), jitStubRoutines
, codeBlocks
);
441 void* stackPointer
= otherThreadStackPointer(regs
);
442 void* stackBase
= thread
->stackBase
;
443 swapIfBackwards(stackPointer
, stackBase
);
444 stackPointer
= reinterpret_cast<void*>(WTF::roundUpToMultipleOf
<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackPointer
)));
445 conservativeRoots
.add(stackPointer
, stackBase
, jitStubRoutines
, codeBlocks
);
447 freePlatformThreadRegisters(regs
);
450 void MachineThreads::gatherConservativeRoots(ConservativeRoots
& conservativeRoots
, JITStubRoutineSet
& jitStubRoutines
, CodeBlockSet
& codeBlocks
, void* stackCurrent
, RegisterState
& registers
)
452 gatherFromCurrentThread(conservativeRoots
, jitStubRoutines
, codeBlocks
, stackCurrent
, registers
);
454 if (m_threadSpecific
) {
455 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
457 MutexLocker
lock(m_registeredThreadsMutex
);
459 Thread
* threadsToBeDeleted
= nullptr;
462 // Forbid malloc during the gather phase. The gather phase suspends
463 // threads, so a malloc during gather would risk a deadlock with a
464 // thread that had been suspended while holding the malloc lock.
467 int numberOfThreads
= 0; // Using 0 to denote that we haven't counted the number of threads yet.
469 Thread
* previousThread
= nullptr;
470 for (Thread
* thread
= m_registeredThreads
; thread
; index
++) {
471 if (!equalThread(thread
->platformThread
, currentPlatformThread
)) {
472 bool success
= suspendThread(thread
->platformThread
);
475 if (!numberOfThreads
) {
476 for (Thread
* countedThread
= m_registeredThreads
; countedThread
; countedThread
= countedThread
->next
)
480 // Re-do the suspension to get the actual failure result for logging.
481 kern_return_t error
= thread_suspend(thread
->platformThread
);
482 ASSERT(error
!= KERN_SUCCESS
);
484 WTFReportError(__FILE__
, __LINE__
, WTF_PRETTY_FUNCTION
,
485 "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] platformThread %p.",
486 error
, index
, numberOfThreads
, thread
, reinterpret_cast<void*>(thread
->platformThread
));
488 // Put the invalid thread on the threadsToBeDeleted list.
489 // We can't just delete it here because we have suspended other
490 // threads, and they may still be holding the C heap lock which
491 // we need for deleting the invalid thread. Hence, we need to
492 // defer the deletion till after we have resumed all threads.
493 Thread
* nextThread
= thread
->next
;
494 thread
->next
= threadsToBeDeleted
;
495 threadsToBeDeleted
= thread
;
498 previousThread
->next
= nextThread
;
500 m_registeredThreads
= nextThread
;
505 UNUSED_PARAM(numberOfThreads
);
506 ASSERT_UNUSED(success
, success
);
509 previousThread
= thread
;
510 thread
= thread
->next
;
513 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
514 // and since this is a shared heap, they are real locks.
515 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
516 if (!equalThread(thread
->platformThread
, currentPlatformThread
))
517 gatherFromOtherThread(conservativeRoots
, thread
, jitStubRoutines
, codeBlocks
);
520 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
521 if (!equalThread(thread
->platformThread
, currentPlatformThread
))
522 resumeThread(thread
->platformThread
);
528 for (Thread
* thread
= threadsToBeDeleted
; thread
; ) {
529 Thread
* nextThread
= thread
->next
;