Synopsis - Cross-Reference

File: /src/Synopsis/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
 25typedef 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
 33unsigned long FindTopOfStack(unsigned long stack_start)
 34{
 35  StackFrame	*frame;
 36
 37  if (stack_start == 0) {
 38# ifdef POWERPC
 39#   if CPP_WORDSZ == 32
 40      __asm__ volatile("lwz	%0,0(r1)" : "=r" (frame));
 41#   else
 42      __asm__ volatile("ld	%0,0(r1)" : "=r" (frame));
 43#   endif
 44# endif
 45  } else {
 46    frame = (StackFrame *)stack_start;
 47  }
 48
 49# ifdef DEBUG_THREADS
 50    /* GC_printf("FindTopOfStack start at sp = %p\n", frame); */
 51# endif
 52  do {
 53    if (frame->savedSP == 0)
 54      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)
 63      break; /* if the next LR is bogus, stop */
 64    if ((~(frame->savedLR) & ~3) == 0)
 65      break; /* ditto */
 66  } while (1);
 67
 68# ifdef DEBUG_THREADS
 69    /* GC_printf("FindTopOfStack finish at sp = %p\n", frame); */
 70# endif
 71
 72  return (unsigned long)frame;
 73}
 74
 75#ifdef DARWIN_DONT_PARSE_STACK
 76void GC_push_all_stacks()
 77{
 78  int i;
 79  kern_return_t r;
 80  GC_thread p;
 81  pthread_t me;
 82  ptr_t lo, hi;
 83  GC_THREAD_STATE_T state;
 84  /* MACHINE_THREAD_STATE_COUNT doesn't seem to be defined everywhere.	*/
 85  /* Hence we use our own version.					*/
 86  mach_msg_type_number_t thread_state_count = GC_MACH_THREAD_STATE_COUNT;
 87
 88  me = pthread_self();
 89  if (!GC_thr_initialized)
 90    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(p->stop_info.mach_thread, GC_MACH_THREAD_STATE,
100			     (natural_t*)&state, &thread_state_count);
101
102#       ifdef DEBUG_THREADS
103	  GC_printf("thread_get_state return value = %d\n", r);
104#	endif
105
106	if(r != KERN_SUCCESS)
107	  ABORT("thread_get_state failed");
108
109#       if defined(I386)
110	  lo = (void*)state . THREAD_FLD (esp);
111	  GC_push_one(state . THREAD_FLD (eax));
112	  GC_push_one(state . THREAD_FLD (ebx));
113	  GC_push_one(state . THREAD_FLD (ecx));
114	  GC_push_one(state . THREAD_FLD (edx));
115	  GC_push_one(state . THREAD_FLD (edi));
116	  GC_push_one(state . THREAD_FLD (esi));
117	  GC_push_one(state . THREAD_FLD (ebp));
118
119#       elif defined(X86_64)
120	  lo = (void*)state . THREAD_FLD (rsp);
121	  GC_push_one(state . THREAD_FLD (rax));
122	  GC_push_one(state . THREAD_FLD (rbx));
123	  GC_push_one(state . THREAD_FLD (rcx));
124	  GC_push_one(state . THREAD_FLD (rdx));
125	  GC_push_one(state . THREAD_FLD (rdi));
126	  GC_push_one(state . THREAD_FLD (rsi));
127	  GC_push_one(state . THREAD_FLD (rbp));
128	  GC_push_one(state . THREAD_FLD (rsp));
129	  GC_push_one(state . THREAD_FLD (r8));
130	  GC_push_one(state . THREAD_FLD (r9));
131	  GC_push_one(state . THREAD_FLD (r10));
132	  GC_push_one(state . THREAD_FLD (r11));
133	  GC_push_one(state . THREAD_FLD (r12));
134	  GC_push_one(state . THREAD_FLD (r13));
135	  GC_push_one(state . THREAD_FLD (r14));
136	  GC_push_one(state . THREAD_FLD (r15));
137	  GC_push_one(state . THREAD_FLD (rip));
138	  GC_push_one(state . THREAD_FLD (rflags));
139	  GC_push_one(state . THREAD_FLD (cs));
140	  GC_push_one(state . THREAD_FLD (fs));
141	  GC_push_one(state . THREAD_FLD (gs));
142
143#       elif defined(POWERPC)
144	  lo = (void*)(state . THREAD_FLD (r1) - PPC_RED_ZONE_SIZE);
145
146	  GC_push_one(state . THREAD_FLD (r0));
147	  GC_push_one(state . THREAD_FLD (r2));
148	  GC_push_one(state . THREAD_FLD (r3));
149	  GC_push_one(state . THREAD_FLD (r4));
150	  GC_push_one(state . THREAD_FLD (r5));
151	  GC_push_one(state . THREAD_FLD (r6));
152	  GC_push_one(state . THREAD_FLD (r7));
153	  GC_push_one(state . THREAD_FLD (r8));
154	  GC_push_one(state . THREAD_FLD (r9));
155	  GC_push_one(state . THREAD_FLD (r10));
156	  GC_push_one(state . THREAD_FLD (r11));
157	  GC_push_one(state . THREAD_FLD (r12));
158	  GC_push_one(state . THREAD_FLD (r13));
159	  GC_push_one(state . THREAD_FLD (r14));
160	  GC_push_one(state . THREAD_FLD (r15));
161	  GC_push_one(state . THREAD_FLD (r16));
162	  GC_push_one(state . THREAD_FLD (r17));
163	  GC_push_one(state . THREAD_FLD (r18));
164	  GC_push_one(state . THREAD_FLD (r19));
165	  GC_push_one(state . THREAD_FLD (r20));
166	  GC_push_one(state . THREAD_FLD (r21));
167	  GC_push_one(state . THREAD_FLD (r22));
168	  GC_push_one(state . THREAD_FLD (r23));
169	  GC_push_one(state . THREAD_FLD (r24));
170	  GC_push_one(state . THREAD_FLD (r25));
171	  GC_push_one(state . THREAD_FLD (r26));
172	  GC_push_one(state . THREAD_FLD (r27));
173	  GC_push_one(state . THREAD_FLD (r28));
174	  GC_push_one(state . THREAD_FLD (r29));
175	  GC_push_one(state . THREAD_FLD (r30));
176	  GC_push_one(state . THREAD_FLD (r31));
177#	else
178#	  error FIXME for non-x86 || ppc architectures
179#	endif
180      } /* p != me */
181      if(p->flags & MAIN_THREAD)
182	hi = GC_stackbottom;
183      else
184	hi = p->stack_end;
185#     if DEBUG_THREADS
186        GC_printf("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
187		  (unsigned long) p -> id, (unsigned long) lo,
188		  (unsigned long) hi);
189#     endif
190      GC_push_all_stack(lo, hi);
191    } /* for(p=GC_threads[i]...) */
192  } /* for(i=0;i<THREAD_TABLE_SZ...) */
193}
194
195#else /* !DARWIN_DONT_PARSE_STACK; Use FindTopOfStack() */
196
197void GC_push_all_stacks()
198{
199  unsigned int i;
200  task_t my_task;
201  kern_return_t r;
202  mach_port_t me;
203  ptr_t lo, hi;
204  thread_act_array_t act_list = 0;
205  mach_msg_type_number_t listcount = 0;
206
207  me = mach_thread_self();
208  if (!GC_thr_initialized)
209    GC_thr_init();
210
211  my_task = current_task();
212  r = task_threads(my_task, &act_list, &listcount);
213  if(r != KERN_SUCCESS)
214    ABORT("task_threads failed");
215  for(i = 0; i < listcount; i++) {
216    thread_act_t thread = act_list[i];
217    if (thread == me) {
218      lo = GC_approx_sp();
219      hi = (ptr_t)FindTopOfStack(0);
220    } else {
221#     if defined(POWERPC)
222        GC_THREAD_STATE_T info;
223	mach_msg_type_number_t outCount = THREAD_STATE_MAX;
224	r = thread_get_state(thread, GC_MACH_THREAD_STATE, (natural_t *)&info,
225			     &outCount);
226	if(r != KERN_SUCCESS)
227	  ABORT("task_get_state failed");
228
229	lo = (void*)(info . THREAD_FLD (r1) - PPC_RED_ZONE_SIZE);
230	hi = (ptr_t)FindTopOfStack(info . THREAD_FLD (r1));
231
232	GC_push_one(info . THREAD_FLD (r0));
233	GC_push_one(info . THREAD_FLD (r2));
234	GC_push_one(info . THREAD_FLD (r3));
235	GC_push_one(info . THREAD_FLD (r4));
236	GC_push_one(info . THREAD_FLD (r5));
237	GC_push_one(info . THREAD_FLD (r6));
238	GC_push_one(info . THREAD_FLD (r7));
239	GC_push_one(info . THREAD_FLD (r8));
240	GC_push_one(info . THREAD_FLD (r9));
241	GC_push_one(info . THREAD_FLD (r10));
242	GC_push_one(info . THREAD_FLD (r11));
243	GC_push_one(info . THREAD_FLD (r12));
244	GC_push_one(info . THREAD_FLD (r13));
245	GC_push_one(info . THREAD_FLD (r14));
246	GC_push_one(info . THREAD_FLD (r15));
247	GC_push_one(info . THREAD_FLD (r16));
248	GC_push_one(info . THREAD_FLD (r17));
249	GC_push_one(info . THREAD_FLD (r18));
250	GC_push_one(info . THREAD_FLD (r19));
251	GC_push_one(info . THREAD_FLD (r20));
252	GC_push_one(info . THREAD_FLD (r21));
253	GC_push_one(info . THREAD_FLD (r22));
254	GC_push_one(info . THREAD_FLD (r23));
255	GC_push_one(info . THREAD_FLD (r24));
256	GC_push_one(info . THREAD_FLD (r25));
257	GC_push_one(info . THREAD_FLD (r26));
258	GC_push_one(info . THREAD_FLD (r27));
259	GC_push_one(info . THREAD_FLD (r28));
260	GC_push_one(info . THREAD_FLD (r29));
261	GC_push_one(info . THREAD_FLD (r30));
262	GC_push_one(info . THREAD_FLD (r31));
263
264#     elif defined(I386)
265	/* FIXME: Remove after testing:	*/
266	WARN("This is completely untested and likely will not work\n", 0);
267	GC_THREAD_STATE_T info;
268	mach_msg_type_number_t outCount = THREAD_STATE_MAX;
269	r = thread_get_state(thread, GC_MACH_THREAD_STATE, (natural_t *)&info,
270			     &outCount);
271	if(r != KERN_SUCCESS)
272	  ABORT("task_get_state failed");
273
274	lo = (void*)info . THREAD_FLD (esp);
275	hi = (ptr_t)FindTopOfStack(info . THREAD_FLD (esp));
276
277	GC_push_one(info . THREAD_FLD (eax));
278	GC_push_one(info . THREAD_FLD (ebx));
279	GC_push_one(info . THREAD_FLD (ecx));
280	GC_push_one(info . THREAD_FLD (edx));
281	GC_push_one(info . THREAD_FLD (edi));
282	GC_push_one(info . THREAD_FLD (esi));
283	/* GC_push_one(info . THREAD_FLD (ebp));  */
284	/* GC_push_one(info . THREAD_FLD (esp));  */
285	GC_push_one(info . THREAD_FLD (ss));
286	GC_push_one(info . THREAD_FLD (eip));
287	GC_push_one(info . THREAD_FLD (cs));
288	GC_push_one(info . THREAD_FLD (ds));
289	GC_push_one(info . THREAD_FLD (es));
290	GC_push_one(info . THREAD_FLD (fs));
291	GC_push_one(info . THREAD_FLD (gs));
292
293#     elif defined(X86_64)
294	GC_THREAD_STATE_T info;
295	mach_msg_type_number_t outCount = THREAD_STATE_MAX;
296	r = thread_get_state(thread, GC_MACH_THREAD_STATE, (natural_t *)&info,
297			     &outCount);
298	if(r != KERN_SUCCESS)
299	  ABORT("task_get_state failed");
300
301	lo = (void*)info . THREAD_FLD (rsp);
302	hi = (ptr_t)FindTopOfStack(info . THREAD_FLD (rsp));
303
304	GC_push_one(info . THREAD_FLD (rax));
305	GC_push_one(info . THREAD_FLD (rbx));
306	GC_push_one(info . THREAD_FLD (rcx));
307	GC_push_one(info . THREAD_FLD (rdx));
308	GC_push_one(info . THREAD_FLD (rdi));
309	GC_push_one(info . THREAD_FLD (rsi));
310	GC_push_one(info . THREAD_FLD (rbp));
311	GC_push_one(info . THREAD_FLD (rsp));
312	GC_push_one(info . THREAD_FLD (r8));
313	GC_push_one(info . THREAD_FLD (r9));
314	GC_push_one(info . THREAD_FLD (r10));
315	GC_push_one(info . THREAD_FLD (r11));
316	GC_push_one(info . THREAD_FLD (r12));
317	GC_push_one(info . THREAD_FLD (r13));
318	GC_push_one(info . THREAD_FLD (r14));
319	GC_push_one(info . THREAD_FLD (r15));
320	GC_push_one(info . THREAD_FLD (rip));
321	GC_push_one(info . THREAD_FLD (rflags));
322	GC_push_one(info . THREAD_FLD (cs));
323	GC_push_one(info . THREAD_FLD (fs));
324	GC_push_one(info . THREAD_FLD (gs));
325
326#     else
327#	error FIXME for non-x86 || ppc architectures
328#     endif
329      }
330#     if DEBUG_THREADS
331        GC_printf("Darwin: Stack for thread 0x%lx = [%p,%p)\n",
332		  (unsigned long) thread, lo, hi);
333#     endif
334      GC_push_all_stack(lo, hi);
335      mach_port_deallocate(my_task, thread);
336    } /* for(p=GC_threads[i]...) */
337    vm_deallocate(my_task, (vm_address_t)act_list,
338		  sizeof(thread_t) * listcount);
339    mach_port_deallocate(my_task, me);
340}
341#endif /* !DARWIN_DONT_PARSE_STACK */
342
343static mach_port_t GC_mach_handler_thread;
344static int GC_use_mach_handler_thread = 0;
345
346static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
347static int GC_mach_threads_count;
348
349void GC_stop_init()
350{
351  int i;
352
353  for (i = 0; i < THREAD_TABLE_SZ; i++) {
354    GC_mach_threads[i].thread = 0;
355    GC_mach_threads[i].already_suspended = 0;
356  }
357  GC_mach_threads_count = 0;
358}
359
360/* returns true if there's a thread in act_list that wasn't in old_list */
361int GC_suspend_thread_list(thread_act_array_t act_list, int count,
362			   thread_act_array_t old_list, int old_count)
363{
364  mach_port_t my_thread = mach_thread_self();
365  int i, j;
366
367  int changed = 0;
368
369  for(i = 0; i < count; i++) {
370    thread_act_t thread = act_list[i];
371#   if DEBUG_THREADS
372      GC_printf("Attempting to suspend thread %p\n", thread);
373#   endif
374    /* find the current thread in the old list */
375    int found = 0;
376    for(j = 0; j < old_count; j++) {
377      thread_act_t old_thread = old_list[j];
378      if (old_thread == thread) {
379	found = 1;
380	break;
381      }
382    }
383    if (!found) {
384      /* add it to the GC_mach_threads list */
385      GC_mach_threads[GC_mach_threads_count].thread = thread;
386      /* default is not suspended */
387      GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
388      changed = 1;
389    }
390
391    if (thread != my_thread
392	&& (!GC_use_mach_handler_thread
393	    || (GC_use_mach_handler_thread
394		&& GC_mach_handler_thread != thread))) {
395      struct thread_basic_info info;
396      mach_msg_type_number_t outCount = THREAD_INFO_MAX;
397      kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
398				(thread_info_t)&info, &outCount);
399      if(kern_result != KERN_SUCCESS) {
400	/* the thread may have quit since the thread_threads () call
401	 * we mark already_suspended so it's not dealt with anymore later
402	 */
403	if (!found) {
404	  GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
405	  GC_mach_threads_count++;
406	}
407	continue;
408      }
409#     if DEBUG_THREADS
410        GC_printf("Thread state for 0x%lx = %d\n", (unsigned long)thread,
411		  info.run_state);
412#     endif
413      if (!found) {
414	GC_mach_threads[GC_mach_threads_count].already_suspended
415	  = info.suspend_count;
416      }
417      if (info.suspend_count)
418	continue;
419
420#     if DEBUG_THREADS
421        GC_printf("Suspending 0x%lx\n", (unsigned long)thread);
422#     endif
423      /* Suspend the thread */
424      kern_result = thread_suspend(thread);
425      if(kern_result != KERN_SUCCESS) {
426	/* the thread may have quit since the thread_threads () call
427	 * we mark already_suspended so it's not dealt with anymore later
428	 */
429	if (!found) {
430	  GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
431	  GC_mach_threads_count++;
432	}
433	continue;
434      }
435    }
436    if (!found) GC_mach_threads_count++;
437  }
438  mach_port_deallocate(current_task(), my_thread);
439  return changed;
440}
441
442
443/* Caller holds allocation lock.	*/
444void GC_stop_world()
445{
446    unsigned int i, changes;
447    task_t my_task = current_task();
448    mach_port_t my_thread = mach_thread_self();
449    kern_return_t kern_result;
450    thread_act_array_t act_list, prev_list;
451    mach_msg_type_number_t listcount, prevcount;
452
453#   if DEBUG_THREADS
454      GC_printf("Stopping the world from 0x%lx\n",
455		(unsigned long)mach_thread_self());
456#   endif
457
458    /* clear out the mach threads list table */
459    GC_stop_init();
460
461    /* Make sure all free list construction has stopped before we start. */
462    /* No new construction can start, since free list construction is	*/
463    /* required to acquire and release the GC lock before it starts,	*/
464    /* and we have the lock.						*/
465#   ifdef PARALLEL_MARK
466      GC_acquire_mark_lock();
467      GC_ASSERT(GC_fl_builder_count == 0);
468      /* We should have previously waited for it to become zero. */
469#   endif /* PARALLEL_MARK */
470
471    /* Loop stopping threads until you have gone over the whole list
472       twice without a new one appearing. thread_create() won't
473       return (and thus the thread stop) until the new thread
474       exists, so there is no window whereby you could stop a
475       thread, recognise it is stopped, but then have a new thread
476       it created before stopping show up later.
477    */
478
479    changes = 1;
480    prev_list = NULL;
481    prevcount = 0;
482    do {
483      int result;
484      kern_result = task_threads(my_task, &act_list, &listcount);
485
486      if(kern_result == KERN_SUCCESS) {
487	result = GC_suspend_thread_list(act_list, listcount, prev_list,
488					prevcount);
489	changes = result;
490
491	if(prev_list != NULL) {
492	  for(i = 0; i < prevcount; i++)
493	    mach_port_deallocate(my_task, prev_list[i]);
494
495	  vm_deallocate(my_task, (vm_address_t)prev_list,
496			sizeof(thread_t) * prevcount);
497	}
498	prev_list = act_list;
499	prevcount = listcount;
500      }
501    } while (changes);
502    GC_ASSERT(prev_list != 0);
503    for(i = 0; i < prevcount; i++)
504      mach_port_deallocate(my_task, prev_list[i]);
505
506    vm_deallocate(my_task, (vm_address_t)act_list,
507		  sizeof(thread_t) * listcount);
508
509#   ifdef MPROTECT_VDB
510      if(GC_incremental) {
511	extern void GC_mprotect_stop();
512	GC_mprotect_stop();
513      }
514#   endif
515
516#   ifdef PARALLEL_MARK
517      GC_release_mark_lock();
518#   endif
519#   if DEBUG_THREADS
520      GC_printf("World stopped from 0x%lx\n", (unsigned long)my_thread);
521#   endif
522
523    mach_port_deallocate(my_task, my_thread);
524}
525
526/* Caller holds allocation lock, and has held it continuously since	*/
527/* the world stopped.							*/
528void GC_start_world()
529{
530  task_t my_task = current_task();
531  mach_port_t my_thread = mach_thread_self();
532  unsigned int i;
533  int j;
534  kern_return_t kern_result;
535  thread_act_array_t act_list;
536  mach_msg_type_number_t listcount;
537  struct thread_basic_info info;
538  mach_msg_type_number_t outCount = THREAD_INFO_MAX;
539
540#   if DEBUG_THREADS
541      GC_printf("World starting\n");
542#   endif
543
544#   ifdef MPROTECT_VDB
545      if(GC_incremental) {
546	extern void GC_mprotect_resume();
547	GC_mprotect_resume();
548      }
549#   endif
550
551    kern_result = task_threads(my_task, &act_list, &listcount);
552    for(i = 0; i < listcount; i++) {
553      thread_act_t thread = act_list[i];
554      if (thread != my_thread
555	  && (!GC_use_mach_handler_thread
556	      || (GC_use_mach_handler_thread
557		  && GC_mach_handler_thread != thread))) {
558	for(j = 0; j < GC_mach_threads_count; j++) {
559	  if (thread == GC_mach_threads[j].thread) {
560	    if (GC_mach_threads[j].already_suspended) {
561#             if DEBUG_THREADS
562	        GC_printf("Not resuming already suspended thread %p\n", thread);
563#             endif
564	      continue;
565	    }
566	    kern_result = thread_info(thread, THREAD_BASIC_INFO,
567				      (thread_info_t)&info, &outCount);
568	    if(kern_result != KERN_SUCCESS)
569	      ABORT("thread_info failed");
570#           if DEBUG_THREADS
571	      GC_printf("Thread state for 0x%lx = %d\n", (unsigned long)thread,
572			 info.run_state);
573	      GC_printf("Resuming 0x%lx\n", (unsigned long)thread);
574#           endif
575	    /* Resume the thread */
576	    kern_result = thread_resume(thread);
577	    if(kern_result != KERN_SUCCESS)
578	      ABORT("thread_resume failed");
579	  }
580	}
581      }
582      mach_port_deallocate(my_task, thread);
583    }
584    vm_deallocate(my_task, (vm_address_t)act_list,
585		  sizeof(thread_t) * listcount);
586
587    mach_port_deallocate(my_task, my_thread);
588#   if DEBUG_THREADS
589      GC_printf("World started\n");
590#   endif
591}
592
593void GC_darwin_register_mach_handler_thread(mach_port_t thread)
594{
595  GC_mach_handler_thread = thread;
596  GC_use_mach_handler_thread = 1;
597}
598
599#endif