OSDN Git Service

gcc/
[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("ld      %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     vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
253 }
254 #endif /* !DARWIN_DONT_PARSE_STACK */
255
256 static mach_port_t GC_mach_handler_thread;
257 static int GC_use_mach_handler_thread = 0;
258
259 static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
260 static int GC_mach_threads_count;
261
262 void GC_stop_init() {
263   int i;
264
265   for (i = 0; i < THREAD_TABLE_SZ; i++) {
266     GC_mach_threads[i].thread = 0;
267     GC_mach_threads[i].already_suspended = 0;
268   }
269   GC_mach_threads_count = 0;
270 }
271
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;
277
278   int changed = 0;
279
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;
292       }
293     }
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;
300     }      
301
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
313          */
314         if (!found) {
315           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
316           GC_mach_threads_count++;
317         }
318         continue;
319       }
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;
325       }
326       if (info.suspend_count) continue;
327       
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
336          */
337         if (!found) {
338           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
339           GC_mach_threads_count++;
340         }
341         continue;
342       }
343     } 
344     if (!found) GC_mach_threads_count++;
345   }
346   return changed;
347 }
348
349
350 /* Caller holds allocation lock.        */
351 void GC_stop_world()
352 {
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;
359     
360 #   if DEBUG_THREADS
361       GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
362 #   endif
363
364     /* clear out the mach threads list table */
365     GC_stop_init(); 
366        
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 */
376
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.
383       */
384       
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);
398       
399  
400 #   ifdef MPROTECT_VDB
401       if(GC_incremental) {
402         extern void GC_mprotect_stop();
403         GC_mprotect_stop();
404       }
405 #   endif
406     
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
413 }
414
415 /* Caller holds allocation lock, and has held it continuously since     */
416 /* the world stopped.                                                   */
417 void GC_start_world()
418 {
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;
427   
428 #   if DEBUG_THREADS
429       GC_printf0("World starting\n");
430 #   endif
431
432 #   ifdef MPROTECT_VDB
433       if(GC_incremental) {
434         extern void GC_mprotect_resume();
435         GC_mprotect_resume();
436       }
437 #   endif
438
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;
452             }
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");
464           } 
465         }
466       }
467     }
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
472 }
473
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;
477 }
478
479 #endif