Blame darwin_stop_world.c

Packit d28291
/*
Packit d28291
 * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
Packit d28291
 * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
Packit d28291
 * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
Packit d28291
 * Copyright (c) 2000-2010 by Hewlett-Packard Development Company.
Packit d28291
 * All rights reserved.
Packit d28291
 *
Packit d28291
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
Packit d28291
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
Packit d28291
 *
Packit d28291
 * Permission is hereby granted to use or copy this program
Packit d28291
 * for any purpose,  provided the above notices are retained on all copies.
Packit d28291
 * Permission to modify the code and to distribute modified code is granted,
Packit d28291
 * provided the above notices are retained, and a notice that the code was
Packit d28291
 * modified is included with the above copyright notice.
Packit d28291
 */
Packit d28291
Packit d28291
#include "private/pthread_support.h"
Packit d28291
Packit d28291
/* This probably needs more porting work to ppc64. */
Packit d28291
Packit d28291
#if defined(GC_DARWIN_THREADS)
Packit d28291
Packit d28291
#include <sys/sysctl.h>
Packit d28291
#include <mach/machine.h>
Packit d28291
#include <CoreFoundation/CoreFoundation.h>
Packit d28291
Packit d28291
/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
Packit d28291
   Page 49:
Packit d28291
   "The space beneath the stack pointer, where a new stack frame would normally
Packit d28291
   be allocated, is called the red zone. This area as shown in Figure 3-2 may
Packit d28291
   be used for any purpose as long as a new stack frame does not need to be
Packit d28291
   added to the stack."
Packit d28291
Packit d28291
   Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
Packit d28291
   it must set up a stack frame just like routines that call other routines."
Packit d28291
*/
Packit d28291
#ifdef POWERPC
Packit d28291
# if CPP_WORDSZ == 32
Packit d28291
#   define PPC_RED_ZONE_SIZE 224
Packit d28291
# elif CPP_WORDSZ == 64
Packit d28291
#   define PPC_RED_ZONE_SIZE 320
Packit d28291
# endif
Packit d28291
#endif
Packit d28291
Packit d28291
#ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
Packit d28291
typedef struct StackFrame {
Packit d28291
  unsigned long savedSP;
Packit d28291
  unsigned long savedCR;
Packit d28291
  unsigned long savedLR;
Packit d28291
  unsigned long reserved[2];
Packit d28291
  unsigned long savedRTOC;
Packit d28291
} StackFrame;
Packit d28291
Packit d28291
GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start)
Packit d28291
{
Packit d28291
  StackFrame *frame;
Packit d28291
Packit d28291
# ifdef POWERPC
Packit d28291
    if (stack_start == 0) {
Packit d28291
#     if CPP_WORDSZ == 32
Packit d28291
        __asm__ __volatile__ ("lwz %0,0(r1)" : "=r" (frame));
Packit d28291
#     else
Packit d28291
        __asm__ __volatile__ ("ld %0,0(r1)" : "=r" (frame));
Packit d28291
#     endif
Packit d28291
    } else
Packit d28291
# else
Packit d28291
    GC_ASSERT(stack_start != 0); /* not implemented */
Packit d28291
# endif /* !POWERPC */
Packit d28291
  /* else */ {
Packit d28291
    frame = (StackFrame *)stack_start;
Packit d28291
  }
Packit d28291
Packit d28291
# ifdef DEBUG_THREADS_EXTRA
Packit d28291
    GC_log_printf("FindTopOfStack start at sp = %p\n", (void *)frame);
Packit d28291
# endif
Packit d28291
  while (frame->savedSP != 0) {
Packit d28291
    /* if there are no more stack frames, stop */
Packit d28291
Packit d28291
    frame = (StackFrame*)frame->savedSP;
Packit d28291
Packit d28291
    /* we do these next two checks after going to the next frame
Packit d28291
       because the LR for the first stack frame in the loop
Packit d28291
       is not set up on purpose, so we shouldn't check it. */
Packit d28291
    if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3UL)
Packit d28291
      break; /* if the next LR is bogus, stop */
Packit d28291
  }
Packit d28291
# ifdef DEBUG_THREADS_EXTRA
Packit d28291
    GC_log_printf("FindTopOfStack finish at sp = %p\n", (void *)frame);
Packit d28291
# endif
Packit d28291
  return (ptr_t)frame;
Packit d28291
}
Packit d28291
Packit d28291
#endif /* !DARWIN_DONT_PARSE_STACK */
Packit d28291
Packit d28291
/* GC_query_task_threads controls whether to obtain the list of */
Packit d28291
/* the threads from the kernel or to use GC_threads table.      */
Packit d28291
#ifdef GC_NO_THREADS_DISCOVERY
Packit d28291
# define GC_query_task_threads FALSE
Packit d28291
#elif defined(GC_DISCOVER_TASK_THREADS)
Packit d28291
# define GC_query_task_threads TRUE
Packit d28291
#else
Packit d28291
  STATIC GC_bool GC_query_task_threads = FALSE;
Packit d28291
#endif /* !GC_NO_THREADS_DISCOVERY */
Packit d28291
Packit d28291
/* Use implicit threads registration (all task threads excluding the GC */
Packit d28291
/* special ones are stopped and scanned).  Should be called before      */
Packit d28291
/* GC_INIT() (or, at least, before going multi-threaded).  Deprecated.  */
Packit d28291
GC_API void GC_CALL GC_use_threads_discovery(void)
Packit d28291
{
Packit d28291
# if defined(GC_NO_THREADS_DISCOVERY) || defined(DARWIN_DONT_PARSE_STACK)
Packit d28291
    ABORT("Darwin task-threads-based stop and push unsupported");
Packit d28291
# else
Packit d28291
#   ifndef GC_ALWAYS_MULTITHREADED
Packit d28291
      GC_ASSERT(!GC_need_to_lock);
Packit d28291
#   endif
Packit d28291
#   ifndef GC_DISCOVER_TASK_THREADS
Packit d28291
      GC_query_task_threads = TRUE;
Packit d28291
#   endif
Packit d28291
    GC_init_parallel(); /* just to be consistent with Win32 one */
Packit d28291
# endif
Packit d28291
}
Packit d28291
Packit d28291
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
Packit d28291
# define kCFCoreFoundationVersionNumber_iOS_8_0 1140.1
Packit d28291
#endif
Packit d28291
Packit d28291
/* Evaluates the stack range for a given thread.  Returns the lower     */
Packit d28291
/* bound and sets *phi to the upper one.                                */
Packit d28291
STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p,
Packit d28291
                                GC_bool thread_blocked, mach_port_t my_thread,
Packit d28291
                                ptr_t *paltstack_lo,
Packit d28291
                                ptr_t *paltstack_hi GC_ATTR_UNUSED)
Packit d28291
{
Packit d28291
  ptr_t lo;
Packit d28291
  if (thread == my_thread) {
Packit d28291
    GC_ASSERT(!thread_blocked);
Packit d28291
    lo = GC_approx_sp();
Packit d28291
#   ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
      *phi = GC_FindTopOfStack(0);
Packit d28291
#   endif
Packit d28291
Packit d28291
  } else if (thread_blocked) {
Packit d28291
#   if defined(CPPCHECK)
Packit d28291
      if (NULL == p) ABORT("Invalid GC_thread passed to GC_stack_range_for");
Packit d28291
#   endif
Packit d28291
    lo = p->stop_info.stack_ptr;
Packit d28291
#   ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
      *phi = p->topOfStack;
Packit d28291
#   endif
Packit d28291
Packit d28291
  } else {
Packit d28291
    /* MACHINE_THREAD_STATE_COUNT does not seem to be defined       */
Packit d28291
    /* everywhere.  Hence we use our own version.  Alternatively,   */
Packit d28291
    /* we could use THREAD_STATE_MAX (but seems to be not optimal). */
Packit d28291
    kern_return_t kern_result;
Packit d28291
    GC_THREAD_STATE_T state;
Packit d28291
Packit d28291
#   if defined(ARM32) && defined(ARM_THREAD_STATE32)
Packit d28291
      /* Use ARM_UNIFIED_THREAD_STATE on iOS8+ 32-bit targets and on    */
Packit d28291
      /* 64-bit H/W (iOS7+ 32-bit mode).                                */
Packit d28291
      size_t size;
Packit d28291
      static cpu_type_t cputype = 0;
Packit d28291
Packit d28291
      if (cputype == 0) {
Packit d28291
        sysctlbyname("hw.cputype", &cputype, &size, NULL, 0);
Packit d28291
      }
Packit d28291
      if (cputype == CPU_TYPE_ARM64
Packit d28291
          || kCFCoreFoundationVersionNumber
Packit d28291
             >= kCFCoreFoundationVersionNumber_iOS_8_0) {
Packit d28291
        arm_unified_thread_state_t unified_state;
Packit d28291
        mach_msg_type_number_t unified_thread_state_count
Packit d28291
                                        = ARM_UNIFIED_THREAD_STATE_COUNT;
Packit d28291
#       if defined(CPPCHECK)
Packit d28291
#         define GC_ARM_UNIFIED_THREAD_STATE 1
Packit d28291
#       else
Packit d28291
#         define GC_ARM_UNIFIED_THREAD_STATE ARM_UNIFIED_THREAD_STATE
Packit d28291
#       endif
Packit d28291
        kern_result = thread_get_state(thread, GC_ARM_UNIFIED_THREAD_STATE,
Packit d28291
                                       (natural_t *)&unified_state,
Packit d28291
                                       &unified_thread_state_count);
Packit d28291
#       if !defined(CPPCHECK)
Packit d28291
          if (unified_state.ash.flavor != ARM_THREAD_STATE32) {
Packit d28291
            ABORT("unified_state flavor should be ARM_THREAD_STATE32");
Packit d28291
          }
Packit d28291
#       endif
Packit d28291
        state = unified_state.ts_32;
Packit d28291
      } else
Packit d28291
#   endif
Packit d28291
    /* else */ {
Packit d28291
      mach_msg_type_number_t thread_state_count = GC_MACH_THREAD_STATE_COUNT;
Packit d28291
Packit d28291
      /* Get the thread state (registers, etc) */
Packit d28291
      kern_result = thread_get_state(thread, GC_MACH_THREAD_STATE,
Packit d28291
                                     (natural_t *)&state,
Packit d28291
                                     &thread_state_count);
Packit d28291
    }
Packit d28291
#   ifdef DEBUG_THREADS
Packit d28291
      GC_log_printf("thread_get_state returns value = %d\n", kern_result);
Packit d28291
#   endif
Packit d28291
    if (kern_result != KERN_SUCCESS)
Packit d28291
      ABORT("thread_get_state failed");
Packit d28291
Packit d28291
#   if defined(I386)
Packit d28291
      lo = (void *)state.THREAD_FLD(esp);
Packit d28291
#     ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
        *phi = GC_FindTopOfStack(state.THREAD_FLD(esp));
Packit d28291
#     endif
Packit d28291
      GC_push_one(state.THREAD_FLD(eax));
Packit d28291
      GC_push_one(state.THREAD_FLD(ebx));
Packit d28291
      GC_push_one(state.THREAD_FLD(ecx));
Packit d28291
      GC_push_one(state.THREAD_FLD(edx));
Packit d28291
      GC_push_one(state.THREAD_FLD(edi));
Packit d28291
      GC_push_one(state.THREAD_FLD(esi));
Packit d28291
      GC_push_one(state.THREAD_FLD(ebp));
Packit d28291
Packit d28291
#   elif defined(X86_64)
Packit d28291
      lo = (void *)state.THREAD_FLD(rsp);
Packit d28291
#     ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
        *phi = GC_FindTopOfStack(state.THREAD_FLD(rsp));
Packit d28291
#     endif
Packit d28291
      GC_push_one(state.THREAD_FLD(rax));
Packit d28291
      GC_push_one(state.THREAD_FLD(rbx));
Packit d28291
      GC_push_one(state.THREAD_FLD(rcx));
Packit d28291
      GC_push_one(state.THREAD_FLD(rdx));
Packit d28291
      GC_push_one(state.THREAD_FLD(rdi));
Packit d28291
      GC_push_one(state.THREAD_FLD(rsi));
Packit d28291
      GC_push_one(state.THREAD_FLD(rbp));
Packit d28291
      /* GC_push_one(state.THREAD_FLD(rsp)); */
Packit d28291
      GC_push_one(state.THREAD_FLD(r8));
Packit d28291
      GC_push_one(state.THREAD_FLD(r9));
Packit d28291
      GC_push_one(state.THREAD_FLD(r10));
Packit d28291
      GC_push_one(state.THREAD_FLD(r11));
Packit d28291
      GC_push_one(state.THREAD_FLD(r12));
Packit d28291
      GC_push_one(state.THREAD_FLD(r13));
Packit d28291
      GC_push_one(state.THREAD_FLD(r14));
Packit d28291
      GC_push_one(state.THREAD_FLD(r15));
Packit d28291
Packit d28291
#   elif defined(POWERPC)
Packit d28291
      lo = (void *)(state.THREAD_FLD(r1) - PPC_RED_ZONE_SIZE);
Packit d28291
#     ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
        *phi = GC_FindTopOfStack(state.THREAD_FLD(r1));
Packit d28291
#     endif
Packit d28291
      GC_push_one(state.THREAD_FLD(r0));
Packit d28291
      GC_push_one(state.THREAD_FLD(r2));
Packit d28291
      GC_push_one(state.THREAD_FLD(r3));
Packit d28291
      GC_push_one(state.THREAD_FLD(r4));
Packit d28291
      GC_push_one(state.THREAD_FLD(r5));
Packit d28291
      GC_push_one(state.THREAD_FLD(r6));
Packit d28291
      GC_push_one(state.THREAD_FLD(r7));
Packit d28291
      GC_push_one(state.THREAD_FLD(r8));
Packit d28291
      GC_push_one(state.THREAD_FLD(r9));
Packit d28291
      GC_push_one(state.THREAD_FLD(r10));
Packit d28291
      GC_push_one(state.THREAD_FLD(r11));
Packit d28291
      GC_push_one(state.THREAD_FLD(r12));
Packit d28291
      GC_push_one(state.THREAD_FLD(r13));
Packit d28291
      GC_push_one(state.THREAD_FLD(r14));
Packit d28291
      GC_push_one(state.THREAD_FLD(r15));
Packit d28291
      GC_push_one(state.THREAD_FLD(r16));
Packit d28291
      GC_push_one(state.THREAD_FLD(r17));
Packit d28291
      GC_push_one(state.THREAD_FLD(r18));
Packit d28291
      GC_push_one(state.THREAD_FLD(r19));
Packit d28291
      GC_push_one(state.THREAD_FLD(r20));
Packit d28291
      GC_push_one(state.THREAD_FLD(r21));
Packit d28291
      GC_push_one(state.THREAD_FLD(r22));
Packit d28291
      GC_push_one(state.THREAD_FLD(r23));
Packit d28291
      GC_push_one(state.THREAD_FLD(r24));
Packit d28291
      GC_push_one(state.THREAD_FLD(r25));
Packit d28291
      GC_push_one(state.THREAD_FLD(r26));
Packit d28291
      GC_push_one(state.THREAD_FLD(r27));
Packit d28291
      GC_push_one(state.THREAD_FLD(r28));
Packit d28291
      GC_push_one(state.THREAD_FLD(r29));
Packit d28291
      GC_push_one(state.THREAD_FLD(r30));
Packit d28291
      GC_push_one(state.THREAD_FLD(r31));
Packit d28291
Packit d28291
#   elif defined(ARM32)
Packit d28291
      lo = (void *)state.THREAD_FLD(sp);
Packit d28291
#     ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
        *phi = GC_FindTopOfStack(state.THREAD_FLD(sp));
Packit d28291
#     endif
Packit d28291
      {
Packit d28291
        int j;
Packit d28291
        for (j = 0; j <= 12; j++) {
Packit d28291
          GC_push_one(state.THREAD_FLD(r[j]));
Packit d28291
        }
Packit d28291
      }
Packit d28291
      /* "pc" and "sp" are skipped */
Packit d28291
      GC_push_one(state.THREAD_FLD(lr));
Packit d28291
      GC_push_one(state.THREAD_FLD(cpsr));
Packit d28291
Packit d28291
#   elif defined(AARCH64)
Packit d28291
      lo = (void *)state.THREAD_FLD(sp);
Packit d28291
#     ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
        *phi = GC_FindTopOfStack(state.THREAD_FLD(sp));
Packit d28291
#     endif
Packit d28291
      {
Packit d28291
        int j;
Packit d28291
        for (j = 0; j <= 28; j++) {
Packit d28291
          GC_push_one(state.THREAD_FLD(x[j]));
Packit d28291
        }
Packit d28291
      }
Packit d28291
      /* "cpsr", "pc" and "sp" are skipped */
Packit d28291
      GC_push_one(state.THREAD_FLD(fp));
Packit d28291
      GC_push_one(state.THREAD_FLD(lr));
Packit d28291
Packit d28291
#   elif defined(CPPCHECK)
Packit d28291
      lo = NULL;
Packit d28291
#   else
Packit d28291
#     error FIXME for non-x86 || ppc || arm architectures
Packit d28291
#   endif
Packit d28291
  } /* thread != my_thread */
Packit d28291
Packit d28291
# ifdef DARWIN_DONT_PARSE_STACK
Packit d28291
    /* p is guaranteed to be non-NULL regardless of GC_query_task_threads. */
Packit d28291
    *phi = (p->flags & MAIN_THREAD) != 0 ? GC_stackbottom : p->stack_end;
Packit d28291
# endif
Packit d28291
Packit d28291
  /* TODO: Determine p and handle altstack if !DARWIN_DONT_PARSE_STACK */
Packit d28291
# ifdef DARWIN_DONT_PARSE_STACK
Packit d28291
  if (p->altstack != NULL && (word)p->altstack <= (word)lo
Packit d28291
      && (word)lo <= (word)p->altstack + p->altstack_size) {
Packit d28291
    *paltstack_lo = lo;
Packit d28291
    *paltstack_hi = p->altstack + p->altstack_size;
Packit d28291
    lo = p->stack;
Packit d28291
    *phi = p->stack + p->stack_size;
Packit d28291
  } else
Packit d28291
# endif
Packit d28291
  /* else */ {
Packit d28291
    *paltstack_lo = NULL;
Packit d28291
  }
Packit d28291
# ifdef DEBUG_THREADS
Packit d28291
    GC_log_printf("Darwin: Stack for thread %p = [%p,%p)\n",
Packit d28291
                  (void *)(word)thread, (void *)lo, (void *)(*phi));
Packit d28291
# endif
Packit d28291
  return lo;
Packit d28291
}
Packit d28291
Packit d28291
GC_INNER void GC_push_all_stacks(void)
Packit d28291
{
Packit d28291
  ptr_t hi, altstack_lo, altstack_hi;
Packit d28291
  task_t my_task = current_task();
Packit d28291
  mach_port_t my_thread = mach_thread_self();
Packit d28291
  GC_bool found_me = FALSE;
Packit d28291
  int nthreads = 0;
Packit d28291
  word total_size = 0;
Packit d28291
  mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
Packit d28291
  if (!EXPECT(GC_thr_initialized, TRUE))
Packit d28291
    GC_thr_init();
Packit d28291
Packit d28291
# ifndef DARWIN_DONT_PARSE_STACK
Packit d28291
    if (GC_query_task_threads) {
Packit d28291
      int i;
Packit d28291
      kern_return_t kern_result;
Packit d28291
      thread_act_array_t act_list = 0;
Packit d28291
Packit d28291
      /* Obtain the list of the threads from the kernel.  */
Packit d28291
      kern_result = task_threads(my_task, &act_list, &listcount);
Packit d28291
      if (kern_result != KERN_SUCCESS)
Packit d28291
        ABORT("task_threads failed");
Packit d28291
Packit d28291
      for (i = 0; i < (int)listcount; i++) {
Packit d28291
        thread_act_t thread = act_list[i];
Packit d28291
        ptr_t lo = GC_stack_range_for(&hi, thread, NULL, FALSE, my_thread,
Packit d28291
                                      &altstack_lo, &altstack_hi);
Packit d28291
Packit d28291
        if (lo) {
Packit d28291
          GC_ASSERT((word)lo <= (word)hi);
Packit d28291
          total_size += hi - lo;
Packit d28291
          GC_push_all_stack(lo, hi);
Packit d28291
        }
Packit d28291
        /* TODO: Handle altstack */
Packit d28291
        nthreads++;
Packit d28291
        if (thread == my_thread)
Packit d28291
          found_me = TRUE;
Packit d28291
        mach_port_deallocate(my_task, thread);
Packit d28291
      } /* for (i=0; ...) */
Packit d28291
Packit d28291
      vm_deallocate(my_task, (vm_address_t)act_list,
Packit d28291
                    sizeof(thread_t) * listcount);
Packit d28291
    } else
Packit d28291
# endif /* !DARWIN_DONT_PARSE_STACK */
Packit d28291
  /* else */ {
Packit d28291
    int i;
Packit d28291
Packit d28291
    for (i = 0; i < (int)listcount; i++) {
Packit d28291
      GC_thread p;
Packit d28291
Packit d28291
      for (p = GC_threads[i]; p != NULL; p = p->next)
Packit d28291
        if ((p->flags & FINISHED) == 0) {
Packit d28291
          thread_act_t thread = (thread_act_t)p->stop_info.mach_thread;
Packit d28291
          ptr_t lo = GC_stack_range_for(&hi, thread, p,
Packit d28291
                                        (GC_bool)p->thread_blocked,
Packit d28291
                                        my_thread, &altstack_lo,
Packit d28291
                                        &altstack_hi);
Packit d28291
Packit d28291
          if (lo) {
Packit d28291
            GC_ASSERT((word)lo <= (word)hi);
Packit d28291
            total_size += hi - lo;
Packit d28291
            GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
Packit d28291
          }
Packit d28291
          if (altstack_lo) {
Packit d28291
            total_size += altstack_hi - altstack_lo;
Packit d28291
            GC_push_all_stack(altstack_lo, altstack_hi);
Packit d28291
          }
Packit d28291
          nthreads++;
Packit d28291
          if (thread == my_thread)
Packit d28291
            found_me = TRUE;
Packit d28291
        }
Packit d28291
    } /* for (i=0; ...) */
Packit d28291
  }
Packit d28291
Packit d28291
  mach_port_deallocate(my_task, my_thread);
Packit d28291
  GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", nthreads);
Packit d28291
  if (!found_me && !GC_in_thread_creation)
Packit d28291
    ABORT("Collecting from unknown thread");
Packit d28291
  GC_total_stacksize = total_size;
Packit d28291
}
Packit d28291
Packit d28291
#ifndef GC_NO_THREADS_DISCOVERY
Packit d28291
Packit d28291
# ifdef MPROTECT_VDB
Packit d28291
    STATIC mach_port_t GC_mach_handler_thread = 0;
Packit d28291
    STATIC GC_bool GC_use_mach_handler_thread = FALSE;
Packit d28291
Packit d28291
    GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread)
Packit d28291
    {
Packit d28291
      GC_mach_handler_thread = thread;
Packit d28291
      GC_use_mach_handler_thread = TRUE;
Packit d28291
    }
Packit d28291
# endif /* MPROTECT_VDB */
Packit d28291
Packit d28291
# ifndef GC_MAX_MACH_THREADS
Packit d28291
#   define GC_MAX_MACH_THREADS THREAD_TABLE_SZ
Packit d28291
# endif
Packit d28291
Packit d28291
  struct GC_mach_thread {
Packit d28291
    thread_act_t thread;
Packit d28291
    GC_bool already_suspended;
Packit d28291
  };
Packit d28291
Packit d28291
  struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS];
Packit d28291
  STATIC int GC_mach_threads_count = 0;
Packit d28291
  /* FIXME: it is better to implement GC_mach_threads as a hash set.  */
Packit d28291
Packit d28291
/* returns true if there's a thread in act_list that wasn't in old_list */
Packit d28291
STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count,
Packit d28291
                                      thread_act_array_t old_list,
Packit d28291
                                      int old_count, mach_port_t my_thread)
Packit d28291
{
Packit d28291
  int i;
Packit d28291
  int j = -1;
Packit d28291
  GC_bool changed = FALSE;
Packit d28291
Packit d28291
  for (i = 0; i < count; i++) {
Packit d28291
    thread_act_t thread = act_list[i];
Packit d28291
    GC_bool found;
Packit d28291
    struct thread_basic_info info;
Packit d28291
    mach_msg_type_number_t outCount;
Packit d28291
    kern_return_t kern_result;
Packit d28291
Packit d28291
    if (thread == my_thread
Packit d28291
#       ifdef MPROTECT_VDB
Packit d28291
          || (GC_mach_handler_thread == thread && GC_use_mach_handler_thread)
Packit d28291
#       endif
Packit d28291
        ) {
Packit d28291
      /* Don't add our and the handler threads. */
Packit d28291
      continue;
Packit d28291
    }
Packit d28291
#   ifdef PARALLEL_MARK
Packit d28291
      if (GC_is_mach_marker(thread))
Packit d28291
        continue; /* ignore the parallel marker threads */
Packit d28291
#   endif
Packit d28291
Packit d28291
#   ifdef DEBUG_THREADS
Packit d28291
      GC_log_printf("Attempting to suspend thread %p\n",
Packit d28291
                    (void *)(word)thread);
Packit d28291
#   endif
Packit d28291
    /* find the current thread in the old list */
Packit d28291
    found = FALSE;
Packit d28291
    {
Packit d28291
      int last_found = j; /* remember the previous found thread index */
Packit d28291
Packit d28291
      /* Search for the thread starting from the last found one first.  */
Packit d28291
      while (++j < old_count)
Packit d28291
        if (old_list[j] == thread) {
Packit d28291
          found = TRUE;
Packit d28291
          break;
Packit d28291
        }
Packit d28291
      if (!found) {
Packit d28291
        /* If not found, search in the rest (beginning) of the list.    */
Packit d28291
        for (j = 0; j < last_found; j++)
Packit d28291
          if (old_list[j] == thread) {
Packit d28291
            found = TRUE;
Packit d28291
            break;
Packit d28291
          }
Packit d28291
Packit d28291
        if (!found) {
Packit d28291
          /* add it to the GC_mach_threads list */
Packit d28291
          if (GC_mach_threads_count == GC_MAX_MACH_THREADS)
Packit d28291
            ABORT("Too many threads");
Packit d28291
          GC_mach_threads[GC_mach_threads_count].thread = thread;
Packit d28291
          /* default is not suspended */
Packit d28291
          GC_mach_threads[GC_mach_threads_count].already_suspended = FALSE;
Packit d28291
          changed = TRUE;
Packit d28291
        }
Packit d28291
      }
Packit d28291
    }
Packit d28291
Packit d28291
    outCount = THREAD_INFO_MAX;
Packit d28291
    kern_result = thread_info(thread, THREAD_BASIC_INFO,
Packit d28291
                              (thread_info_t)&info, &outCount);
Packit d28291
    if (kern_result != KERN_SUCCESS) {
Packit d28291
      /* The thread may have quit since the thread_threads() call we  */
Packit d28291
      /* mark already suspended so it's not dealt with anymore later. */
Packit d28291
      if (!found)
Packit d28291
        GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE;
Packit d28291
      continue;
Packit d28291
    }
Packit d28291
#   ifdef DEBUG_THREADS
Packit d28291
      GC_log_printf("Thread state for %p = %d\n", (void *)(word)thread,
Packit d28291
                    info.run_state);
Packit d28291
#   endif
Packit d28291
    if (info.suspend_count != 0) {
Packit d28291
      /* thread is already suspended. */
Packit d28291
      if (!found)
Packit d28291
        GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE;
Packit d28291
      continue;
Packit d28291
    }
Packit d28291
Packit d28291
#   ifdef DEBUG_THREADS
Packit d28291
      GC_log_printf("Suspending %p\n", (void *)(word)thread);
Packit d28291
#   endif
Packit d28291
    kern_result = thread_suspend(thread);
Packit d28291
    if (kern_result != KERN_SUCCESS) {
Packit d28291
      /* The thread may have quit since the thread_threads() call we  */
Packit d28291
      /* mark already suspended so it's not dealt with anymore later. */
Packit d28291
      if (!found)
Packit d28291
        GC_mach_threads[GC_mach_threads_count++].already_suspended = TRUE;
Packit d28291
      continue;
Packit d28291
    }
Packit d28291
    if (!found)
Packit d28291
      GC_mach_threads_count++;
Packit d28291
    if (GC_on_thread_event)
Packit d28291
      GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)thread);
Packit d28291
  }
Packit d28291
  return changed;
Packit d28291
}
Packit d28291
Packit d28291
#endif /* !GC_NO_THREADS_DISCOVERY */
Packit d28291
Packit d28291
/* Caller holds allocation lock.        */
Packit d28291
GC_INNER void GC_stop_world(void)
Packit d28291
{
Packit d28291
  task_t my_task = current_task();
Packit d28291
  mach_port_t my_thread = mach_thread_self();
Packit d28291
  kern_return_t kern_result;
Packit d28291
Packit d28291
# ifdef DEBUG_THREADS
Packit d28291
    GC_log_printf("Stopping the world from thread %p\n",
Packit d28291
                  (void *)(word)my_thread);
Packit d28291
# endif
Packit d28291
# ifdef PARALLEL_MARK
Packit d28291
    if (GC_parallel) {
Packit d28291
      /* Make sure all free list construction has stopped before we     */
Packit d28291
      /* start.  No new construction can start, since free list         */
Packit d28291
      /* construction is required to acquire and release the GC lock    */
Packit d28291
      /* before it starts, and we have the lock.                        */
Packit d28291
      GC_acquire_mark_lock();
Packit d28291
      GC_ASSERT(GC_fl_builder_count == 0);
Packit d28291
      /* We should have previously waited for it to become zero. */
Packit d28291
    }
Packit d28291
# endif /* PARALLEL_MARK */
Packit d28291
Packit d28291
  if (GC_query_task_threads) {
Packit d28291
#   ifndef GC_NO_THREADS_DISCOVERY
Packit d28291
      unsigned i;
Packit d28291
      GC_bool changed;
Packit d28291
      thread_act_array_t act_list, prev_list;
Packit d28291
      mach_msg_type_number_t listcount, prevcount;
Packit d28291
Packit d28291
      /* Clear out the mach threads list table.  We do not need to      */
Packit d28291
      /* really clear GC_mach_threads[] as it is used only in the range */
Packit d28291
      /* from 0 to GC_mach_threads_count-1, inclusive.                  */
Packit d28291
      GC_mach_threads_count = 0;
Packit d28291
Packit d28291
      /* Loop stopping threads until you have gone over the whole list  */
Packit d28291
      /* twice without a new one appearing.  thread_create() won't      */
Packit d28291
      /* return (and thus the thread stop) until the new thread exists, */
Packit d28291
      /* so there is no window whereby you could stop a thread,         */
Packit d28291
      /* recognize it is stopped, but then have a new thread it created */
Packit d28291
      /* before stopping show up later.                                 */
Packit d28291
      changed = TRUE;
Packit d28291
      prev_list = NULL;
Packit d28291
      prevcount = 0;
Packit d28291
      do {
Packit d28291
        kern_result = task_threads(my_task, &act_list, &listcount);
Packit d28291
Packit d28291
        if (kern_result == KERN_SUCCESS) {
Packit d28291
          changed = GC_suspend_thread_list(act_list, listcount, prev_list,
Packit d28291
                                           prevcount, my_thread);
Packit d28291
Packit d28291
          if (prev_list != NULL) {
Packit d28291
            for (i = 0; i < prevcount; i++)
Packit d28291
              mach_port_deallocate(my_task, prev_list[i]);
Packit d28291
Packit d28291
            vm_deallocate(my_task, (vm_address_t)prev_list,
Packit d28291
                          sizeof(thread_t) * prevcount);
Packit d28291
          }
Packit d28291
Packit d28291
          /* Repeat while having changes. */
Packit d28291
          prev_list = act_list;
Packit d28291
          prevcount = listcount;
Packit d28291
        }
Packit d28291
      } while (changed);
Packit d28291
Packit d28291
      GC_ASSERT(prev_list != 0);
Packit d28291
      for (i = 0; i < prevcount; i++)
Packit d28291
        mach_port_deallocate(my_task, prev_list[i]);
Packit d28291
      vm_deallocate(my_task, (vm_address_t)act_list,
Packit d28291
                    sizeof(thread_t) * listcount);
Packit d28291
#   endif /* !GC_NO_THREADS_DISCOVERY */
Packit d28291
Packit d28291
  } else {
Packit d28291
    unsigned i;
Packit d28291
Packit d28291
    for (i = 0; i < THREAD_TABLE_SZ; i++) {
Packit d28291
      GC_thread p;
Packit d28291
Packit d28291
      for (p = GC_threads[i]; p != NULL; p = p->next) {
Packit d28291
        if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
Packit d28291
             p->stop_info.mach_thread != my_thread) {
Packit d28291
Packit d28291
          kern_result = thread_suspend(p->stop_info.mach_thread);
Packit d28291
          if (kern_result != KERN_SUCCESS)
Packit d28291
            ABORT("thread_suspend failed");
Packit d28291
          if (GC_on_thread_event)
Packit d28291
            GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED,
Packit d28291
                               (void *)(word)p->stop_info.mach_thread);
Packit d28291
        }
Packit d28291
      }
Packit d28291
    }
Packit d28291
  }
Packit d28291
Packit d28291
# ifdef MPROTECT_VDB
Packit d28291
    if(GC_incremental) {
Packit d28291
      GC_mprotect_stop();
Packit d28291
    }
Packit d28291
# endif
Packit d28291
# ifdef PARALLEL_MARK
Packit d28291
    if (GC_parallel)
Packit d28291
      GC_release_mark_lock();
Packit d28291
# endif
Packit d28291
Packit d28291
# ifdef DEBUG_THREADS
Packit d28291
    GC_log_printf("World stopped from %p\n", (void *)(word)my_thread);
Packit d28291
# endif
Packit d28291
  mach_port_deallocate(my_task, my_thread);
Packit d28291
}
Packit d28291
Packit d28291
GC_INLINE void GC_thread_resume(thread_act_t thread)
Packit d28291
{
Packit d28291
  kern_return_t kern_result;
Packit d28291
# if defined(DEBUG_THREADS) || defined(GC_ASSERTIONS)
Packit d28291
    struct thread_basic_info info;
Packit d28291
    mach_msg_type_number_t outCount = THREAD_INFO_MAX;
Packit d28291
    kern_result = thread_info(thread, THREAD_BASIC_INFO,
Packit d28291
                              (thread_info_t)&info, &outCount);
Packit d28291
    if (kern_result != KERN_SUCCESS)
Packit d28291
      ABORT("thread_info failed");
Packit d28291
# endif
Packit d28291
# ifdef DEBUG_THREADS
Packit d28291
    GC_log_printf("Resuming thread %p with state %d\n", (void *)(word)thread,
Packit d28291
                  info.run_state);
Packit d28291
# endif
Packit d28291
  /* Resume the thread */
Packit d28291
  kern_result = thread_resume(thread);
Packit d28291
  if (kern_result != KERN_SUCCESS)
Packit d28291
    ABORT("thread_resume failed");
Packit d28291
  if (GC_on_thread_event)
Packit d28291
    GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)(word)thread);
Packit d28291
}
Packit d28291
Packit d28291
/* Caller holds allocation lock, and has held it continuously since     */
Packit d28291
/* the world stopped.                                                   */
Packit d28291
GC_INNER void GC_start_world(void)
Packit d28291
{
Packit d28291
  task_t my_task = current_task();
Packit d28291
# ifdef DEBUG_THREADS
Packit d28291
    GC_log_printf("World starting\n");
Packit d28291
# endif
Packit d28291
# ifdef MPROTECT_VDB
Packit d28291
    if(GC_incremental) {
Packit d28291
      GC_mprotect_resume();
Packit d28291
    }
Packit d28291
# endif
Packit d28291
Packit d28291
  if (GC_query_task_threads) {
Packit d28291
#   ifndef GC_NO_THREADS_DISCOVERY
Packit d28291
      int i;
Packit d28291
      int j = GC_mach_threads_count;
Packit d28291
      kern_return_t kern_result;
Packit d28291
      thread_act_array_t act_list;
Packit d28291
      mach_msg_type_number_t listcount;
Packit d28291
Packit d28291
      kern_result = task_threads(my_task, &act_list, &listcount);
Packit d28291
      if (kern_result != KERN_SUCCESS)
Packit d28291
        ABORT("task_threads failed");
Packit d28291
Packit d28291
      for (i = 0; i < (int)listcount; i++) {
Packit d28291
        thread_act_t thread = act_list[i];
Packit d28291
        int last_found = j;        /* The thread index found during the   */
Packit d28291
                                   /* previous iteration (count value     */
Packit d28291
                                   /* means no thread found yet).         */
Packit d28291
Packit d28291
        /* Search for the thread starting from the last found one first.  */
Packit d28291
        while (++j < GC_mach_threads_count) {
Packit d28291
          if (GC_mach_threads[j].thread == thread)
Packit d28291
            break;
Packit d28291
        }
Packit d28291
        if (j >= GC_mach_threads_count) {
Packit d28291
          /* If not found, search in the rest (beginning) of the list.    */
Packit d28291
          for (j = 0; j < last_found; j++) {
Packit d28291
            if (GC_mach_threads[j].thread == thread)
Packit d28291
              break;
Packit d28291
          }
Packit d28291
        }
Packit d28291
Packit d28291
        if (j != last_found) {
Packit d28291
          /* The thread is found in GC_mach_threads.      */
Packit d28291
          if (GC_mach_threads[j].already_suspended) {
Packit d28291
#           ifdef DEBUG_THREADS
Packit d28291
              GC_log_printf("Not resuming already suspended thread %p\n",
Packit d28291
                            (void *)(word)thread);
Packit d28291
#           endif
Packit d28291
          } else {
Packit d28291
            GC_thread_resume(thread);
Packit d28291
          }
Packit d28291
        }
Packit d28291
Packit d28291
        mach_port_deallocate(my_task, thread);
Packit d28291
      }
Packit d28291
      vm_deallocate(my_task, (vm_address_t)act_list,
Packit d28291
                    sizeof(thread_t) * listcount);
Packit d28291
#   endif /* !GC_NO_THREADS_DISCOVERY */
Packit d28291
Packit d28291
  } else {
Packit d28291
    int i;
Packit d28291
    mach_port_t my_thread = mach_thread_self();
Packit d28291
Packit d28291
    for (i = 0; i < THREAD_TABLE_SZ; i++) {
Packit d28291
      GC_thread p;
Packit d28291
      for (p = GC_threads[i]; p != NULL; p = p->next) {
Packit d28291
        if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
Packit d28291
             p->stop_info.mach_thread != my_thread)
Packit d28291
          GC_thread_resume(p->stop_info.mach_thread);
Packit d28291
      }
Packit d28291
    }
Packit d28291
Packit d28291
    mach_port_deallocate(my_task, my_thread);
Packit d28291
  }
Packit d28291
Packit d28291
# ifdef DEBUG_THREADS
Packit d28291
    GC_log_printf("World started\n");
Packit d28291
# endif
Packit d28291
}
Packit d28291
Packit d28291
#endif /* GC_DARWIN_THREADS */