]>
Commit | Line | Data |
---|---|---|
14957cd0 A |
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" | |
93a37866 | 28 | #include "VM.h" |
14957cd0 A |
29 | #include <setjmp.h> |
30 | #include <stdlib.h> | |
31 | #include <wtf/StdLibExtras.h> | |
32 | ||
14957cd0 A |
33 | #if OS(DARWIN) |
34 | ||
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> | |
40 | ||
41 | #elif OS(WINDOWS) | |
42 | ||
43 | #include <windows.h> | |
44 | #include <malloc.h> | |
45 | ||
14957cd0 A |
46 | #elif OS(UNIX) |
47 | ||
14957cd0 | 48 | #include <sys/mman.h> |
14957cd0 A |
49 | #include <unistd.h> |
50 | ||
51 | #if OS(SOLARIS) | |
52 | #include <thread.h> | |
53 | #else | |
54 | #include <pthread.h> | |
55 | #endif | |
56 | ||
57 | #if HAVE(PTHREAD_NP_H) | |
58 | #include <pthread_np.h> | |
59 | #endif | |
60 | ||
61 | #if OS(QNX) | |
62 | #include <fcntl.h> | |
63 | #include <sys/procfs.h> | |
64 | #include <stdio.h> | |
65 | #include <errno.h> | |
66 | #endif | |
67 | ||
68 | #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) | |
69 | #include <signal.h> | |
14957cd0 A |
70 | #endif |
71 | ||
72 | #endif | |
73 | ||
74 | using namespace WTF; | |
75 | ||
76 | namespace JSC { | |
77 | ||
78 | static inline void swapIfBackwards(void*& begin, void*& end) | |
79 | { | |
80 | #if OS(WINCE) | |
81 | if (begin <= end) | |
82 | return; | |
83 | std::swap(begin, end); | |
84 | #else | |
85 | UNUSED_PARAM(begin); | |
86 | UNUSED_PARAM(end); | |
87 | #endif | |
88 | } | |
89 | ||
14957cd0 A |
90 | #if OS(DARWIN) |
91 | typedef mach_port_t PlatformThread; | |
92 | #elif OS(WINDOWS) | |
93 | typedef HANDLE PlatformThread; | |
94 | #elif USE(PTHREADS) | |
95 | typedef pthread_t PlatformThread; | |
96 | static const int SigThreadSuspendResume = SIGUSR2; | |
97 | ||
6fe7ccc8 | 98 | #if defined(SA_RESTART) |
93a37866 | 99 | static void pthreadSignalHandlerSuspendResume(int) |
14957cd0 A |
100 | { |
101 | sigset_t signalSet; | |
102 | sigemptyset(&signalSet); | |
103 | sigaddset(&signalSet, SigThreadSuspendResume); | |
104 | sigsuspend(&signalSet); | |
105 | } | |
106 | #endif | |
6fe7ccc8 | 107 | #endif |
14957cd0 A |
108 | |
109 | class MachineThreads::Thread { | |
93a37866 | 110 | WTF_MAKE_FAST_ALLOCATED; |
14957cd0 | 111 | public: |
6fe7ccc8 A |
112 | Thread(const PlatformThread& platThread, void* base) |
113 | : platformThread(platThread) | |
14957cd0 A |
114 | , stackBase(base) |
115 | { | |
6fe7ccc8 A |
116 | #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART) |
117 | // if we have SA_RESTART, enable SIGUSR2 debugging mechanism | |
14957cd0 A |
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); | |
123 | ||
124 | sigset_t mask; | |
125 | sigemptyset(&mask); | |
126 | sigaddset(&mask, SigThreadSuspendResume); | |
127 | pthread_sigmask(SIG_UNBLOCK, &mask, 0); | |
128 | #endif | |
129 | } | |
130 | ||
131 | Thread* next; | |
14957cd0 A |
132 | PlatformThread platformThread; |
133 | void* stackBase; | |
134 | }; | |
135 | ||
14957cd0 | 136 | MachineThreads::MachineThreads(Heap* heap) |
93a37866 | 137 | : m_registeredThreads(0) |
14957cd0 | 138 | , m_threadSpecific(0) |
93a37866 A |
139 | #if !ASSERT_DISABLED |
140 | , m_heap(heap) | |
141 | #endif | |
14957cd0 | 142 | { |
93a37866 | 143 | UNUSED_PARAM(heap); |
14957cd0 A |
144 | } |
145 | ||
146 | MachineThreads::~MachineThreads() | |
147 | { | |
93a37866 A |
148 | if (m_threadSpecific) |
149 | threadSpecificKeyDelete(m_threadSpecific); | |
14957cd0 A |
150 | |
151 | MutexLocker registeredThreadsLock(m_registeredThreadsMutex); | |
152 | for (Thread* t = m_registeredThreads; t;) { | |
153 | Thread* next = t->next; | |
154 | delete t; | |
155 | t = next; | |
156 | } | |
14957cd0 A |
157 | } |
158 | ||
14957cd0 A |
159 | static inline PlatformThread getCurrentPlatformThread() |
160 | { | |
161 | #if OS(DARWIN) | |
162 | return pthread_mach_thread_np(pthread_self()); | |
163 | #elif OS(WINDOWS) | |
6fe7ccc8 | 164 | return GetCurrentThread(); |
14957cd0 A |
165 | #elif USE(PTHREADS) |
166 | return pthread_self(); | |
167 | #endif | |
168 | } | |
169 | ||
6fe7ccc8 A |
170 | static inline bool equalThread(const PlatformThread& first, const PlatformThread& second) |
171 | { | |
172 | #if OS(DARWIN) || OS(WINDOWS) | |
173 | return first == second; | |
174 | #elif USE(PTHREADS) | |
175 | return !!pthread_equal(first, second); | |
176 | #else | |
177 | #error Need a way to compare threads on this platform | |
178 | #endif | |
179 | } | |
180 | ||
14957cd0 A |
181 | void MachineThreads::makeUsableFromMultipleThreads() |
182 | { | |
183 | if (m_threadSpecific) | |
184 | return; | |
185 | ||
93a37866 | 186 | threadSpecificKeyCreate(&m_threadSpecific, removeThread); |
14957cd0 A |
187 | } |
188 | ||
189 | void MachineThreads::addCurrentThread() | |
190 | { | |
93a37866 | 191 | ASSERT(!m_heap->vm()->exclusiveThread || m_heap->vm()->exclusiveThread == currentThread()); |
14957cd0 | 192 | |
93a37866 | 193 | if (!m_threadSpecific || threadSpecificGet(m_threadSpecific)) |
14957cd0 A |
194 | return; |
195 | ||
93a37866 | 196 | threadSpecificSet(m_threadSpecific, this); |
6fe7ccc8 | 197 | Thread* thread = new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin()); |
14957cd0 A |
198 | |
199 | MutexLocker lock(m_registeredThreadsMutex); | |
200 | ||
201 | thread->next = m_registeredThreads; | |
202 | m_registeredThreads = thread; | |
203 | } | |
204 | ||
205 | void MachineThreads::removeThread(void* p) | |
206 | { | |
207 | if (p) | |
208 | static_cast<MachineThreads*>(p)->removeCurrentThread(); | |
209 | } | |
210 | ||
211 | void MachineThreads::removeCurrentThread() | |
212 | { | |
6fe7ccc8 | 213 | PlatformThread currentPlatformThread = getCurrentPlatformThread(); |
14957cd0 A |
214 | |
215 | MutexLocker lock(m_registeredThreadsMutex); | |
216 | ||
6fe7ccc8 | 217 | if (equalThread(currentPlatformThread, m_registeredThreads->platformThread)) { |
14957cd0 A |
218 | Thread* t = m_registeredThreads; |
219 | m_registeredThreads = m_registeredThreads->next; | |
220 | delete t; | |
221 | } else { | |
222 | Thread* last = m_registeredThreads; | |
223 | Thread* t; | |
224 | for (t = m_registeredThreads->next; t; t = t->next) { | |
6fe7ccc8 | 225 | if (equalThread(t->platformThread, currentPlatformThread)) { |
14957cd0 A |
226 | last->next = t->next; |
227 | break; | |
228 | } | |
229 | last = t; | |
230 | } | |
231 | ASSERT(t); // If t is NULL, we never found ourselves in the list. | |
232 | delete t; | |
233 | } | |
234 | } | |
235 | ||
14957cd0 A |
236 | #if COMPILER(GCC) |
237 | #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*)))) | |
238 | #else | |
239 | #define REGISTER_BUFFER_ALIGNMENT | |
240 | #endif | |
241 | ||
242 | void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, void* stackCurrent) | |
243 | { | |
244 | // setjmp forces volatile registers onto the stack | |
245 | jmp_buf registers REGISTER_BUFFER_ALIGNMENT; | |
246 | #if COMPILER(MSVC) | |
247 | #pragma warning(push) | |
248 | #pragma warning(disable: 4611) | |
249 | #endif | |
250 | setjmp(registers); | |
251 | #if COMPILER(MSVC) | |
252 | #pragma warning(pop) | |
253 | #endif | |
254 | ||
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); | |
259 | ||
260 | void* stackBegin = stackCurrent; | |
6fe7ccc8 | 261 | void* stackEnd = wtfThreadData().stack().origin(); |
14957cd0 A |
262 | swapIfBackwards(stackBegin, stackEnd); |
263 | conservativeRoots.add(stackBegin, stackEnd); | |
264 | } | |
265 | ||
14957cd0 A |
266 | static inline void suspendThread(const PlatformThread& platformThread) |
267 | { | |
268 | #if OS(DARWIN) | |
269 | thread_suspend(platformThread); | |
270 | #elif OS(WINDOWS) | |
271 | SuspendThread(platformThread); | |
272 | #elif USE(PTHREADS) | |
273 | pthread_kill(platformThread, SigThreadSuspendResume); | |
274 | #else | |
275 | #error Need a way to suspend threads on this platform | |
276 | #endif | |
277 | } | |
278 | ||
279 | static inline void resumeThread(const PlatformThread& platformThread) | |
280 | { | |
281 | #if OS(DARWIN) | |
282 | thread_resume(platformThread); | |
283 | #elif OS(WINDOWS) | |
284 | ResumeThread(platformThread); | |
285 | #elif USE(PTHREADS) | |
286 | pthread_kill(platformThread, SigThreadSuspendResume); | |
287 | #else | |
288 | #error Need a way to resume threads on this platform | |
289 | #endif | |
290 | } | |
291 | ||
292 | typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit | |
293 | ||
294 | #if OS(DARWIN) | |
295 | ||
296 | #if CPU(X86) | |
297 | typedef i386_thread_state_t PlatformThreadRegisters; | |
298 | #elif CPU(X86_64) | |
299 | typedef x86_thread_state64_t PlatformThreadRegisters; | |
300 | #elif CPU(PPC) | |
301 | typedef ppc_thread_state_t PlatformThreadRegisters; | |
302 | #elif CPU(PPC64) | |
303 | typedef ppc_thread_state64_t PlatformThreadRegisters; | |
304 | #elif CPU(ARM) | |
305 | typedef arm_thread_state_t PlatformThreadRegisters; | |
93a37866 A |
306 | #elif CPU(ARM64) |
307 | typedef arm_thread_state64_t PlatformThreadRegisters; | |
14957cd0 A |
308 | #else |
309 | #error Unknown Architecture | |
310 | #endif | |
311 | ||
312 | #elif OS(WINDOWS) | |
313 | typedef CONTEXT PlatformThreadRegisters; | |
6fe7ccc8 A |
314 | #elif OS(QNX) |
315 | typedef struct _debug_thread_info PlatformThreadRegisters; | |
14957cd0 A |
316 | #elif USE(PTHREADS) |
317 | typedef pthread_attr_t PlatformThreadRegisters; | |
318 | #else | |
319 | #error Need a thread register struct for this platform | |
320 | #endif | |
321 | ||
322 | static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs) | |
323 | { | |
324 | #if OS(DARWIN) | |
325 | ||
326 | #if CPU(X86) | |
327 | unsigned user_count = sizeof(regs)/sizeof(int); | |
328 | thread_state_flavor_t flavor = i386_THREAD_STATE; | |
329 | #elif CPU(X86_64) | |
330 | unsigned user_count = x86_THREAD_STATE64_COUNT; | |
331 | thread_state_flavor_t flavor = x86_THREAD_STATE64; | |
332 | #elif CPU(PPC) | |
333 | unsigned user_count = PPC_THREAD_STATE_COUNT; | |
334 | thread_state_flavor_t flavor = PPC_THREAD_STATE; | |
335 | #elif CPU(PPC64) | |
336 | unsigned user_count = PPC_THREAD_STATE64_COUNT; | |
337 | thread_state_flavor_t flavor = PPC_THREAD_STATE64; | |
338 | #elif CPU(ARM) | |
339 | unsigned user_count = ARM_THREAD_STATE_COUNT; | |
340 | thread_state_flavor_t flavor = ARM_THREAD_STATE; | |
93a37866 A |
341 | #elif CPU(ARM64) |
342 | unsigned user_count = ARM_THREAD_STATE64_COUNT; | |
343 | thread_state_flavor_t flavor = ARM_THREAD_STATE64; | |
14957cd0 A |
344 | #else |
345 | #error Unknown Architecture | |
346 | #endif | |
347 | ||
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); | |
352 | CRASH(); | |
353 | } | |
354 | return user_count * sizeof(usword_t); | |
355 | // end OS(DARWIN) | |
356 | ||
357 | #elif OS(WINDOWS) | |
6fe7ccc8 | 358 | regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; |
14957cd0 A |
359 | GetThreadContext(platformThread, ®s); |
360 | return sizeof(CONTEXT); | |
6fe7ccc8 A |
361 | #elif OS(QNX) |
362 | memset(®s, 0, sizeof(regs)); | |
93a37866 A |
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); | |
6fe7ccc8 | 366 | if (fd == -1) { |
93a37866 A |
367 | LOG_ERROR("Unable to open /proc/self/as (errno: %d)", errno); |
368 | CRASH(); | |
369 | } | |
370 | int rc = devctl(fd, DCMD_PROC_TIDSTATUS, ®s, sizeof(regs), 0); | |
371 | if (rc != EOK) { | |
372 | LOG_ERROR("devctl(DCMD_PROC_TIDSTATUS) failed (error: %d)", rc); | |
6fe7ccc8 A |
373 | CRASH(); |
374 | } | |
6fe7ccc8 | 375 | close(fd); |
93a37866 | 376 | return sizeof(struct _debug_thread_info); |
14957cd0 A |
377 | #elif USE(PTHREADS) |
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); | |
382 | #else | |
383 | // FIXME: this function is non-portable; other POSIX systems may have different np alternatives | |
384 | pthread_getattr_np(platformThread, ®s); | |
385 | #endif | |
386 | return 0; | |
387 | #else | |
388 | #error Need a way to get thread registers on this platform | |
389 | #endif | |
390 | } | |
391 | ||
392 | static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) | |
393 | { | |
394 | #if OS(DARWIN) | |
395 | ||
396 | #if __DARWIN_UNIX03 | |
397 | ||
398 | #if CPU(X86) | |
399 | return reinterpret_cast<void*>(regs.__esp); | |
400 | #elif CPU(X86_64) | |
401 | return reinterpret_cast<void*>(regs.__rsp); | |
402 | #elif CPU(PPC) || CPU(PPC64) | |
403 | return reinterpret_cast<void*>(regs.__r1); | |
404 | #elif CPU(ARM) | |
405 | return reinterpret_cast<void*>(regs.__sp); | |
93a37866 A |
406 | #elif CPU(ARM64) |
407 | return reinterpret_cast<void*>(regs.__sp); | |
14957cd0 A |
408 | #else |
409 | #error Unknown Architecture | |
410 | #endif | |
411 | ||
412 | #else // !__DARWIN_UNIX03 | |
413 | ||
414 | #if CPU(X86) | |
415 | return reinterpret_cast<void*>(regs.esp); | |
416 | #elif CPU(X86_64) | |
417 | return reinterpret_cast<void*>(regs.rsp); | |
418 | #elif CPU(PPC) || CPU(PPC64) | |
419 | return reinterpret_cast<void*>(regs.r1); | |
420 | #else | |
421 | #error Unknown Architecture | |
422 | #endif | |
423 | ||
424 | #endif // __DARWIN_UNIX03 | |
425 | ||
426 | // end OS(DARWIN) | |
6fe7ccc8 A |
427 | #elif OS(WINDOWS) |
428 | ||
429 | #if CPU(ARM) | |
430 | return reinterpret_cast<void*>((uintptr_t) regs.Sp); | |
431 | #elif CPU(MIPS) | |
432 | return reinterpret_cast<void*>((uintptr_t) regs.IntSp); | |
433 | #elif CPU(X86) | |
14957cd0 | 434 | return reinterpret_cast<void*>((uintptr_t) regs.Esp); |
6fe7ccc8 | 435 | #elif CPU(X86_64) |
14957cd0 | 436 | return reinterpret_cast<void*>((uintptr_t) regs.Rsp); |
6fe7ccc8 A |
437 | #else |
438 | #error Unknown Architecture | |
439 | #endif | |
440 | ||
441 | #elif OS(QNX) | |
442 | return reinterpret_cast<void*>((uintptr_t) regs.sp); | |
443 | ||
14957cd0 A |
444 | #elif USE(PTHREADS) |
445 | void* stackBase = 0; | |
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. | |
449 | ASSERT(stackBase); | |
450 | return static_cast<char*>(stackBase) + stackSize; | |
451 | #else | |
452 | #error Need a way to get the stack pointer for another thread on this platform | |
453 | #endif | |
454 | } | |
455 | ||
456 | static void freePlatformThreadRegisters(PlatformThreadRegisters& regs) | |
457 | { | |
6fe7ccc8 | 458 | #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && !OS(QNX) |
14957cd0 A |
459 | pthread_attr_destroy(®s); |
460 | #else | |
461 | UNUSED_PARAM(regs); | |
462 | #endif | |
463 | } | |
464 | ||
465 | void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread) | |
466 | { | |
14957cd0 A |
467 | PlatformThreadRegisters regs; |
468 | size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs); | |
469 | ||
470 | conservativeRoots.add(static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize)); | |
471 | ||
472 | void* stackPointer = otherThreadStackPointer(regs); | |
473 | void* stackBase = thread->stackBase; | |
474 | swapIfBackwards(stackPointer, stackBase); | |
475 | conservativeRoots.add(stackPointer, stackBase); | |
476 | ||
14957cd0 A |
477 | freePlatformThreadRegisters(regs); |
478 | } | |
479 | ||
14957cd0 A |
480 | void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent) |
481 | { | |
482 | gatherFromCurrentThread(conservativeRoots, stackCurrent); | |
483 | ||
14957cd0 | 484 | if (m_threadSpecific) { |
6fe7ccc8 | 485 | PlatformThread currentPlatformThread = getCurrentPlatformThread(); |
14957cd0 A |
486 | |
487 | MutexLocker lock(m_registeredThreadsMutex); | |
488 | ||
489 | #ifndef NDEBUG | |
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. | |
493 | fastMallocForbid(); | |
494 | #endif | |
93a37866 A |
495 | for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { |
496 | if (!equalThread(thread->platformThread, currentPlatformThread)) | |
497 | suspendThread(thread->platformThread); | |
498 | } | |
499 | ||
14957cd0 A |
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) { | |
6fe7ccc8 | 503 | if (!equalThread(thread->platformThread, currentPlatformThread)) |
14957cd0 A |
504 | gatherFromOtherThread(conservativeRoots, thread); |
505 | } | |
93a37866 A |
506 | |
507 | for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { | |
508 | if (!equalThread(thread->platformThread, currentPlatformThread)) | |
509 | resumeThread(thread->platformThread); | |
510 | } | |
511 | ||
14957cd0 A |
512 | #ifndef NDEBUG |
513 | fastMallocAllow(); | |
514 | #endif | |
515 | } | |
14957cd0 A |
516 | } |
517 | ||
518 | } // namespace JSC |