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