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