• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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.
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 
swapIfBackwards(void * & begin,void * & end)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 
pthreadSignalHandlerSuspendResume(int signo)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:
Thread(pthread_t pthread,const PlatformThread & platThread,void * base)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 
MachineThreads(Heap * heap)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 
~MachineThreads()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 
getCurrentPlatformThread()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 
makeUsableFromMultipleThreads()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 
addCurrentThread()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 
removeThread(void * p)217 void MachineThreads::removeThread(void* p)
218 {
219     if (p)
220         static_cast<MachineThreads*>(p)->removeCurrentThread();
221 }
222 
removeCurrentThread()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 
gatherFromCurrentThread(ConservativeRoots & conservativeRoots,void * stackCurrent)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 = &registers;
270     void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&registers + 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 
suspendThread(const PlatformThread & platformThread)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 
resumeThread(const PlatformThread & platformThread)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 
getPlatformThreadRegisters(const PlatformThread & platformThread,PlatformThreadRegisters & regs)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)&regs, &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, &regs);
369     return sizeof(CONTEXT);
370 #elif USE(PTHREADS)
371     pthread_attr_init(&regs);
372 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
373     // e.g. on FreeBSD 5.4, neundorf@kde.org
374     pthread_attr_get_np(platformThread, &regs);
375 #else
376     // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
377     pthread_getattr_np(platformThread, &regs);
378 #endif
379     return 0;
380 #else
381 #error Need a way to get thread registers on this platform
382 #endif
383 }
384 
otherThreadStackPointer(const PlatformThreadRegisters & regs)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(&regs, &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 
freePlatformThreadRegisters(PlatformThreadRegisters & regs)432 static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
433 {
434 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
435     pthread_attr_destroy(&regs);
436 #else
437     UNUSED_PARAM(regs);
438 #endif
439 }
440 
gatherFromOtherThread(ConservativeRoots & conservativeRoots,Thread * thread)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*>(&regs), static_cast<void*>(reinterpret_cast<char*>(&regs) + 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 
gatherConservativeRoots(ConservativeRoots & conservativeRoots,void * stackCurrent)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