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"
31 #include <wtf/StdLibExtras.h>
35 #include <mach/mach_init.h>
36 #include <mach/mach_port.h>
37 #include <mach/task.h>
38 #include <mach/thread_act.h>
39 #include <mach/vm_map.h>
57 #if HAVE(PTHREAD_NP_H)
58 #include <pthread_np.h>
63 #include <sys/procfs.h>
68 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
78 static inline void swapIfBackwards(void*& begin
, void*& end
)
83 std::swap(begin
, end
);
91 typedef mach_port_t PlatformThread
;
93 typedef HANDLE PlatformThread
;
95 typedef pthread_t PlatformThread
;
96 static const int SigThreadSuspendResume
= SIGUSR2
;
98 #if defined(SA_RESTART)
99 static void pthreadSignalHandlerSuspendResume(int)
102 sigemptyset(&signalSet
);
103 sigaddset(&signalSet
, SigThreadSuspendResume
);
104 sigsuspend(&signalSet
);
109 class MachineThreads::Thread
{
110 WTF_MAKE_FAST_ALLOCATED
;
112 Thread(const PlatformThread
& platThread
, void* base
)
113 : platformThread(platThread
)
116 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART)
117 // if we have SA_RESTART, enable SIGUSR2 debugging mechanism
118 struct sigaction action
;
119 action
.sa_handler
= pthreadSignalHandlerSuspendResume
;
120 sigemptyset(&action
.sa_mask
);
121 action
.sa_flags
= SA_RESTART
;
122 sigaction(SigThreadSuspendResume
, &action
, 0);
126 sigaddset(&mask
, SigThreadSuspendResume
);
127 pthread_sigmask(SIG_UNBLOCK
, &mask
, 0);
132 PlatformThread platformThread
;
136 MachineThreads::MachineThreads(Heap
* heap
)
137 : m_registeredThreads(0)
138 , m_threadSpecific(0)
146 MachineThreads::~MachineThreads()
148 if (m_threadSpecific
)
149 threadSpecificKeyDelete(m_threadSpecific
);
151 MutexLocker
registeredThreadsLock(m_registeredThreadsMutex
);
152 for (Thread
* t
= m_registeredThreads
; t
;) {
153 Thread
* next
= t
->next
;
159 static inline PlatformThread
getCurrentPlatformThread()
162 return pthread_mach_thread_np(pthread_self());
164 return GetCurrentThread();
166 return pthread_self();
170 static inline bool equalThread(const PlatformThread
& first
, const PlatformThread
& second
)
172 #if OS(DARWIN) || OS(WINDOWS)
173 return first
== second
;
175 return !!pthread_equal(first
, second
);
177 #error Need a way to compare threads on this platform
181 void MachineThreads::makeUsableFromMultipleThreads()
183 if (m_threadSpecific
)
186 threadSpecificKeyCreate(&m_threadSpecific
, removeThread
);
189 void MachineThreads::addCurrentThread()
191 ASSERT(!m_heap
->vm()->exclusiveThread
|| m_heap
->vm()->exclusiveThread
== currentThread());
193 if (!m_threadSpecific
|| threadSpecificGet(m_threadSpecific
))
196 threadSpecificSet(m_threadSpecific
, this);
197 Thread
* thread
= new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
199 MutexLocker
lock(m_registeredThreadsMutex
);
201 thread
->next
= m_registeredThreads
;
202 m_registeredThreads
= thread
;
205 void MachineThreads::removeThread(void* p
)
208 static_cast<MachineThreads
*>(p
)->removeCurrentThread();
211 void MachineThreads::removeCurrentThread()
213 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
215 MutexLocker
lock(m_registeredThreadsMutex
);
217 if (equalThread(currentPlatformThread
, m_registeredThreads
->platformThread
)) {
218 Thread
* t
= m_registeredThreads
;
219 m_registeredThreads
= m_registeredThreads
->next
;
222 Thread
* last
= m_registeredThreads
;
224 for (t
= m_registeredThreads
->next
; t
; t
= t
->next
) {
225 if (equalThread(t
->platformThread
, currentPlatformThread
)) {
226 last
->next
= t
->next
;
231 ASSERT(t
); // If t is NULL, we never found ourselves in the list.
237 #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
239 #define REGISTER_BUFFER_ALIGNMENT
242 void MachineThreads::gatherFromCurrentThread(ConservativeRoots
& conservativeRoots
, void* stackCurrent
)
244 // setjmp forces volatile registers onto the stack
245 jmp_buf registers REGISTER_BUFFER_ALIGNMENT
;
247 #pragma warning(push)
248 #pragma warning(disable: 4611)
255 void* registersBegin
= ®isters
;
256 void* registersEnd
= reinterpret_cast<void*>(roundUpToMultipleOf
<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters
+ 1)));
257 swapIfBackwards(registersBegin
, registersEnd
);
258 conservativeRoots
.add(registersBegin
, registersEnd
);
260 void* stackBegin
= stackCurrent
;
261 void* stackEnd
= wtfThreadData().stack().origin();
262 swapIfBackwards(stackBegin
, stackEnd
);
263 conservativeRoots
.add(stackBegin
, stackEnd
);
266 static inline void suspendThread(const PlatformThread
& platformThread
)
269 thread_suspend(platformThread
);
271 SuspendThread(platformThread
);
273 pthread_kill(platformThread
, SigThreadSuspendResume
);
275 #error Need a way to suspend threads on this platform
279 static inline void resumeThread(const PlatformThread
& platformThread
)
282 thread_resume(platformThread
);
284 ResumeThread(platformThread
);
286 pthread_kill(platformThread
, SigThreadSuspendResume
);
288 #error Need a way to resume threads on this platform
292 typedef unsigned long usword_t
; // word size, assumed to be either 32 or 64 bit
297 typedef i386_thread_state_t PlatformThreadRegisters
;
299 typedef x86_thread_state64_t PlatformThreadRegisters
;
301 typedef ppc_thread_state_t PlatformThreadRegisters
;
303 typedef ppc_thread_state64_t PlatformThreadRegisters
;
305 typedef arm_thread_state_t PlatformThreadRegisters
;
307 typedef arm_thread_state64_t PlatformThreadRegisters
;
309 #error Unknown Architecture
313 typedef CONTEXT PlatformThreadRegisters
;
315 typedef struct _debug_thread_info PlatformThreadRegisters
;
317 typedef pthread_attr_t PlatformThreadRegisters
;
319 #error Need a thread register struct for this platform
322 static size_t getPlatformThreadRegisters(const PlatformThread
& platformThread
, PlatformThreadRegisters
& regs
)
327 unsigned user_count
= sizeof(regs
)/sizeof(int);
328 thread_state_flavor_t flavor
= i386_THREAD_STATE
;
330 unsigned user_count
= x86_THREAD_STATE64_COUNT
;
331 thread_state_flavor_t flavor
= x86_THREAD_STATE64
;
333 unsigned user_count
= PPC_THREAD_STATE_COUNT
;
334 thread_state_flavor_t flavor
= PPC_THREAD_STATE
;
336 unsigned user_count
= PPC_THREAD_STATE64_COUNT
;
337 thread_state_flavor_t flavor
= PPC_THREAD_STATE64
;
339 unsigned user_count
= ARM_THREAD_STATE_COUNT
;
340 thread_state_flavor_t flavor
= ARM_THREAD_STATE
;
342 unsigned user_count
= ARM_THREAD_STATE64_COUNT
;
343 thread_state_flavor_t flavor
= ARM_THREAD_STATE64
;
345 #error Unknown Architecture
348 kern_return_t result
= thread_get_state(platformThread
, flavor
, (thread_state_t
)®s
, &user_count
);
349 if (result
!= KERN_SUCCESS
) {
350 WTFReportFatalError(__FILE__
, __LINE__
, WTF_PRETTY_FUNCTION
,
351 "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
);
354 return user_count
* sizeof(usword_t
);
358 regs
.ContextFlags
= CONTEXT_INTEGER
| CONTEXT_CONTROL
;
359 GetThreadContext(platformThread
, ®s
);
360 return sizeof(CONTEXT
);
362 memset(®s
, 0, sizeof(regs
));
363 regs
.tid
= platformThread
;
364 // FIXME: If we find this hurts performance, we can consider caching the fd and keeping it open.
365 int fd
= open("/proc/self/as", O_RDONLY
);
367 LOG_ERROR("Unable to open /proc/self/as (errno: %d)", errno
);
370 int rc
= devctl(fd
, DCMD_PROC_TIDSTATUS
, ®s
, sizeof(regs
), 0);
372 LOG_ERROR("devctl(DCMD_PROC_TIDSTATUS) failed (error: %d)", rc
);
376 return sizeof(struct _debug_thread_info
);
378 pthread_attr_init(®s
);
379 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
380 // e.g. on FreeBSD 5.4, neundorf@kde.org
381 pthread_attr_get_np(platformThread
, ®s
);
383 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
384 pthread_getattr_np(platformThread
, ®s
);
388 #error Need a way to get thread registers on this platform
392 static inline void* otherThreadStackPointer(const PlatformThreadRegisters
& regs
)
399 return reinterpret_cast<void*>(regs
.__esp
);
401 return reinterpret_cast<void*>(regs
.__rsp
);
402 #elif CPU(PPC) || CPU(PPC64)
403 return reinterpret_cast<void*>(regs
.__r1
);
405 return reinterpret_cast<void*>(regs
.__sp
);
407 return reinterpret_cast<void*>(regs
.__sp
);
409 #error Unknown Architecture
412 #else // !__DARWIN_UNIX03
415 return reinterpret_cast<void*>(regs
.esp
);
417 return reinterpret_cast<void*>(regs
.rsp
);
418 #elif CPU(PPC) || CPU(PPC64)
419 return reinterpret_cast<void*>(regs
.r1
);
421 #error Unknown Architecture
424 #endif // __DARWIN_UNIX03
430 return reinterpret_cast<void*>((uintptr_t) regs
.Sp
);
432 return reinterpret_cast<void*>((uintptr_t) regs
.IntSp
);
434 return reinterpret_cast<void*>((uintptr_t) regs
.Esp
);
436 return reinterpret_cast<void*>((uintptr_t) regs
.Rsp
);
438 #error Unknown Architecture
442 return reinterpret_cast<void*>((uintptr_t) regs
.sp
);
446 size_t stackSize
= 0;
447 int rc
= pthread_attr_getstack(®s
, &stackBase
, &stackSize
);
448 (void)rc
; // FIXME: Deal with error code somehow? Seems fatal.
450 return static_cast<char*>(stackBase
) + stackSize
;
452 #error Need a way to get the stack pointer for another thread on this platform
456 static void freePlatformThreadRegisters(PlatformThreadRegisters
& regs
)
458 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && !OS(QNX)
459 pthread_attr_destroy(®s
);
465 void MachineThreads::gatherFromOtherThread(ConservativeRoots
& conservativeRoots
, Thread
* thread
)
467 PlatformThreadRegisters regs
;
468 size_t regSize
= getPlatformThreadRegisters(thread
->platformThread
, regs
);
470 conservativeRoots
.add(static_cast<void*>(®s
), static_cast<void*>(reinterpret_cast<char*>(®s
) + regSize
));
472 void* stackPointer
= otherThreadStackPointer(regs
);
473 void* stackBase
= thread
->stackBase
;
474 swapIfBackwards(stackPointer
, stackBase
);
475 conservativeRoots
.add(stackPointer
, stackBase
);
477 freePlatformThreadRegisters(regs
);
480 void MachineThreads::gatherConservativeRoots(ConservativeRoots
& conservativeRoots
, void* stackCurrent
)
482 gatherFromCurrentThread(conservativeRoots
, stackCurrent
);
484 if (m_threadSpecific
) {
485 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
487 MutexLocker
lock(m_registeredThreadsMutex
);
490 // Forbid malloc during the gather phase. The gather phase suspends
491 // threads, so a malloc during gather would risk a deadlock with a
492 // thread that had been suspended while holding the malloc lock.
495 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
496 if (!equalThread(thread
->platformThread
, currentPlatformThread
))
497 suspendThread(thread
->platformThread
);
500 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
501 // and since this is a shared heap, they are real locks.
502 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
503 if (!equalThread(thread
->platformThread
, currentPlatformThread
))
504 gatherFromOtherThread(conservativeRoots
, thread
);
507 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
508 if (!equalThread(thread
->platformThread
, currentPlatformThread
))
509 resumeThread(thread
->platformThread
);