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 "JSGlobalData.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>
58 #if HAVE(PTHREAD_NP_H)
59 #include <pthread_np.h>
64 #include <sys/procfs.h>
69 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
79 static inline void swapIfBackwards(void*& begin
, void*& end
)
84 std::swap(begin
, end
);
92 typedef mach_port_t PlatformThread
;
94 typedef HANDLE PlatformThread
;
96 typedef pthread_t PlatformThread
;
97 static const int SigThreadSuspendResume
= SIGUSR2
;
99 #if defined(SA_RESTART)
100 static void pthreadSignalHandlerSuspendResume(int signo
)
103 sigemptyset(&signalSet
);
104 sigaddset(&signalSet
, SigThreadSuspendResume
);
105 sigsuspend(&signalSet
);
110 class MachineThreads::Thread
{
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
)
138 , m_registeredThreads(0)
139 , m_threadSpecific(0)
143 MachineThreads::~MachineThreads()
145 if (m_threadSpecific
) {
146 int error
= pthread_key_delete(m_threadSpecific
);
147 ASSERT_UNUSED(error
, !error
);
150 MutexLocker
registeredThreadsLock(m_registeredThreadsMutex
);
151 for (Thread
* t
= m_registeredThreads
; t
;) {
152 Thread
* next
= t
->next
;
158 static inline PlatformThread
getCurrentPlatformThread()
161 return pthread_mach_thread_np(pthread_self());
163 return GetCurrentThread();
165 return pthread_self();
169 static inline bool equalThread(const PlatformThread
& first
, const PlatformThread
& second
)
171 #if OS(DARWIN) || OS(WINDOWS)
172 return first
== second
;
174 return !!pthread_equal(first
, second
);
176 #error Need a way to compare threads on this platform
180 void MachineThreads::makeUsableFromMultipleThreads()
182 if (m_threadSpecific
)
185 int error
= pthread_key_create(&m_threadSpecific
, removeThread
);
190 void MachineThreads::addCurrentThread()
192 ASSERT(!m_heap
->globalData()->exclusiveThread
|| m_heap
->globalData()->exclusiveThread
== currentThread());
194 if (!m_threadSpecific
|| pthread_getspecific(m_threadSpecific
))
197 pthread_setspecific(m_threadSpecific
, this);
198 Thread
* thread
= new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
200 MutexLocker
lock(m_registeredThreadsMutex
);
202 thread
->next
= m_registeredThreads
;
203 m_registeredThreads
= thread
;
206 void MachineThreads::removeThread(void* p
)
209 static_cast<MachineThreads
*>(p
)->removeCurrentThread();
212 void MachineThreads::removeCurrentThread()
214 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
216 MutexLocker
lock(m_registeredThreadsMutex
);
218 if (equalThread(currentPlatformThread
, m_registeredThreads
->platformThread
)) {
219 Thread
* t
= m_registeredThreads
;
220 m_registeredThreads
= m_registeredThreads
->next
;
223 Thread
* last
= m_registeredThreads
;
225 for (t
= m_registeredThreads
->next
; t
; t
= t
->next
) {
226 if (equalThread(t
->platformThread
, currentPlatformThread
)) {
227 last
->next
= t
->next
;
232 ASSERT(t
); // If t is NULL, we never found ourselves in the list.
238 #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
240 #define REGISTER_BUFFER_ALIGNMENT
243 void MachineThreads::gatherFromCurrentThread(ConservativeRoots
& conservativeRoots
, void* stackCurrent
)
245 // setjmp forces volatile registers onto the stack
246 jmp_buf registers REGISTER_BUFFER_ALIGNMENT
;
248 #pragma warning(push)
249 #pragma warning(disable: 4611)
256 void* registersBegin
= ®isters
;
257 void* registersEnd
= reinterpret_cast<void*>(roundUpToMultipleOf
<sizeof(void*)>(reinterpret_cast<uintptr_t>(®isters
+ 1)));
258 swapIfBackwards(registersBegin
, registersEnd
);
259 conservativeRoots
.add(registersBegin
, registersEnd
);
261 void* stackBegin
= stackCurrent
;
262 void* stackEnd
= wtfThreadData().stack().origin();
263 swapIfBackwards(stackBegin
, stackEnd
);
264 conservativeRoots
.add(stackBegin
, stackEnd
);
267 static inline void suspendThread(const PlatformThread
& platformThread
)
270 thread_suspend(platformThread
);
272 SuspendThread(platformThread
);
274 pthread_kill(platformThread
, SigThreadSuspendResume
);
276 #error Need a way to suspend threads on this platform
280 static inline void resumeThread(const PlatformThread
& platformThread
)
283 thread_resume(platformThread
);
285 ResumeThread(platformThread
);
287 pthread_kill(platformThread
, SigThreadSuspendResume
);
289 #error Need a way to resume threads on this platform
293 typedef unsigned long usword_t
; // word size, assumed to be either 32 or 64 bit
298 typedef i386_thread_state_t PlatformThreadRegisters
;
300 typedef x86_thread_state64_t PlatformThreadRegisters
;
302 typedef ppc_thread_state_t PlatformThreadRegisters
;
304 typedef ppc_thread_state64_t PlatformThreadRegisters
;
306 typedef arm_thread_state_t PlatformThreadRegisters
;
308 #error Unknown Architecture
312 typedef CONTEXT PlatformThreadRegisters
;
314 typedef struct _debug_thread_info PlatformThreadRegisters
;
316 typedef pthread_attr_t PlatformThreadRegisters
;
318 #error Need a thread register struct for this platform
321 static size_t getPlatformThreadRegisters(const PlatformThread
& platformThread
, PlatformThreadRegisters
& regs
)
326 unsigned user_count
= sizeof(regs
)/sizeof(int);
327 thread_state_flavor_t flavor
= i386_THREAD_STATE
;
329 unsigned user_count
= x86_THREAD_STATE64_COUNT
;
330 thread_state_flavor_t flavor
= x86_THREAD_STATE64
;
332 unsigned user_count
= PPC_THREAD_STATE_COUNT
;
333 thread_state_flavor_t flavor
= PPC_THREAD_STATE
;
335 unsigned user_count
= PPC_THREAD_STATE64_COUNT
;
336 thread_state_flavor_t flavor
= PPC_THREAD_STATE64
;
338 unsigned user_count
= ARM_THREAD_STATE_COUNT
;
339 thread_state_flavor_t flavor
= ARM_THREAD_STATE
;
341 #error Unknown Architecture
344 kern_return_t result
= thread_get_state(platformThread
, flavor
, (thread_state_t
)®s
, &user_count
);
345 if (result
!= KERN_SUCCESS
) {
346 WTFReportFatalError(__FILE__
, __LINE__
, WTF_PRETTY_FUNCTION
,
347 "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
);
350 return user_count
* sizeof(usword_t
);
354 regs
.ContextFlags
= CONTEXT_INTEGER
| CONTEXT_CONTROL
;
355 GetThreadContext(platformThread
, ®s
);
356 return sizeof(CONTEXT
);
358 memset(®s
, 0, sizeof(regs
));
359 regs
.tid
= pthread_self();
360 int fd
= open("/proc/self", O_RDONLY
);
362 LOG_ERROR("Unable to open /proc/self (errno: %d)", errno
);
365 devctl(fd
, DCMD_PROC_TIDSTATUS
, ®s
, sizeof(regs
), 0);
368 pthread_attr_init(®s
);
369 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
370 // e.g. on FreeBSD 5.4, neundorf@kde.org
371 pthread_attr_get_np(platformThread
, ®s
);
373 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
374 pthread_getattr_np(platformThread
, ®s
);
378 #error Need a way to get thread registers on this platform
382 static inline void* otherThreadStackPointer(const PlatformThreadRegisters
& regs
)
389 return reinterpret_cast<void*>(regs
.__esp
);
391 return reinterpret_cast<void*>(regs
.__rsp
);
392 #elif CPU(PPC) || CPU(PPC64)
393 return reinterpret_cast<void*>(regs
.__r1
);
395 return reinterpret_cast<void*>(regs
.__sp
);
397 #error Unknown Architecture
400 #else // !__DARWIN_UNIX03
403 return reinterpret_cast<void*>(regs
.esp
);
405 return reinterpret_cast<void*>(regs
.rsp
);
406 #elif CPU(PPC) || CPU(PPC64)
407 return reinterpret_cast<void*>(regs
.r1
);
409 #error Unknown Architecture
412 #endif // __DARWIN_UNIX03
418 return reinterpret_cast<void*>((uintptr_t) regs
.Sp
);
420 return reinterpret_cast<void*>((uintptr_t) regs
.IntSp
);
422 return reinterpret_cast<void*>((uintptr_t) regs
.Esp
);
424 return reinterpret_cast<void*>((uintptr_t) regs
.Rsp
);
426 #error Unknown Architecture
430 return reinterpret_cast<void*>((uintptr_t) regs
.sp
);
434 size_t stackSize
= 0;
435 int rc
= pthread_attr_getstack(®s
, &stackBase
, &stackSize
);
436 (void)rc
; // FIXME: Deal with error code somehow? Seems fatal.
438 return static_cast<char*>(stackBase
) + stackSize
;
440 #error Need a way to get the stack pointer for another thread on this platform
444 static void freePlatformThreadRegisters(PlatformThreadRegisters
& regs
)
446 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && !OS(QNX)
447 pthread_attr_destroy(®s
);
453 void MachineThreads::gatherFromOtherThread(ConservativeRoots
& conservativeRoots
, Thread
* thread
)
455 suspendThread(thread
->platformThread
);
457 PlatformThreadRegisters regs
;
458 size_t regSize
= getPlatformThreadRegisters(thread
->platformThread
, regs
);
460 conservativeRoots
.add(static_cast<void*>(®s
), static_cast<void*>(reinterpret_cast<char*>(®s
) + regSize
));
462 void* stackPointer
= otherThreadStackPointer(regs
);
463 void* stackBase
= thread
->stackBase
;
464 swapIfBackwards(stackPointer
, stackBase
);
465 conservativeRoots
.add(stackPointer
, stackBase
);
467 resumeThread(thread
->platformThread
);
469 freePlatformThreadRegisters(regs
);
472 void MachineThreads::gatherConservativeRoots(ConservativeRoots
& conservativeRoots
, void* stackCurrent
)
474 gatherFromCurrentThread(conservativeRoots
, stackCurrent
);
476 if (m_threadSpecific
) {
477 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
479 MutexLocker
lock(m_registeredThreadsMutex
);
482 // Forbid malloc during the gather phase. The gather phase suspends
483 // threads, so a malloc during gather would risk a deadlock with a
484 // thread that had been suspended while holding the malloc lock.
487 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
488 // and since this is a shared heap, they are real locks.
489 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
490 if (!equalThread(thread
->platformThread
, currentPlatformThread
))
491 gatherFromOtherThread(conservativeRoots
, thread
);