OSDN Git Service

22c76a857332f2ea0c3596d7a08d061221afc66a
[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 #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
24
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;
32
33 unsigned long FindTopOfStack(unsigned int stack_start) {
34   StackFrame    *frame;
35   
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("ldz     %0,0(r1)" : "=r" (frame));
42 #   endif
43 # endif
44   } else {
45     frame = (StackFrame *)stack_start;
46   }
47
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 */
54
55     frame = (StackFrame*)frame->savedSP;
56
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); 
63
64 # ifdef DEBUG_THREADS
65     /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
66 # endif
67
68   return (unsigned long)frame;
69 }       
70
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;
80   
81   me = pthread_self();
82   if (!GC_thr_initialized) GC_thr_init();
83   
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");
97         
98         lo = (void*)(state.r1 - PPC_RED_ZONE_SIZE);
99         
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
141                  );
142 #endif
143       GC_push_all_stack(lo,hi);
144     } /* for(p=GC_threads[i]...) */
145   } /* for(i=0;i<THREAD_TABLE_SZ...) */
146 }
147
148 #else /* !DARWIN_DONT_PARSE_STACK; Use FindTopOfStack() */
149
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;
157
158     me = mach_thread_self();
159     if (!GC_thr_initialized) GC_thr_init();
160     
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");
179
180         lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
181         hi = (ptr_t)FindTopOfStack(info.r1);
182
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");
222
223         lo = (void*)info.esp;
224         hi = (ptr_t)FindTopOfStack(info.esp);
225
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 */
242       }
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
248                  );
249 #     endif
250       GC_push_all_stack(lo, hi); 
251     } /* for(p=GC_threads[i]...) */
252 }
253 #endif /* !DARWIN_DONT_PARSE_STACK */
254
255 static mach_port_t GC_mach_handler_thread;
256 static int GC_use_mach_handler_thread = 0;
257
258 static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
259 static int GC_mach_threads_count;
260
261 void GC_stop_init() {
262   int i;
263
264   for (i = 0; i < THREAD_TABLE_SZ; i++) {
265     GC_mach_threads[i].thread = 0;
266     GC_mach_threads[i].already_suspended = 0;
267   }
268   GC_mach_threads_count = 0;
269 }
270
271 /* returns true if there's a thread in act_list that wasn't in old_list */
272 int GC_suspend_thread_list(thread_act_array_t act_list, int count, 
273                            thread_act_array_t old_list, int old_count) {
274   mach_port_t my_thread = mach_thread_self();
275   int i, j;
276
277   int changed = 0;
278
279   for(i = 0; i < count; i++) {
280     thread_act_t thread = act_list[i];
281 #   if DEBUG_THREADS 
282       GC_printf1("Attempting to suspend thread %p\n", thread);
283 #   endif
284     /* find the current thread in the old list */
285     int found = 0;
286     for(j = 0; j < old_count; j++) {
287       thread_act_t old_thread = old_list[j];
288       if (old_thread == thread) {
289         found = 1;
290         break;
291       }
292     }
293     if (!found) {
294       /* add it to the GC_mach_threads list */
295       GC_mach_threads[GC_mach_threads_count].thread = thread;
296       /* default is not suspended */
297       GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
298       changed = 1;
299     }      
300
301     if (thread != my_thread &&
302         (!GC_use_mach_handler_thread
303          || (GC_use_mach_handler_thread
304              && GC_mach_handler_thread != thread))) {
305       struct thread_basic_info info;
306       mach_msg_type_number_t outCount = THREAD_INFO_MAX;
307       kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
308                                 (thread_info_t)&info, &outCount);
309       if(kern_result != KERN_SUCCESS) {
310         /* the thread may have quit since the thread_threads () call 
311          * we mark already_suspended so it's not dealt with anymore later
312          */
313         if (!found) {
314           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
315           GC_mach_threads_count++;
316         }
317         continue;
318       }
319 #     if DEBUG_THREADS
320         GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
321 #     endif
322       if (!found) {
323         GC_mach_threads[GC_mach_threads_count].already_suspended = info.suspend_count;
324       }
325       if (info.suspend_count) continue;
326       
327 #     if DEBUG_THREADS
328         GC_printf1("Suspending 0x%lx\n", thread);
329 #     endif
330       /* Suspend the thread */
331       kern_result = thread_suspend(thread);
332       if(kern_result != KERN_SUCCESS) {
333         /* the thread may have quit since the thread_threads () call 
334          * we mark already_suspended so it's not dealt with anymore later
335          */
336         if (!found) {
337           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
338           GC_mach_threads_count++;
339         }
340         continue;
341       }
342     } 
343     if (!found) GC_mach_threads_count++;
344   }
345   return changed;
346 }
347
348
349 /* Caller holds allocation lock.        */
350 void GC_stop_world()
351 {
352   int i, changes;
353     GC_thread p;
354     mach_port_t my_thread = mach_thread_self();
355     kern_return_t kern_result;
356     thread_act_array_t act_list, prev_list;
357     mach_msg_type_number_t listcount, prevcount;
358     
359 #   if DEBUG_THREADS
360       GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
361 #   endif
362
363     /* clear out the mach threads list table */
364     GC_stop_init(); 
365        
366     /* Make sure all free list construction has stopped before we start. */
367     /* No new construction can start, since free list construction is   */
368     /* required to acquire and release the GC lock before it starts,    */
369     /* and we have the lock.                                            */
370 #   ifdef PARALLEL_MARK
371       GC_acquire_mark_lock();
372       GC_ASSERT(GC_fl_builder_count == 0);
373       /* We should have previously waited for it to become zero. */
374 #   endif /* PARALLEL_MARK */
375
376       /* Loop stopping threads until you have gone over the whole list
377          twice without a new one appearing. thread_create() won't
378          return (and thus the thread stop) until the new thread
379          exists, so there is no window whereby you could stop a
380          thread, recognise it is stopped, but then have a new thread
381          it created before stopping show up later.
382       */
383       
384       changes = 1;
385       prev_list = NULL;
386       prevcount = 0;
387       do {
388         int result;
389         kern_result = task_threads(current_task(), &act_list, &listcount);
390         result = GC_suspend_thread_list(act_list, listcount,
391                                         prev_list, prevcount);
392         changes = result;
393         prev_list = act_list;
394         prevcount = listcount;
395       } while (changes);
396       
397  
398 #   ifdef MPROTECT_VDB
399       if(GC_incremental) {
400         extern void GC_mprotect_stop();
401         GC_mprotect_stop();
402       }
403 #   endif
404     
405 #   ifdef PARALLEL_MARK
406       GC_release_mark_lock();
407 #   endif
408     #if DEBUG_THREADS
409       GC_printf1("World stopped from 0x%lx\n", my_thread);
410     #endif
411 }
412
413 /* Caller holds allocation lock, and has held it continuously since     */
414 /* the world stopped.                                                   */
415 void GC_start_world()
416 {
417   mach_port_t my_thread = mach_thread_self();
418   int i, j;
419   GC_thread p;
420   kern_return_t kern_result;
421   thread_act_array_t act_list;
422   mach_msg_type_number_t listcount;
423   struct thread_basic_info info;
424   mach_msg_type_number_t outCount = THREAD_INFO_MAX;
425   
426 #   if DEBUG_THREADS
427       GC_printf0("World starting\n");
428 #   endif
429
430 #   ifdef MPROTECT_VDB
431       if(GC_incremental) {
432         extern void GC_mprotect_resume();
433         GC_mprotect_resume();
434       }
435 #   endif
436
437     kern_result = task_threads(current_task(), &act_list, &listcount);
438     for(i = 0; i < listcount; i++) {
439       thread_act_t thread = act_list[i];
440       if (thread != my_thread &&
441           (!GC_use_mach_handler_thread ||
442            (GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
443         for(j = 0; j < GC_mach_threads_count; j++) {
444           if (thread == GC_mach_threads[j].thread) {
445             if (GC_mach_threads[j].already_suspended) {
446 #             if DEBUG_THREADS
447                 GC_printf1("Not resuming already suspended thread %p\n", thread);
448 #             endif
449               continue;
450             }
451             kern_result = thread_info(thread, THREAD_BASIC_INFO,
452                                       (thread_info_t)&info, &outCount);
453             if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
454 #           if DEBUG_THREADS
455               GC_printf2("Thread state for 0x%lx = %d\n", thread,
456                          info.run_state);
457               GC_printf1("Resuming 0x%lx\n", thread);
458 #           endif
459             /* Resume the thread */
460             kern_result = thread_resume(thread);
461             if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
462           } 
463         }
464       }
465     }
466 #   if DEBUG_THREADS
467      GC_printf0("World started\n");
468 #   endif
469 }
470
471 void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
472   GC_mach_handler_thread = thread;
473   GC_use_mach_handler_thread = 1;
474 }
475
476 #endif