OSDN Git Service

2006-10-16 Tobias Burnus <burnus@net-b.de>
[pf3gnuchains/gcc-fork.git] / boehm-gc / darwin_stop_world.c
1 #include "private/pthread_support.h"
2
3 /* This probably needs more porting work to ppc64. */
4
5 # if defined(GC_DARWIN_THREADS)
6
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."
13    
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."
16 */
17 #if defined(__ppc__)
18 # define PPC_RED_ZONE_SIZE 224
19 #elif defined(__ppc64__)
20 # define PPC_RED_ZONE_SIZE 320
21 #endif
22
23 /* Try to work out the right way to access thread state structure members.
24    The structure has changed its definition in different Darwin versions.  */
25 #if defined(__ppc__)
26 # define THREAD_STATE ppc_thread_state_t
27 # if defined (HAS_PPC_THREAD_STATE_R0)
28 #  define THREAD_FLD(x) x
29 # elif defined (HAS_PPC_THREAD_STATE___R0)
30 #  define THREAD_FLD(x) __ ## x
31 # else
32 #  error can not work out how to access fields of ppc_thread_state_t
33 # endif
34 #elif defined(__ppc64__)
35 # define THREAD_STATE ppc_thread_state64_t
36 # if defined (HAS_PPC_THREAD_STATE64_R0)
37 #  define THREAD_FLD(x) x
38 # elif defined (HAS_PPC_THREAD_STATE64___R0)
39 #  define THREAD_FLD(x) __ ## x
40 # else
41 #  error can not work out how to access fields of ppc_thread_state64_t
42 # endif
43 #elif defined(__i386__)
44 # define THREAD_STATE i386_thread_state_t
45 # if defined (HAS_I386_THREAD_STATE_EAX)
46 #  define THREAD_FLD(x) x
47 # elif defined (HAS_I386_THREAD_STATE___EAX)
48 #  define THREAD_FLD(x) __ ## x
49 # else
50 #  error can not work out how to access fields of i386_thread_state_t
51 # endif
52 #else
53 # error unknown architecture
54 #endif
55
56 typedef struct StackFrame {
57   unsigned long savedSP;
58   unsigned long savedCR;
59   unsigned long savedLR;
60   unsigned long reserved[2];
61   unsigned long savedRTOC;
62 } StackFrame;
63
64 unsigned long FindTopOfStack(unsigned long stack_start) {
65   StackFrame    *frame;
66   
67   if (stack_start == 0) {
68 # ifdef POWERPC
69 #   if CPP_WORDSZ == 32
70       __asm__ volatile("lwz     %0,0(r1)" : "=r" (frame));
71 #   else
72       __asm__ volatile("ld      %0,0(r1)" : "=r" (frame));
73 #   endif
74 # endif
75   } else {
76     frame = (StackFrame *)stack_start;
77   }
78
79 # ifdef DEBUG_THREADS
80     /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
81 # endif
82   do {
83     if (frame->savedSP == 0) break;
84                 /* if there are no more stack frames, stop */
85
86     frame = (StackFrame*)frame->savedSP;
87
88     /* we do these next two checks after going to the next frame
89        because the LR for the first stack frame in the loop
90        is not set up on purpose, so we shouldn't check it. */
91     if ((frame->savedLR & ~3) == 0) break; /* if the next LR is bogus, stop */
92     if ((~(frame->savedLR) & ~3) == 0) break; /* ditto */
93   } while (1); 
94
95 # ifdef DEBUG_THREADS
96     /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
97 # endif
98
99   return (unsigned long)frame;
100 }       
101
102 #ifdef DARWIN_DONT_PARSE_STACK
103 void GC_push_all_stacks() {
104   int i;
105   kern_return_t r;
106   GC_thread p;
107   pthread_t me;
108   ptr_t lo, hi;
109   THREAD_STATE state;
110   mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
111   
112   me = pthread_self();
113   if (!GC_thr_initialized) GC_thr_init();
114   
115   for(i=0;i<THREAD_TABLE_SZ;i++) {
116     for(p=GC_threads[i];p!=0;p=p->next) {
117       if(p -> flags & FINISHED) continue;
118       if(pthread_equal(p->id,me)) {
119         lo = GC_approx_sp();
120       } else {
121         /* Get the thread state (registers, etc) */
122         r = thread_get_state(
123                              p->stop_info.mach_thread,
124                              MACHINE_THREAD_STATE,
125                              (natural_t*)&state,
126                              &thread_state_count);
127         if(r != KERN_SUCCESS) ABORT("thread_get_state failed");
128
129 #if defined(I386)
130         lo = state.esp;
131
132         GC_push_one(state.eax); 
133         GC_push_one(state.ebx); 
134         GC_push_one(state.ecx); 
135         GC_push_one(state.edx); 
136         GC_push_one(state.edi); 
137         GC_push_one(state.esi); 
138         GC_push_one(state.ebp); 
139 #elif defined(POWERPC)
140         lo = (void*)(state . THREAD_FLD (r1) - PPC_RED_ZONE_SIZE);
141         
142         GC_push_one(state . THREAD_FLD (r0)); 
143         GC_push_one(state . THREAD_FLD (r2)); 
144         GC_push_one(state . THREAD_FLD (r3)); 
145         GC_push_one(state . THREAD_FLD (r4)); 
146         GC_push_one(state . THREAD_FLD (r5)); 
147         GC_push_one(state . THREAD_FLD (r6)); 
148         GC_push_one(state . THREAD_FLD (r7)); 
149         GC_push_one(state . THREAD_FLD (r8)); 
150         GC_push_one(state . THREAD_FLD (r9)); 
151         GC_push_one(state . THREAD_FLD (r10)); 
152         GC_push_one(state . THREAD_FLD (r11)); 
153         GC_push_one(state . THREAD_FLD (r12)); 
154         GC_push_one(state . THREAD_FLD (r13)); 
155         GC_push_one(state . THREAD_FLD (r14)); 
156         GC_push_one(state . THREAD_FLD (r15)); 
157         GC_push_one(state . THREAD_FLD (r16)); 
158         GC_push_one(state . THREAD_FLD (r17)); 
159         GC_push_one(state . THREAD_FLD (r18)); 
160         GC_push_one(state . THREAD_FLD (r19)); 
161         GC_push_one(state . THREAD_FLD (r20)); 
162         GC_push_one(state . THREAD_FLD (r21)); 
163         GC_push_one(state . THREAD_FLD (r22)); 
164         GC_push_one(state . THREAD_FLD (r23)); 
165         GC_push_one(state . THREAD_FLD (r24)); 
166         GC_push_one(state . THREAD_FLD (r25)); 
167         GC_push_one(state . THREAD_FLD (r26)); 
168         GC_push_one(state . THREAD_FLD (r27)); 
169         GC_push_one(state . THREAD_FLD (r28)); 
170         GC_push_one(state . THREAD_FLD (r29)); 
171         GC_push_one(state . THREAD_FLD (r30)); 
172         GC_push_one(state . THREAD_FLD (r31));
173 #else
174 # error FIXME for non-x86 || ppc architectures
175 #endif
176       } /* p != me */
177       if(p->flags & MAIN_THREAD)
178         hi = GC_stackbottom;
179       else
180         hi = p->stack_end;
181 #if DEBUG_THREADS
182       GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
183                  (unsigned long) p -> id,
184                  (unsigned long) lo,
185                  (unsigned long) hi
186                  );
187 #endif
188       GC_push_all_stack(lo,hi);
189     } /* for(p=GC_threads[i]...) */
190   } /* for(i=0;i<THREAD_TABLE_SZ...) */
191 }
192
193 #else /* !DARWIN_DONT_PARSE_STACK; Use FindTopOfStack() */
194
195 void GC_push_all_stacks() {
196     int i;
197     kern_return_t r;
198     mach_port_t me;
199     ptr_t lo, hi;
200     thread_act_array_t act_list = 0;
201     mach_msg_type_number_t listcount = 0;
202
203     me = mach_thread_self();
204     if (!GC_thr_initialized) GC_thr_init();
205     
206     r = task_threads(current_task(), &act_list, &listcount);
207     if(r != KERN_SUCCESS) ABORT("task_threads failed");
208     for(i = 0; i < listcount; i++) {
209       thread_act_t thread = act_list[i];
210       if (thread == me) {
211         lo = GC_approx_sp();
212         hi = (ptr_t)FindTopOfStack(0);
213       } else {
214 #     if defined(__ppc__) || defined(__ppc64__)
215         THREAD_STATE info;
216         mach_msg_type_number_t outCount = THREAD_STATE_MAX;
217         r = thread_get_state(thread, MACHINE_THREAD_STATE,
218                              (natural_t *)&info, &outCount);
219         if(r != KERN_SUCCESS) ABORT("task_get_state failed");
220
221         lo = (void*)(info . THREAD_FLD (r1) - PPC_RED_ZONE_SIZE);
222         hi = (ptr_t)FindTopOfStack(info . THREAD_FLD (r1));
223
224         GC_push_one(info . THREAD_FLD (r0)); 
225         GC_push_one(info . THREAD_FLD (r2)); 
226         GC_push_one(info . THREAD_FLD (r3)); 
227         GC_push_one(info . THREAD_FLD (r4)); 
228         GC_push_one(info . THREAD_FLD (r5)); 
229         GC_push_one(info . THREAD_FLD (r6)); 
230         GC_push_one(info . THREAD_FLD (r7)); 
231         GC_push_one(info . THREAD_FLD (r8)); 
232         GC_push_one(info . THREAD_FLD (r9)); 
233         GC_push_one(info . THREAD_FLD (r10)); 
234         GC_push_one(info . THREAD_FLD (r11)); 
235         GC_push_one(info . THREAD_FLD (r12)); 
236         GC_push_one(info . THREAD_FLD (r13)); 
237         GC_push_one(info . THREAD_FLD (r14)); 
238         GC_push_one(info . THREAD_FLD (r15)); 
239         GC_push_one(info . THREAD_FLD (r16)); 
240         GC_push_one(info . THREAD_FLD (r17)); 
241         GC_push_one(info . THREAD_FLD (r18)); 
242         GC_push_one(info . THREAD_FLD (r19)); 
243         GC_push_one(info . THREAD_FLD (r20)); 
244         GC_push_one(info . THREAD_FLD (r21)); 
245         GC_push_one(info . THREAD_FLD (r22)); 
246         GC_push_one(info . THREAD_FLD (r23)); 
247         GC_push_one(info . THREAD_FLD (r24)); 
248         GC_push_one(info . THREAD_FLD (r25)); 
249         GC_push_one(info . THREAD_FLD (r26)); 
250         GC_push_one(info . THREAD_FLD (r27)); 
251         GC_push_one(info . THREAD_FLD (r28)); 
252         GC_push_one(info . THREAD_FLD (r29)); 
253         GC_push_one(info . THREAD_FLD (r30)); 
254         GC_push_one(info . THREAD_FLD (r31));
255 #      else
256         /* FIXME: Remove after testing: */
257         WARN("This is completely untested and likely will not work\n", 0);
258         THREAD_STATE info;
259         mach_msg_type_number_t outCount = THREAD_STATE_MAX;
260         r = thread_get_state(thread, MACHINE_THREAD_STATE,
261                              (natural_t *)&info, &outCount);
262         if(r != KERN_SUCCESS) ABORT("task_get_state failed");
263
264         lo = (void*)info . THREAD_FLD (esp);
265         hi = (ptr_t)FindTopOfStack(info . THREAD_FLD (esp));
266
267         GC_push_one(info . THREAD_FLD (eax)); 
268         GC_push_one(info . THREAD_FLD (ebx)); 
269         GC_push_one(info . THREAD_FLD (ecx)); 
270         GC_push_one(info . THREAD_FLD (edx)); 
271         GC_push_one(info . THREAD_FLD (edi)); 
272         GC_push_one(info . THREAD_FLD (esi)); 
273         /* GC_push_one(info . THREAD_FLD (ebp));  */
274         /* GC_push_one(info . THREAD_FLD (esp));  */
275         GC_push_one(info . THREAD_FLD (ss)); 
276         GC_push_one(info . THREAD_FLD (eip)); 
277         GC_push_one(info . THREAD_FLD (cs)); 
278         GC_push_one(info . THREAD_FLD (ds)); 
279         GC_push_one(info . THREAD_FLD (es)); 
280         GC_push_one(info . THREAD_FLD (fs)); 
281         GC_push_one(info . THREAD_FLD (gs)); 
282 #      endif /* !POWERPC */
283       }
284 #     if DEBUG_THREADS
285        GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
286                   (unsigned long) thread,
287                   (unsigned long) lo,
288                   (unsigned long) hi
289                  );
290 #     endif
291       GC_push_all_stack(lo, hi); 
292     } /* for(p=GC_threads[i]...) */
293     vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
294 }
295 #endif /* !DARWIN_DONT_PARSE_STACK */
296
297 static mach_port_t GC_mach_handler_thread;
298 static int GC_use_mach_handler_thread = 0;
299
300 static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
301 static int GC_mach_threads_count;
302
303 void GC_stop_init() {
304   int i;
305
306   for (i = 0; i < THREAD_TABLE_SZ; i++) {
307     GC_mach_threads[i].thread = 0;
308     GC_mach_threads[i].already_suspended = 0;
309   }
310   GC_mach_threads_count = 0;
311 }
312
313 /* returns true if there's a thread in act_list that wasn't in old_list */
314 int GC_suspend_thread_list(thread_act_array_t act_list, int count, 
315                            thread_act_array_t old_list, int old_count) {
316   mach_port_t my_thread = mach_thread_self();
317   int i, j;
318
319   int changed = 0;
320
321   for(i = 0; i < count; i++) {
322     thread_act_t thread = act_list[i];
323 #   if DEBUG_THREADS 
324       GC_printf1("Attempting to suspend thread %p\n", thread);
325 #   endif
326     /* find the current thread in the old list */
327     int found = 0;
328     for(j = 0; j < old_count; j++) {
329       thread_act_t old_thread = old_list[j];
330       if (old_thread == thread) {
331         found = 1;
332         break;
333       }
334     }
335     if (!found) {
336       /* add it to the GC_mach_threads list */
337       GC_mach_threads[GC_mach_threads_count].thread = thread;
338       /* default is not suspended */
339       GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
340       changed = 1;
341     }      
342
343     if (thread != my_thread &&
344         (!GC_use_mach_handler_thread
345          || (GC_use_mach_handler_thread
346              && GC_mach_handler_thread != thread))) {
347       struct thread_basic_info info;
348       mach_msg_type_number_t outCount = THREAD_INFO_MAX;
349       kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
350                                 (thread_info_t)&info, &outCount);
351       if(kern_result != KERN_SUCCESS) {
352         /* the thread may have quit since the thread_threads () call 
353          * we mark already_suspended so it's not dealt with anymore later
354          */
355         if (!found) {
356           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
357           GC_mach_threads_count++;
358         }
359         continue;
360       }
361 #     if DEBUG_THREADS
362         GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
363 #     endif
364       if (!found) {
365         GC_mach_threads[GC_mach_threads_count].already_suspended = info.suspend_count;
366       }
367       if (info.suspend_count) continue;
368       
369 #     if DEBUG_THREADS
370         GC_printf1("Suspending 0x%lx\n", thread);
371 #     endif
372       /* Suspend the thread */
373       kern_result = thread_suspend(thread);
374       if(kern_result != KERN_SUCCESS) {
375         /* the thread may have quit since the thread_threads () call 
376          * we mark already_suspended so it's not dealt with anymore later
377          */
378         if (!found) {
379           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
380           GC_mach_threads_count++;
381         }
382         continue;
383       }
384     } 
385     if (!found) GC_mach_threads_count++;
386   }
387   return changed;
388 }
389
390
391 /* Caller holds allocation lock.        */
392 void GC_stop_world()
393 {
394   int i, changes;
395     GC_thread p;
396     mach_port_t my_thread = mach_thread_self();
397     kern_return_t kern_result;
398     thread_act_array_t act_list, prev_list;
399     mach_msg_type_number_t listcount, prevcount;
400     
401 #   if DEBUG_THREADS
402       GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
403 #   endif
404
405     /* clear out the mach threads list table */
406     GC_stop_init(); 
407        
408     /* Make sure all free list construction has stopped before we start. */
409     /* No new construction can start, since free list construction is   */
410     /* required to acquire and release the GC lock before it starts,    */
411     /* and we have the lock.                                            */
412 #   ifdef PARALLEL_MARK
413       GC_acquire_mark_lock();
414       GC_ASSERT(GC_fl_builder_count == 0);
415       /* We should have previously waited for it to become zero. */
416 #   endif /* PARALLEL_MARK */
417
418       /* Loop stopping threads until you have gone over the whole list
419          twice without a new one appearing. thread_create() won't
420          return (and thus the thread stop) until the new thread
421          exists, so there is no window whereby you could stop a
422          thread, recognise it is stopped, but then have a new thread
423          it created before stopping show up later.
424       */
425       
426       changes = 1;
427       prev_list = NULL;
428       prevcount = 0;
429       do {
430         int result;
431         kern_result = task_threads(current_task(), &act_list, &listcount);
432         result = GC_suspend_thread_list(act_list, listcount,
433                                         prev_list, prevcount);
434         changes = result;
435         prev_list = act_list;
436         prevcount = listcount;
437         vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
438       } while (changes);
439       
440  
441 #   ifdef MPROTECT_VDB
442       if(GC_incremental) {
443         extern void GC_mprotect_stop();
444         GC_mprotect_stop();
445       }
446 #   endif
447     
448 #   ifdef PARALLEL_MARK
449       GC_release_mark_lock();
450 #   endif
451     #if DEBUG_THREADS
452       GC_printf1("World stopped from 0x%lx\n", my_thread);
453     #endif
454 }
455
456 /* Caller holds allocation lock, and has held it continuously since     */
457 /* the world stopped.                                                   */
458 void GC_start_world()
459 {
460   mach_port_t my_thread = mach_thread_self();
461   int i, j;
462   GC_thread p;
463   kern_return_t kern_result;
464   thread_act_array_t act_list;
465   mach_msg_type_number_t listcount;
466   struct thread_basic_info info;
467   mach_msg_type_number_t outCount = THREAD_INFO_MAX;
468   
469 #   if DEBUG_THREADS
470       GC_printf0("World starting\n");
471 #   endif
472
473 #   ifdef MPROTECT_VDB
474       if(GC_incremental) {
475         extern void GC_mprotect_resume();
476         GC_mprotect_resume();
477       }
478 #   endif
479
480     kern_result = task_threads(current_task(), &act_list, &listcount);
481     for(i = 0; i < listcount; i++) {
482       thread_act_t thread = act_list[i];
483       if (thread != my_thread &&
484           (!GC_use_mach_handler_thread ||
485            (GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
486         for(j = 0; j < GC_mach_threads_count; j++) {
487           if (thread == GC_mach_threads[j].thread) {
488             if (GC_mach_threads[j].already_suspended) {
489 #             if DEBUG_THREADS
490                 GC_printf1("Not resuming already suspended thread %p\n", thread);
491 #             endif
492               continue;
493             }
494             kern_result = thread_info(thread, THREAD_BASIC_INFO,
495                                       (thread_info_t)&info, &outCount);
496             if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
497 #           if DEBUG_THREADS
498               GC_printf2("Thread state for 0x%lx = %d\n", thread,
499                          info.run_state);
500               GC_printf1("Resuming 0x%lx\n", thread);
501 #           endif
502             /* Resume the thread */
503             kern_result = thread_resume(thread);
504             if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
505           } 
506         }
507       }
508     }
509     vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
510 #   if DEBUG_THREADS
511      GC_printf0("World started\n");
512 #   endif
513 }
514
515 void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
516   GC_mach_handler_thread = thread;
517   GC_use_mach_handler_thread = 1;
518 }
519
520 #endif