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>
33 #if USE(PTHREAD_BASED_QT) && !defined(WTF_USE_PTHREADS)
34 #define WTF_USE_PTHREADS 1
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>
68 #if HAVE(PTHREAD_NP_H)
69 #include <pthread_np.h>
74 #include <sys/procfs.h>
79 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
82 #error MachineThreads requires SA_RESTART
92 static inline void swapIfBackwards(void*& begin
, void*& end
)
97 std::swap(begin
, end
);
104 #if ENABLE(JSC_MULTIPLE_THREADS)
107 typedef mach_port_t PlatformThread
;
109 typedef HANDLE PlatformThread
;
111 typedef pthread_t PlatformThread
;
112 static const int SigThreadSuspendResume
= SIGUSR2
;
114 static void pthreadSignalHandlerSuspendResume(int signo
)
117 sigemptyset(&signalSet
);
118 sigaddset(&signalSet
, SigThreadSuspendResume
);
119 sigsuspend(&signalSet
);
123 class MachineThreads::Thread
{
125 Thread(pthread_t pthread
, const PlatformThread
& platThread
, void* base
)
126 : posixThread(pthread
)
127 , platformThread(platThread
)
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);
139 sigaddset(&mask
, SigThreadSuspendResume
);
140 pthread_sigmask(SIG_UNBLOCK
, &mask
, 0);
145 pthread_t posixThread
;
146 PlatformThread platformThread
;
152 MachineThreads::MachineThreads(Heap
* heap
)
154 #if ENABLE(JSC_MULTIPLE_THREADS)
155 , m_registeredThreads(0)
156 , m_threadSpecific(0)
161 MachineThreads::~MachineThreads()
163 #if ENABLE(JSC_MULTIPLE_THREADS)
164 if (m_threadSpecific
) {
165 int error
= pthread_key_delete(m_threadSpecific
);
166 ASSERT_UNUSED(error
, !error
);
169 MutexLocker
registeredThreadsLock(m_registeredThreadsMutex
);
170 for (Thread
* t
= m_registeredThreads
; t
;) {
171 Thread
* next
= t
->next
;
178 #if ENABLE(JSC_MULTIPLE_THREADS)
180 static inline PlatformThread
getCurrentPlatformThread()
183 return pthread_mach_thread_np(pthread_self());
185 return pthread_getw32threadhandle_np(pthread_self());
187 return pthread_self();
191 void MachineThreads::makeUsableFromMultipleThreads()
193 if (m_threadSpecific
)
196 int error
= pthread_key_create(&m_threadSpecific
, removeThread
);
201 void MachineThreads::addCurrentThread()
203 ASSERT(!m_heap
->globalData()->exclusiveThread
|| m_heap
->globalData()->exclusiveThread
== currentThread());
205 if (!m_threadSpecific
|| pthread_getspecific(m_threadSpecific
))
208 pthread_setspecific(m_threadSpecific
, this);
209 Thread
* thread
= new Thread(pthread_self(), getCurrentPlatformThread(), m_heap
->globalData()->stack().origin());
211 MutexLocker
lock(m_registeredThreadsMutex
);
213 thread
->next
= m_registeredThreads
;
214 m_registeredThreads
= thread
;
217 void MachineThreads::removeThread(void* p
)
220 static_cast<MachineThreads
*>(p
)->removeCurrentThread();
223 void MachineThreads::removeCurrentThread()
225 pthread_t currentPosixThread
= pthread_self();
227 MutexLocker
lock(m_registeredThreadsMutex
);
229 if (pthread_equal(currentPosixThread
, m_registeredThreads
->posixThread
)) {
230 Thread
* t
= m_registeredThreads
;
231 m_registeredThreads
= m_registeredThreads
->next
;
234 Thread
* last
= m_registeredThreads
;
236 for (t
= m_registeredThreads
->next
; t
; t
= t
->next
) {
237 if (pthread_equal(t
->posixThread
, currentPosixThread
)) {
238 last
->next
= t
->next
;
243 ASSERT(t
); // If t is NULL, we never found ourselves in the list.
251 #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
253 #define REGISTER_BUFFER_ALIGNMENT
256 void MachineThreads::gatherFromCurrentThread(ConservativeRoots
& conservativeRoots
, void* stackCurrent
)
258 // setjmp forces volatile registers onto the stack
259 jmp_buf registers REGISTER_BUFFER_ALIGNMENT
;
261 #pragma warning(push)
262 #pragma warning(disable: 4611)
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
);
274 void* stackBegin
= stackCurrent
;
275 void* stackEnd
= m_heap
->globalData()->stack().origin();
276 swapIfBackwards(stackBegin
, stackEnd
);
277 conservativeRoots
.add(stackBegin
, stackEnd
);
280 #if ENABLE(JSC_MULTIPLE_THREADS)
282 static inline void suspendThread(const PlatformThread
& platformThread
)
285 thread_suspend(platformThread
);
287 SuspendThread(platformThread
);
289 pthread_kill(platformThread
, SigThreadSuspendResume
);
291 #error Need a way to suspend threads on this platform
295 static inline void resumeThread(const PlatformThread
& platformThread
)
298 thread_resume(platformThread
);
300 ResumeThread(platformThread
);
302 pthread_kill(platformThread
, SigThreadSuspendResume
);
304 #error Need a way to resume threads on this platform
308 typedef unsigned long usword_t
; // word size, assumed to be either 32 or 64 bit
313 typedef i386_thread_state_t PlatformThreadRegisters
;
315 typedef x86_thread_state64_t PlatformThreadRegisters
;
317 typedef ppc_thread_state_t PlatformThreadRegisters
;
319 typedef ppc_thread_state64_t PlatformThreadRegisters
;
321 typedef arm_thread_state_t PlatformThreadRegisters
;
323 #error Unknown Architecture
327 typedef CONTEXT PlatformThreadRegisters
;
329 typedef pthread_attr_t PlatformThreadRegisters
;
331 #error Need a thread register struct for this platform
334 static size_t getPlatformThreadRegisters(const PlatformThread
& platformThread
, PlatformThreadRegisters
& regs
)
339 unsigned user_count
= sizeof(regs
)/sizeof(int);
340 thread_state_flavor_t flavor
= i386_THREAD_STATE
;
342 unsigned user_count
= x86_THREAD_STATE64_COUNT
;
343 thread_state_flavor_t flavor
= x86_THREAD_STATE64
;
345 unsigned user_count
= PPC_THREAD_STATE_COUNT
;
346 thread_state_flavor_t flavor
= PPC_THREAD_STATE
;
348 unsigned user_count
= PPC_THREAD_STATE64_COUNT
;
349 thread_state_flavor_t flavor
= PPC_THREAD_STATE64
;
351 unsigned user_count
= ARM_THREAD_STATE_COUNT
;
352 thread_state_flavor_t flavor
= ARM_THREAD_STATE
;
354 #error Unknown Architecture
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
);
363 return user_count
* sizeof(usword_t
);
367 regs
.ContextFlags
= CONTEXT_INTEGER
| CONTEXT_CONTROL
| CONTEXT_SEGMENTS
;
368 GetThreadContext(platformThread
, ®s
);
369 return sizeof(CONTEXT
);
371 pthread_attr_init(®s
);
372 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
373 // e.g. on FreeBSD 5.4, neundorf@kde.org
374 pthread_attr_get_np(platformThread
, ®s
);
376 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
377 pthread_getattr_np(platformThread
, ®s
);
381 #error Need a way to get thread registers on this platform
385 static inline void* otherThreadStackPointer(const PlatformThreadRegisters
& regs
)
392 return reinterpret_cast<void*>(regs
.__esp
);
394 return reinterpret_cast<void*>(regs
.__rsp
);
395 #elif CPU(PPC) || CPU(PPC64)
396 return reinterpret_cast<void*>(regs
.__r1
);
398 return reinterpret_cast<void*>(regs
.__sp
);
400 #error Unknown Architecture
403 #else // !__DARWIN_UNIX03
406 return reinterpret_cast<void*>(regs
.esp
);
408 return reinterpret_cast<void*>(regs
.rsp
);
409 #elif CPU(PPC) || CPU(PPC64)
410 return reinterpret_cast<void*>(regs
.r1
);
412 #error Unknown Architecture
415 #endif // __DARWIN_UNIX03
418 #elif CPU(X86) && OS(WINDOWS)
419 return reinterpret_cast<void*>((uintptr_t) regs
.Esp
);
420 #elif CPU(X86_64) && OS(WINDOWS)
421 return reinterpret_cast<void*>((uintptr_t) regs
.Rsp
);
424 size_t stackSize
= 0;
425 int rc
= pthread_attr_getstack(®s
, &stackBase
, &stackSize
);
426 (void)rc
; // FIXME: Deal with error code somehow? Seems fatal.
428 return static_cast<char*>(stackBase
) + stackSize
;
430 #error Need a way to get the stack pointer for another thread on this platform
434 static void freePlatformThreadRegisters(PlatformThreadRegisters
& regs
)
436 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
437 pthread_attr_destroy(®s
);
443 void MachineThreads::gatherFromOtherThread(ConservativeRoots
& conservativeRoots
, Thread
* thread
)
445 suspendThread(thread
->platformThread
);
447 PlatformThreadRegisters regs
;
448 size_t regSize
= getPlatformThreadRegisters(thread
->platformThread
, regs
);
450 conservativeRoots
.add(static_cast<void*>(®s
), static_cast<void*>(reinterpret_cast<char*>(®s
) + regSize
));
452 void* stackPointer
= otherThreadStackPointer(regs
);
453 void* stackBase
= thread
->stackBase
;
454 swapIfBackwards(stackPointer
, stackBase
);
455 conservativeRoots
.add(stackPointer
, stackBase
);
457 resumeThread(thread
->platformThread
);
459 freePlatformThreadRegisters(regs
);
464 void MachineThreads::gatherConservativeRoots(ConservativeRoots
& conservativeRoots
, void* stackCurrent
)
466 gatherFromCurrentThread(conservativeRoots
, stackCurrent
);
468 #if ENABLE(JSC_MULTIPLE_THREADS)
470 if (m_threadSpecific
) {
472 MutexLocker
lock(m_registeredThreadsMutex
);
475 // Forbid malloc during the gather phase. The gather phase suspends
476 // threads, so a malloc during gather would risk a deadlock with a
477 // thread that had been suspended while holding the malloc lock.
480 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
481 // and since this is a shared heap, they are real locks.
482 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
483 if (!pthread_equal(thread
->posixThread
, pthread_self()))
484 gatherFromOtherThread(conservativeRoots
, thread
);