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 = ®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);
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)®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);
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, ®s);
369 return sizeof(CONTEXT);
370 #elif USE(PTHREADS)
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);
375 #else
376 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
377 pthread_getattr_np(platformThread, ®s);
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(®s, &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(®s);
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*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + 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