1 #include "private/pthread_support.h"
3 /* This probably needs more porting work to ppc64. */
5 # if defined(GC_DARWIN_THREADS)
7 /* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
9 "The space beneath the stack pointer, where a new stack frame would normally
10 be allocated, is called the red zone. This area as shown in Figure 3-2 may
11 be used for any purpose as long as a new stack frame does not need to be
14 Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
15 it must set up a stack frame just like routines that call other routines."
19 # define PPC_RED_ZONE_SIZE 224
20 # elif CPP_WORDSZ == 64
21 # define PPC_RED_ZONE_SIZE 320
25 typedef struct StackFrame
{
26 unsigned long savedSP
;
27 unsigned long savedCR
;
28 unsigned long savedLR
;
29 unsigned long reserved
[2];
30 unsigned long savedRTOC
;
33 unsigned long FindTopOfStack(unsigned int stack_start
) {
36 if (stack_start
== 0) {
39 __asm__
volatile("lwz %0,0(r1)" : "=r" (frame
));
41 __asm__
volatile("ld %0,0(r1)" : "=r" (frame
));
45 frame
= (StackFrame
*)stack_start
;
49 /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
52 if (frame
->savedSP
== 0) break;
53 /* if there are no more stack frames, stop */
55 frame
= (StackFrame
*)frame
->savedSP
;
57 /* we do these next two checks after going to the next frame
58 because the LR for the first stack frame in the loop
59 is not set up on purpose, so we shouldn't check it. */
60 if ((frame
->savedLR
& ~3) == 0) break; /* if the next LR is bogus, stop */
61 if ((~(frame
->savedLR
) & ~3) == 0) break; /* ditto */
65 /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
68 return (unsigned long)frame
;
71 #ifdef DARWIN_DONT_PARSE_STACK
72 void GC_push_all_stacks() {
78 ppc_thread_state_t state
;
79 mach_msg_type_number_t thread_state_count
= MACHINE_THREAD_STATE_COUNT
;
82 if (!GC_thr_initialized
) GC_thr_init();
84 for(i
=0;i
<THREAD_TABLE_SZ
;i
++) {
85 for(p
=GC_threads
[i
];p
!=0;p
=p
->next
) {
86 if(p
-> flags
& FINISHED
) continue;
87 if(pthread_equal(p
->id
,me
)) {
90 /* Get the thread state (registers, etc) */
92 p
->stop_info
.mach_thread
,
96 if(r
!= KERN_SUCCESS
) ABORT("thread_get_state failed");
98 lo
= (void*)(state
.r1
- PPC_RED_ZONE_SIZE
);
100 GC_push_one(state
.r0
);
101 GC_push_one(state
.r2
);
102 GC_push_one(state
.r3
);
103 GC_push_one(state
.r4
);
104 GC_push_one(state
.r5
);
105 GC_push_one(state
.r6
);
106 GC_push_one(state
.r7
);
107 GC_push_one(state
.r8
);
108 GC_push_one(state
.r9
);
109 GC_push_one(state
.r10
);
110 GC_push_one(state
.r11
);
111 GC_push_one(state
.r12
);
112 GC_push_one(state
.r13
);
113 GC_push_one(state
.r14
);
114 GC_push_one(state
.r15
);
115 GC_push_one(state
.r16
);
116 GC_push_one(state
.r17
);
117 GC_push_one(state
.r18
);
118 GC_push_one(state
.r19
);
119 GC_push_one(state
.r20
);
120 GC_push_one(state
.r21
);
121 GC_push_one(state
.r22
);
122 GC_push_one(state
.r23
);
123 GC_push_one(state
.r24
);
124 GC_push_one(state
.r25
);
125 GC_push_one(state
.r26
);
126 GC_push_one(state
.r27
);
127 GC_push_one(state
.r28
);
128 GC_push_one(state
.r29
);
129 GC_push_one(state
.r30
);
130 GC_push_one(state
.r31
);
132 if(p
->flags
& MAIN_THREAD
)
137 GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
138 (unsigned long) p
-> id
,
143 GC_push_all_stack(lo
,hi
);
144 } /* for(p=GC_threads[i]...) */
145 } /* for(i=0;i<THREAD_TABLE_SZ...) */
148 #else /* !DARWIN_DONT_PARSE_STACK; Use FindTopOfStack() */
150 void GC_push_all_stacks() {
155 thread_act_array_t act_list
= 0;
156 mach_msg_type_number_t listcount
= 0;
158 me
= mach_thread_self();
159 if (!GC_thr_initialized
) GC_thr_init();
161 r
= task_threads(current_task(), &act_list
, &listcount
);
162 if(r
!= KERN_SUCCESS
) ABORT("task_threads failed");
163 for(i
= 0; i
< listcount
; i
++) {
164 thread_act_t thread
= act_list
[i
];
167 hi
= (ptr_t
)FindTopOfStack(0);
169 # if defined(POWERPC)
170 # if CPP_WORDSZ == 32
171 ppc_thread_state_t info
;
173 ppc_thread_state64_t info
;
175 mach_msg_type_number_t outCount
= THREAD_STATE_MAX
;
176 r
= thread_get_state(thread
, MACHINE_THREAD_STATE
,
177 (natural_t
*)&info
, &outCount
);
178 if(r
!= KERN_SUCCESS
) ABORT("task_get_state failed");
180 lo
= (void*)(info
.r1
- PPC_RED_ZONE_SIZE
);
181 hi
= (ptr_t
)FindTopOfStack(info
.r1
);
183 GC_push_one(info
.r0
);
184 GC_push_one(info
.r2
);
185 GC_push_one(info
.r3
);
186 GC_push_one(info
.r4
);
187 GC_push_one(info
.r5
);
188 GC_push_one(info
.r6
);
189 GC_push_one(info
.r7
);
190 GC_push_one(info
.r8
);
191 GC_push_one(info
.r9
);
192 GC_push_one(info
.r10
);
193 GC_push_one(info
.r11
);
194 GC_push_one(info
.r12
);
195 GC_push_one(info
.r13
);
196 GC_push_one(info
.r14
);
197 GC_push_one(info
.r15
);
198 GC_push_one(info
.r16
);
199 GC_push_one(info
.r17
);
200 GC_push_one(info
.r18
);
201 GC_push_one(info
.r19
);
202 GC_push_one(info
.r20
);
203 GC_push_one(info
.r21
);
204 GC_push_one(info
.r22
);
205 GC_push_one(info
.r23
);
206 GC_push_one(info
.r24
);
207 GC_push_one(info
.r25
);
208 GC_push_one(info
.r26
);
209 GC_push_one(info
.r27
);
210 GC_push_one(info
.r28
);
211 GC_push_one(info
.r29
);
212 GC_push_one(info
.r30
);
213 GC_push_one(info
.r31
);
215 /* FIXME: Remove after testing: */
216 WARN("This is completely untested and likely will not work\n", 0);
217 i386_thread_state_t info
;
218 mach_msg_type_number_t outCount
= THREAD_STATE_MAX
;
219 r
= thread_get_state(thread
, MACHINE_THREAD_STATE
,
220 (natural_t
*)&info
, &outCount
);
221 if(r
!= KERN_SUCCESS
) ABORT("task_get_state failed");
223 lo
= (void*)info
.esp
;
224 hi
= (ptr_t
)FindTopOfStack(info
.esp
);
226 GC_push_one(info
.eax
);
227 GC_push_one(info
.ebx
);
228 GC_push_one(info
.ecx
);
229 GC_push_one(info
.edx
);
230 GC_push_one(info
.edi
);
231 GC_push_one(info
.esi
);
232 /* GC_push_one(info.ebp); */
233 /* GC_push_one(info.esp); */
234 GC_push_one(info
.ss
);
235 GC_push_one(info
.eip
);
236 GC_push_one(info
.cs
);
237 GC_push_one(info
.ds
);
238 GC_push_one(info
.es
);
239 GC_push_one(info
.fs
);
240 GC_push_one(info
.gs
);
241 # endif /* !POWERPC */
244 GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
245 (unsigned long) thread
,
250 GC_push_all_stack(lo
, hi
);
251 } /* for(p=GC_threads[i]...) */
252 vm_deallocate(current_task(), (vm_address_t
)act_list
, sizeof(thread_t
) * listcount
);
254 #endif /* !DARWIN_DONT_PARSE_STACK */
256 static mach_port_t GC_mach_handler_thread
;
257 static int GC_use_mach_handler_thread
= 0;
259 static struct GC_mach_thread GC_mach_threads
[THREAD_TABLE_SZ
];
260 static int GC_mach_threads_count
;
262 void GC_stop_init() {
265 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
266 GC_mach_threads
[i
].thread
= 0;
267 GC_mach_threads
[i
].already_suspended
= 0;
269 GC_mach_threads_count
= 0;
272 /* returns true if there's a thread in act_list that wasn't in old_list */
273 int GC_suspend_thread_list(thread_act_array_t act_list
, int count
,
274 thread_act_array_t old_list
, int old_count
) {
275 mach_port_t my_thread
= mach_thread_self();
280 for(i
= 0; i
< count
; i
++) {
281 thread_act_t thread
= act_list
[i
];
283 GC_printf1("Attempting to suspend thread %p\n", thread
);
285 /* find the current thread in the old list */
287 for(j
= 0; j
< old_count
; j
++) {
288 thread_act_t old_thread
= old_list
[j
];
289 if (old_thread
== thread
) {
295 /* add it to the GC_mach_threads list */
296 GC_mach_threads
[GC_mach_threads_count
].thread
= thread
;
297 /* default is not suspended */
298 GC_mach_threads
[GC_mach_threads_count
].already_suspended
= 0;
302 if (thread
!= my_thread
&&
303 (!GC_use_mach_handler_thread
304 || (GC_use_mach_handler_thread
305 && GC_mach_handler_thread
!= thread
))) {
306 struct thread_basic_info info
;
307 mach_msg_type_number_t outCount
= THREAD_INFO_MAX
;
308 kern_return_t kern_result
= thread_info(thread
, THREAD_BASIC_INFO
,
309 (thread_info_t
)&info
, &outCount
);
310 if(kern_result
!= KERN_SUCCESS
) {
311 /* the thread may have quit since the thread_threads () call
312 * we mark already_suspended so it's not dealt with anymore later
315 GC_mach_threads
[GC_mach_threads_count
].already_suspended
= TRUE
;
316 GC_mach_threads_count
++;
321 GC_printf2("Thread state for 0x%lx = %d\n", thread
, info
.run_state
);
324 GC_mach_threads
[GC_mach_threads_count
].already_suspended
= info
.suspend_count
;
326 if (info
.suspend_count
) continue;
329 GC_printf1("Suspending 0x%lx\n", thread
);
331 /* Suspend the thread */
332 kern_result
= thread_suspend(thread
);
333 if(kern_result
!= KERN_SUCCESS
) {
334 /* the thread may have quit since the thread_threads () call
335 * we mark already_suspended so it's not dealt with anymore later
338 GC_mach_threads
[GC_mach_threads_count
].already_suspended
= TRUE
;
339 GC_mach_threads_count
++;
344 if (!found
) GC_mach_threads_count
++;
350 /* Caller holds allocation lock. */
355 mach_port_t my_thread
= mach_thread_self();
356 kern_return_t kern_result
;
357 thread_act_array_t act_list
, prev_list
;
358 mach_msg_type_number_t listcount
, prevcount
;
361 GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
364 /* clear out the mach threads list table */
367 /* Make sure all free list construction has stopped before we start. */
368 /* No new construction can start, since free list construction is */
369 /* required to acquire and release the GC lock before it starts, */
370 /* and we have the lock. */
371 # ifdef PARALLEL_MARK
372 GC_acquire_mark_lock();
373 GC_ASSERT(GC_fl_builder_count
== 0);
374 /* We should have previously waited for it to become zero. */
375 # endif /* PARALLEL_MARK */
377 /* Loop stopping threads until you have gone over the whole list
378 twice without a new one appearing. thread_create() won't
379 return (and thus the thread stop) until the new thread
380 exists, so there is no window whereby you could stop a
381 thread, recognise it is stopped, but then have a new thread
382 it created before stopping show up later.
390 kern_result
= task_threads(current_task(), &act_list
, &listcount
);
391 result
= GC_suspend_thread_list(act_list
, listcount
,
392 prev_list
, prevcount
);
394 prev_list
= act_list
;
395 prevcount
= listcount
;
396 vm_deallocate(current_task(), (vm_address_t
)act_list
, sizeof(thread_t
) * listcount
);
402 extern void GC_mprotect_stop();
407 # ifdef PARALLEL_MARK
408 GC_release_mark_lock();
411 GC_printf1("World stopped from 0x%lx\n", my_thread
);
415 /* Caller holds allocation lock, and has held it continuously since */
416 /* the world stopped. */
417 void GC_start_world()
419 mach_port_t my_thread
= mach_thread_self();
422 kern_return_t kern_result
;
423 thread_act_array_t act_list
;
424 mach_msg_type_number_t listcount
;
425 struct thread_basic_info info
;
426 mach_msg_type_number_t outCount
= THREAD_INFO_MAX
;
429 GC_printf0("World starting\n");
434 extern void GC_mprotect_resume();
435 GC_mprotect_resume();
439 kern_result
= task_threads(current_task(), &act_list
, &listcount
);
440 for(i
= 0; i
< listcount
; i
++) {
441 thread_act_t thread
= act_list
[i
];
442 if (thread
!= my_thread
&&
443 (!GC_use_mach_handler_thread
||
444 (GC_use_mach_handler_thread
&& GC_mach_handler_thread
!= thread
))) {
445 for(j
= 0; j
< GC_mach_threads_count
; j
++) {
446 if (thread
== GC_mach_threads
[j
].thread
) {
447 if (GC_mach_threads
[j
].already_suspended
) {
449 GC_printf1("Not resuming already suspended thread %p\n", thread
);
453 kern_result
= thread_info(thread
, THREAD_BASIC_INFO
,
454 (thread_info_t
)&info
, &outCount
);
455 if(kern_result
!= KERN_SUCCESS
) ABORT("thread_info failed");
457 GC_printf2("Thread state for 0x%lx = %d\n", thread
,
459 GC_printf1("Resuming 0x%lx\n", thread
);
461 /* Resume the thread */
462 kern_result
= thread_resume(thread
);
463 if(kern_result
!= KERN_SUCCESS
) ABORT("thread_resume failed");
468 vm_deallocate(current_task(), (vm_address_t
)act_list
, sizeof(thread_t
) * listcount
);
470 GC_printf0("World started\n");
474 void GC_darwin_register_mach_handler_thread(mach_port_t thread
) {
475 GC_mach_handler_thread
= thread
;
476 GC_use_mach_handler_thread
= 1;