242c9495cbd7c2b7489a0c2b3ecf64c792409681
[mono.git] / libgc / darwin_stop_world.c
1 #include "private/pthread_support.h"
2
3 # if defined(GC_DARWIN_THREADS)
4
5 /* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
6    Page 49:
7    "The space beneath the stack pointer, where a new stack frame would normally
8    be allocated, is called the red zone. This area as shown in Figure 3-2 may
9    be used for any purpose as long as a new stack frame does not need to be
10    added to the stack."
11    
12    Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
13    it must set up a stack frame just like routines that call other routines."
14 */
15 #ifdef POWERPC
16 # if CPP_WORDSZ == 32
17 #   define PPC_RED_ZONE_SIZE 224
18 # elif CPP_WORDSZ == 64
19 #   define PPC_RED_ZONE_SIZE 320
20 # endif
21 #endif
22
23 typedef struct StackFrame {
24   unsigned long savedSP;
25   unsigned long savedCR;
26   unsigned long savedLR;
27   unsigned long reserved[2];
28   unsigned long savedRTOC;
29 } StackFrame;
30
31 unsigned long FindTopOfStack(unsigned int stack_start) {
32   StackFrame    *frame;
33   
34   if (stack_start == 0) {
35 # ifdef POWERPC
36 #   if CPP_WORDSZ == 32
37       __asm__ volatile("lwz     %0,0(r1)" : "=r" (frame));
38 #   else
39       __asm__ volatile("ldz     %0,0(r1)" : "=r" (frame));
40 #   endif
41 # endif
42   } else {
43     frame = (StackFrame *)stack_start;
44   }
45
46 # ifdef DEBUG_THREADS
47     /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
48 # endif
49   do {
50     if (frame->savedSP == 0) break;
51                 /* if there are no more stack frames, stop */
52
53     frame = (StackFrame*)frame->savedSP;
54
55     /* we do these next two checks after going to the next frame
56        because the LR for the first stack frame in the loop
57        is not set up on purpose, so we shouldn't check it. */
58     if ((frame->savedLR & ~3) == 0) break; /* if the next LR is bogus, stop */
59     if ((~(frame->savedLR) & ~3) == 0) break; /* ditto */
60   } while (1); 
61
62 # ifdef DEBUG_THREADS
63     /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
64 # endif
65
66   return (unsigned long)frame;
67 }       
68
69 #ifdef DARWIN_DONT_PARSE_STACK
70 void GC_push_all_stacks() {
71   int i;
72   kern_return_t r;
73   GC_thread p;
74   pthread_t me;
75   ptr_t lo, hi;
76 #if defined(POWERPC)
77   ppc_thread_state_t state;
78   mach_msg_type_number_t thread_state_count = PPC_THREAD_STATE_COUNT;
79 #elif defined(I386)
80   i386_thread_state_t state;
81   mach_msg_type_number_t thread_state_count = i386_THREAD_STATE_COUNT;
82 #else
83 # error FIXME for non-x86 || ppc architectures
84   mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
85 #endif
86   
87   me = pthread_self();
88   if (!GC_thr_initialized) GC_thr_init();
89   
90   for(i=0;i<THREAD_TABLE_SZ;i++) {
91     for(p=GC_threads[i];p!=0;p=p->next) {
92       if(p -> flags & FINISHED) continue;
93       if(pthread_equal(p->id,me)) {
94         lo = GC_approx_sp();
95       } else {
96         /* Get the thread state (registers, etc) */
97         r = thread_get_state(
98                              p->stop_info.mach_thread,
99                              GC_MACH_THREAD_STATE_FLAVOR,
100                              (natural_t*)&state,
101                              &thread_state_count);
102         if(r != KERN_SUCCESS) ABORT("thread_get_state failed");
103         
104 #if defined(I386)
105 #if defined(_STRUCT_X86_EXCEPTION_STATE)
106         lo = state.__esp;
107
108         GC_push_one(state.__eax); 
109         GC_push_one(state.__ebx); 
110         GC_push_one(state.__ecx); 
111         GC_push_one(state.__edx); 
112         GC_push_one(state.__edi); 
113         GC_push_one(state.__esi); 
114         GC_push_one(state.__ebp); 
115 #else
116         lo = state.esp;
117
118         GC_push_one(state.eax); 
119         GC_push_one(state.ebx); 
120         GC_push_one(state.ecx); 
121         GC_push_one(state.edx); 
122         GC_push_one(state.edi); 
123         GC_push_one(state.esi); 
124         GC_push_one(state.ebp); 
125 #endif
126 #elif defined(POWERPC)
127 #if defined(_STRUCT_PPC_EXCEPTION_STATE)
128         lo = (void*)(state.__r1 - PPC_RED_ZONE_SIZE);
129         
130         GC_push_one(state.__r0); 
131         GC_push_one(state.__r2); 
132         GC_push_one(state.__r3); 
133         GC_push_one(state.__r4); 
134         GC_push_one(state.__r5); 
135         GC_push_one(state.__r6); 
136         GC_push_one(state.__r7); 
137         GC_push_one(state.__r8); 
138         GC_push_one(state.__r9); 
139         GC_push_one(state.__r10); 
140         GC_push_one(state.__r11); 
141         GC_push_one(state.__r12); 
142         GC_push_one(state.__r13); 
143         GC_push_one(state.__r14); 
144         GC_push_one(state.__r15); 
145         GC_push_one(state.__r16); 
146         GC_push_one(state.__r17); 
147         GC_push_one(state.__r18); 
148         GC_push_one(state.__r19); 
149         GC_push_one(state.__r20); 
150         GC_push_one(state.__r21); 
151         GC_push_one(state.__r22); 
152         GC_push_one(state.__r23); 
153         GC_push_one(state.__r24); 
154         GC_push_one(state.__r25); 
155         GC_push_one(state.__r26); 
156         GC_push_one(state.__r27); 
157         GC_push_one(state.__r28); 
158         GC_push_one(state.__r29); 
159         GC_push_one(state.__r30); 
160         GC_push_one(state.__r31);
161 #else
162         lo = (void*)(state.r1 - PPC_RED_ZONE_SIZE);
163         
164         GC_push_one(state.r0); 
165         GC_push_one(state.r2); 
166         GC_push_one(state.r3); 
167         GC_push_one(state.r4); 
168         GC_push_one(state.r5); 
169         GC_push_one(state.r6); 
170         GC_push_one(state.r7); 
171         GC_push_one(state.r8); 
172         GC_push_one(state.r9); 
173         GC_push_one(state.r10); 
174         GC_push_one(state.r11); 
175         GC_push_one(state.r12); 
176         GC_push_one(state.r13); 
177         GC_push_one(state.r14); 
178         GC_push_one(state.r15); 
179         GC_push_one(state.r16); 
180         GC_push_one(state.r17); 
181         GC_push_one(state.r18); 
182         GC_push_one(state.r19); 
183         GC_push_one(state.r20); 
184         GC_push_one(state.r21); 
185         GC_push_one(state.r22); 
186         GC_push_one(state.r23); 
187         GC_push_one(state.r24); 
188         GC_push_one(state.r25); 
189         GC_push_one(state.r26); 
190         GC_push_one(state.r27); 
191         GC_push_one(state.r28); 
192         GC_push_one(state.r29); 
193         GC_push_one(state.r30); 
194         GC_push_one(state.r31);
195 #endif
196 #else
197 # error FIXME for non-x86 || ppc architectures
198 #endif
199       } /* p != me */
200       if(p->flags & MAIN_THREAD)
201         hi = GC_stackbottom;
202       else
203         hi = p->stack_end;
204 #if DEBUG_THREADS
205       GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
206                  (unsigned long) p -> id,
207                  (unsigned long) lo,
208                  (unsigned long) hi
209                  );
210 #endif
211       GC_push_all_stack(lo,hi);
212     } /* for(p=GC_threads[i]...) */
213   } /* for(i=0;i<THREAD_TABLE_SZ...) */
214 }
215
216 #else /* !DARWIN_DONT_PARSE_STACK; Use FindTopOfStack() */
217
218 void GC_push_all_stacks() {
219     int i;
220         task_t my_task;
221     kern_return_t r;
222     mach_port_t me;
223     ptr_t lo, hi;
224     thread_act_array_t act_list = 0;
225     mach_msg_type_number_t listcount = 0;
226
227     me = mach_thread_self();
228     if (!GC_thr_initialized) GC_thr_init();
229     
230         my_task = current_task();
231     r = task_threads(my_task, &act_list, &listcount);
232     if(r != KERN_SUCCESS) ABORT("task_threads failed");
233     for(i = 0; i < listcount; i++) {
234       thread_act_t thread = act_list[i];
235       if (thread == me) {
236         lo = GC_approx_sp();
237         hi = (ptr_t)FindTopOfStack(0);
238       } else {
239 #     if defined(POWERPC)
240 #      if CPP_WORDSZ == 32
241         ppc_thread_state_t info;
242 #      else
243         ppc_thread_state64_t info;
244 #      endif
245         mach_msg_type_number_t outCount = THREAD_STATE_MAX;
246         r = thread_get_state(thread, GC_MACH_THREAD_STATE_FLAVOR,
247                              (natural_t *)&info, &outCount);
248         if(r != KERN_SUCCESS) continue;
249
250 #if defined(_STRUCT_PPC_EXCEPTION_STATE)
251         lo = (void*)(info.__r1 - PPC_RED_ZONE_SIZE);
252         hi = (ptr_t)FindTopOfStack(info.__r1);
253
254         GC_push_one(info.__r0); 
255         GC_push_one(info.__r2); 
256         GC_push_one(info.__r3); 
257         GC_push_one(info.__r4); 
258         GC_push_one(info.__r5); 
259         GC_push_one(info.__r6); 
260         GC_push_one(info.__r7); 
261         GC_push_one(info.__r8); 
262         GC_push_one(info.__r9); 
263         GC_push_one(info.__r10); 
264         GC_push_one(info.__r11); 
265         GC_push_one(info.__r12); 
266         GC_push_one(info.__r13); 
267         GC_push_one(info.__r14); 
268         GC_push_one(info.__r15); 
269         GC_push_one(info.__r16); 
270         GC_push_one(info.__r17); 
271         GC_push_one(info.__r18); 
272         GC_push_one(info.__r19); 
273         GC_push_one(info.__r20); 
274         GC_push_one(info.__r21); 
275         GC_push_one(info.__r22); 
276         GC_push_one(info.__r23); 
277         GC_push_one(info.__r24); 
278         GC_push_one(info.__r25); 
279         GC_push_one(info.__r26); 
280         GC_push_one(info.__r27); 
281         GC_push_one(info.__r28); 
282         GC_push_one(info.__r29); 
283         GC_push_one(info.__r30); 
284         GC_push_one(info.__r31);
285 #else
286         lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
287         hi = (ptr_t)FindTopOfStack(info.r1);
288
289         GC_push_one(info.r0); 
290         GC_push_one(info.r2); 
291         GC_push_one(info.r3); 
292         GC_push_one(info.r4); 
293         GC_push_one(info.r5); 
294         GC_push_one(info.r6); 
295         GC_push_one(info.r7); 
296         GC_push_one(info.r8); 
297         GC_push_one(info.r9); 
298         GC_push_one(info.r10); 
299         GC_push_one(info.r11); 
300         GC_push_one(info.r12); 
301         GC_push_one(info.r13); 
302         GC_push_one(info.r14); 
303         GC_push_one(info.r15); 
304         GC_push_one(info.r16); 
305         GC_push_one(info.r17); 
306         GC_push_one(info.r18); 
307         GC_push_one(info.r19); 
308         GC_push_one(info.r20); 
309         GC_push_one(info.r21); 
310         GC_push_one(info.r22); 
311         GC_push_one(info.r23); 
312         GC_push_one(info.r24); 
313         GC_push_one(info.r25); 
314         GC_push_one(info.r26); 
315         GC_push_one(info.r27); 
316         GC_push_one(info.r28); 
317         GC_push_one(info.r29); 
318         GC_push_one(info.r30); 
319         GC_push_one(info.r31);
320 #endif
321 #      else
322         /* FIXME: Remove after testing: */
323         WARN("This is completely untested and likely will not work\n", 0);
324         i386_thread_state_t info;
325         mach_msg_type_number_t outCount = THREAD_STATE_MAX;
326         r = thread_get_state(thread, GC_MACH_THREAD_STATE_FLAVOR,
327                              (natural_t *)&info, &outCount);
328         if(r != KERN_SUCCESS) continue;
329
330 #if defined(_STRUCT_X86_EXCEPTION_STATE)
331         lo = (void*)info.__esp;
332         hi = (ptr_t)FindTopOfStack(info.__esp);
333
334         GC_push_one(info.__eax); 
335         GC_push_one(info.__ebx); 
336         GC_push_one(info.__ecx); 
337         GC_push_one(info.__edx); 
338         GC_push_one(info.__edi); 
339         GC_push_one(info.__esi); 
340         /* GC_push_one(info.__ebp);  */
341         /* GC_push_one(info.__esp);  */
342         GC_push_one(info.__ss); 
343         GC_push_one(info.__eip); 
344         GC_push_one(info.__cs); 
345         GC_push_one(info.__ds); 
346         GC_push_one(info.__es); 
347         GC_push_one(info.__fs); 
348         GC_push_one(info.__gs); 
349 #else
350         lo = (void*)info.esp;
351         hi = (ptr_t)FindTopOfStack(info.esp);
352
353         GC_push_one(info.eax); 
354         GC_push_one(info.ebx); 
355         GC_push_one(info.ecx); 
356         GC_push_one(info.edx); 
357         GC_push_one(info.edi); 
358         GC_push_one(info.esi); 
359         /* GC_push_one(info.ebp);  */
360         /* GC_push_one(info.esp);  */
361         GC_push_one(info.ss); 
362         GC_push_one(info.eip); 
363         GC_push_one(info.cs); 
364         GC_push_one(info.ds); 
365         GC_push_one(info.es); 
366         GC_push_one(info.fs); 
367         GC_push_one(info.gs); 
368 #endif
369 #      endif /* !POWERPC */
370       }
371 #     if DEBUG_THREADS
372        GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
373                   (unsigned long) thread,
374                   (unsigned long) lo,
375                   (unsigned long) hi
376                  );
377 #     endif
378       GC_push_all_stack(lo, hi);
379           mach_port_deallocate(my_task, thread);
380     } /* for(p=GC_threads[i]...) */
381     vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount);
382         mach_port_deallocate(my_task, me);
383 }
384 #endif /* !DARWIN_DONT_PARSE_STACK */
385
386 static mach_port_t GC_mach_handler_thread;
387 static int GC_use_mach_handler_thread = 0;
388
389 #define SUSPEND_THREADS_SIZE 2048
390 static struct GC_mach_thread GC_mach_threads[SUSPEND_THREADS_SIZE];
391 static int GC_mach_threads_count;
392
393 void GC_stop_init() {
394   int i;
395
396   for (i = 0; i < SUSPEND_THREADS_SIZE; i++) {
397     GC_mach_threads[i].thread = 0;
398     GC_mach_threads[i].already_suspended = 0;
399   }
400   GC_mach_threads_count = 0;
401 }
402
403 /* returns true if there's a thread in act_list that wasn't in old_list */
404 int GC_suspend_thread_list(thread_act_array_t act_list, int count, 
405                            thread_act_array_t old_list, int old_count) {
406   mach_port_t my_thread = mach_thread_self();
407   int i, j;
408
409   int changed = 0;
410
411   for(i = 0; i < count; i++) {
412     thread_act_t thread = act_list[i];
413 #   if DEBUG_THREADS 
414       GC_printf1("Attempting to suspend thread %p\n", thread);
415 #   endif
416     /* find the current thread in the old list */
417     int found = 0;
418     for(j = 0; j < old_count; j++) {
419       thread_act_t old_thread = old_list[j];
420       if (old_thread == thread) {
421         found = 1;
422         break;
423       }
424     }
425     if (!found) {
426       /* add it to the GC_mach_threads list */
427       GC_mach_threads[GC_mach_threads_count].thread = thread;
428       /* default is not suspended */
429       GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
430       changed = 1;
431     }      
432
433     if (thread != my_thread &&
434         (!GC_use_mach_handler_thread
435          || (GC_use_mach_handler_thread
436              && GC_mach_handler_thread != thread))) {
437       struct thread_basic_info info;
438       mach_msg_type_number_t outCount = THREAD_INFO_MAX;
439       kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
440                                 (thread_info_t)&info, &outCount);
441       if(kern_result != KERN_SUCCESS) {
442         /* the thread may have quit since the thread_threads () call 
443          * we mark already_suspended so it's not dealt with anymore later
444          */
445         if (!found) {
446           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
447           GC_mach_threads_count++;
448         }
449         continue;
450       }
451 #     if DEBUG_THREADS
452         GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
453 #     endif
454       if (!found) {
455         GC_mach_threads[GC_mach_threads_count].already_suspended = info.suspend_count;
456       }
457       if (info.suspend_count) continue;
458       
459 #     if DEBUG_THREADS
460         GC_printf1("Suspending 0x%lx\n", thread);
461 #     endif
462       /* Suspend the thread */
463       kern_result = thread_suspend(thread);
464       if(kern_result != KERN_SUCCESS) {
465         /* the thread may have quit since the thread_threads () call 
466          * we mark already_suspended so it's not dealt with anymore later
467          */
468         if (!found) {
469           GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
470           GC_mach_threads_count++;
471         }
472         continue;
473       }
474     } 
475     if (!found) GC_mach_threads_count++;
476   }
477   
478   mach_port_deallocate(current_task(), my_thread);
479   return changed;
480 }
481
482
483 /* Caller holds allocation lock.        */
484 void GC_stop_world()
485 {
486   int i, changes;
487     GC_thread p;
488         task_t my_task = current_task();
489     mach_port_t my_thread = mach_thread_self();
490     kern_return_t kern_result;
491     thread_act_array_t act_list, prev_list;
492     mach_msg_type_number_t listcount, prevcount;
493     
494 #   if DEBUG_THREADS
495       GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
496 #   endif
497
498     /* clear out the mach threads list table */
499     GC_stop_init(); 
500        
501     /* Make sure all free list construction has stopped before we start. */
502     /* No new construction can start, since free list construction is   */
503     /* required to acquire and release the GC lock before it starts,    */
504     /* and we have the lock.                                            */
505 #   ifdef PARALLEL_MARK
506       GC_acquire_mark_lock();
507       GC_ASSERT(GC_fl_builder_count == 0);
508       /* We should have previously waited for it to become zero. */
509 #   endif /* PARALLEL_MARK */
510
511       /* Loop stopping threads until you have gone over the whole list
512          twice without a new one appearing. thread_create() won't
513          return (and thus the thread stop) until the new thread
514          exists, so there is no window whereby you could stop a
515          thread, recognise it is stopped, but then have a new thread
516          it created before stopping show up later.
517       */
518       
519       changes = 1;
520       prev_list = NULL;
521       prevcount = 0;
522       do {
523         int result;               
524         kern_result = task_threads(my_task, &act_list, &listcount);
525         
526         if(kern_result == KERN_SUCCESS) {       
527                 result = GC_suspend_thread_list(act_list, listcount,
528                                                                                 prev_list, prevcount);
529                 changes = result;
530                 
531                 if(prev_list != NULL) {
532                         for(i = 0; i < prevcount; i++)
533                                 mach_port_deallocate(my_task, prev_list[i]);
534                         
535                         vm_deallocate(my_task, (vm_address_t)prev_list, sizeof(thread_t) * prevcount);
536                 }
537                 
538                 prev_list = act_list;
539                 prevcount = listcount;
540         }               
541       } while (changes);
542      
543           for(i = 0; i < listcount; i++)
544                   mach_port_deallocate(my_task, act_list[i]);
545           
546           vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount);
547           
548  
549 #   ifdef MPROTECT_VDB
550       if(GC_incremental) {
551         extern void GC_mprotect_stop();
552         GC_mprotect_stop();
553       }
554 #   endif
555     
556 #   ifdef PARALLEL_MARK
557       GC_release_mark_lock();
558 #   endif
559     #if DEBUG_THREADS
560       GC_printf1("World stopped from 0x%lx\n", my_thread);
561     #endif
562           
563           mach_port_deallocate(my_task, my_thread);
564 }
565
566 /* Caller holds allocation lock, and has held it continuously since     */
567 /* the world stopped.                                                   */
568 void GC_start_world()
569 {
570   task_t my_task = current_task();
571   mach_port_t my_thread = mach_thread_self();
572   int i, j;
573   GC_thread p;
574   kern_return_t kern_result;
575   thread_act_array_t act_list;
576   mach_msg_type_number_t listcount;
577   struct thread_basic_info info;
578   mach_msg_type_number_t outCount = THREAD_INFO_MAX;
579   
580 #   if DEBUG_THREADS
581       GC_printf0("World starting\n");
582 #   endif
583
584 #   ifdef MPROTECT_VDB
585       if(GC_incremental) {
586         extern void GC_mprotect_resume();
587         GC_mprotect_resume();
588       }
589 #   endif
590
591     kern_result = task_threads(my_task, &act_list, &listcount);
592     for(i = 0; i < listcount; i++) {
593       thread_act_t thread = act_list[i];
594       if (thread != my_thread &&
595           (!GC_use_mach_handler_thread ||
596            (GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
597         for(j = 0; j < GC_mach_threads_count; j++) {
598           if (thread == GC_mach_threads[j].thread) {
599             if (GC_mach_threads[j].already_suspended) {
600 #             if DEBUG_THREADS
601                 GC_printf1("Not resuming already suspended thread %p\n", thread);
602 #             endif
603               continue;
604             }
605             kern_result = thread_info(thread, THREAD_BASIC_INFO,
606                                       (thread_info_t)&info, &outCount);
607             if(kern_result != KERN_SUCCESS) continue;
608 #           if DEBUG_THREADS
609               GC_printf2("Thread state for 0x%lx = %d\n", thread,
610                          info.run_state);
611               GC_printf1("Resuming 0x%lx\n", thread);
612 #           endif
613             /* Resume the thread */
614             kern_result = thread_resume(thread);
615             if(kern_result != KERN_SUCCESS) continue;
616           } 
617         }
618       }
619           
620           mach_port_deallocate(my_task, thread);
621     }
622     vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount);
623         
624         mach_port_deallocate(my_task, my_thread);
625 #   if DEBUG_THREADS
626      GC_printf0("World started\n");
627 #   endif
628 }
629
630 void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
631   GC_mach_handler_thread = thread;
632   GC_use_mach_handler_thread = 1;
633 }
634
635 #endif