2006-04-21 Paul Brook <paul@codesourcery.com>
[official-gcc.git] / boehm-gc / darwin_stop_world.c
blobc2a033d3cc44d2c9e547d39558676bcbc8ff37d2
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
8 Page 49:
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
12 added to the stack."
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."
17 #ifdef POWERPC
18 # if CPP_WORDSZ == 32
19 # define PPC_RED_ZONE_SIZE 224
20 # elif CPP_WORDSZ == 64
21 # define PPC_RED_ZONE_SIZE 320
22 # endif
23 #endif
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;
31 } StackFrame;
33 unsigned long FindTopOfStack(unsigned int stack_start) {
34 StackFrame *frame;
36 if (stack_start == 0) {
37 # ifdef POWERPC
38 # if CPP_WORDSZ == 32
39 __asm__ volatile("lwz %0,0(r1)" : "=r" (frame));
40 # else
41 __asm__ volatile("ld %0,0(r1)" : "=r" (frame));
42 # endif
43 # endif
44 } else {
45 frame = (StackFrame *)stack_start;
48 # ifdef DEBUG_THREADS
49 /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
50 # endif
51 do {
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 */
62 } while (1);
64 # ifdef DEBUG_THREADS
65 /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
66 # endif
68 return (unsigned long)frame;
71 #ifdef DARWIN_DONT_PARSE_STACK
72 void GC_push_all_stacks() {
73 int i;
74 kern_return_t r;
75 GC_thread p;
76 pthread_t me;
77 ptr_t lo, hi;
78 ppc_thread_state_t state;
79 mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
81 me = pthread_self();
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)) {
88 lo = GC_approx_sp();
89 } else {
90 /* Get the thread state (registers, etc) */
91 r = thread_get_state(
92 p->stop_info.mach_thread,
93 MACHINE_THREAD_STATE,
94 (natural_t*)&state,
95 &thread_state_count);
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);
131 } /* p != me */
132 if(p->flags & MAIN_THREAD)
133 hi = GC_stackbottom;
134 else
135 hi = p->stack_end;
136 #if DEBUG_THREADS
137 GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
138 (unsigned long) p -> id,
139 (unsigned long) lo,
140 (unsigned long) hi
142 #endif
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() {
151 int i;
152 kern_return_t r;
153 mach_port_t me;
154 ptr_t lo, hi;
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];
165 if (thread == me) {
166 lo = GC_approx_sp();
167 hi = (ptr_t)FindTopOfStack(0);
168 } else {
169 # if defined(POWERPC)
170 # if CPP_WORDSZ == 32
171 ppc_thread_state_t info;
172 # else
173 ppc_thread_state64_t info;
174 # endif
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);
214 # else
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 */
243 # if DEBUG_THREADS
244 GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
245 (unsigned long) thread,
246 (unsigned long) lo,
247 (unsigned long) hi
249 # endif
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() {
263 int i;
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();
276 int i, j;
278 int changed = 0;
280 for(i = 0; i < count; i++) {
281 thread_act_t thread = act_list[i];
282 # if DEBUG_THREADS
283 GC_printf1("Attempting to suspend thread %p\n", thread);
284 # endif
285 /* find the current thread in the old list */
286 int found = 0;
287 for(j = 0; j < old_count; j++) {
288 thread_act_t old_thread = old_list[j];
289 if (old_thread == thread) {
290 found = 1;
291 break;
294 if (!found) {
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;
299 changed = 1;
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
314 if (!found) {
315 GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
316 GC_mach_threads_count++;
318 continue;
320 # if DEBUG_THREADS
321 GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
322 # endif
323 if (!found) {
324 GC_mach_threads[GC_mach_threads_count].already_suspended = info.suspend_count;
326 if (info.suspend_count) continue;
328 # if DEBUG_THREADS
329 GC_printf1("Suspending 0x%lx\n", thread);
330 # endif
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
337 if (!found) {
338 GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
339 GC_mach_threads_count++;
341 continue;
344 if (!found) GC_mach_threads_count++;
346 return changed;
350 /* Caller holds allocation lock. */
351 void GC_stop_world()
353 int i, changes;
354 GC_thread p;
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;
360 # if DEBUG_THREADS
361 GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
362 # endif
364 /* clear out the mach threads list table */
365 GC_stop_init();
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.
385 changes = 1;
386 prev_list = NULL;
387 prevcount = 0;
388 do {
389 int result;
390 kern_result = task_threads(current_task(), &act_list, &listcount);
391 result = GC_suspend_thread_list(act_list, listcount,
392 prev_list, prevcount);
393 changes = result;
394 prev_list = act_list;
395 prevcount = listcount;
396 vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
397 } while (changes);
400 # ifdef MPROTECT_VDB
401 if(GC_incremental) {
402 extern void GC_mprotect_stop();
403 GC_mprotect_stop();
405 # endif
407 # ifdef PARALLEL_MARK
408 GC_release_mark_lock();
409 # endif
410 #if DEBUG_THREADS
411 GC_printf1("World stopped from 0x%lx\n", my_thread);
412 #endif
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();
420 int i, j;
421 GC_thread p;
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;
428 # if DEBUG_THREADS
429 GC_printf0("World starting\n");
430 # endif
432 # ifdef MPROTECT_VDB
433 if(GC_incremental) {
434 extern void GC_mprotect_resume();
435 GC_mprotect_resume();
437 # endif
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) {
448 # if DEBUG_THREADS
449 GC_printf1("Not resuming already suspended thread %p\n", thread);
450 # endif
451 continue;
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");
456 # if DEBUG_THREADS
457 GC_printf2("Thread state for 0x%lx = %d\n", thread,
458 info.run_state);
459 GC_printf1("Resuming 0x%lx\n", thread);
460 # endif
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);
469 # if DEBUG_THREADS
470 GC_printf0("World started\n");
471 # endif
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;
479 #endif