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