(linenum→info "unix/slp.c:2238")

gauche/0.8.12/gc/darwin_stop_world.c

    1: #include "private/pthread_support.h"
    2: 
    3: /* This probably needs more porting work to ppc64. */
    4: 
    5: # if defined(GC_DARWIN_THREADS)
    6: 
    7: /* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
    8:    Page 49:
    9:    "The space beneath the stack pointer, where a new stack frame would normally
   10:    be allocated, is called the red zone. This area as shown in Figure 3-2 may
   11:    be used for any purpose as long as a new stack frame does not need to be
   12:    added to the stack."
   13:    
   14:    Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
   15:    it must set up a stack frame just like routines that call other routines."
   16: */
   17: #ifdef POWERPC
   18: # if CPP_WORDSZ == 32
   19: #   define PPC_RED_ZONE_SIZE 224
   20: # elif CPP_WORDSZ == 64
   21: #   define PPC_RED_ZONE_SIZE 320
   22: # endif
   23: #endif
   24: 
   25: typedef struct StackFrame {
   26:   unsigned long savedSP;
   27:   unsigned long savedCR;
   28:   unsigned long savedLR;
   29:   unsigned long reserved[2];
   30:   unsigned long savedRTOC;
   31: } StackFrame;
   32: 
   33: unsigned long FindTopOfStack(unsigned int stack_start) {
   34:   StackFrame    *frame;
   35:   
   36:   if (stack_start == 0) {
   37: # ifdef POWERPC
   38: #   if CPP_WORDSZ == 32
   39:       __asm__ volatile("lwz     %0,0(r1)" : "=r" (frame));
   40: #   else
   41:       __asm__ volatile("ld      %0,0(r1)" : "=r" (frame));
   42: #   endif
   43: # endif
   44:   } else {
   45:     frame = (StackFrame *)stack_start;
   46:   }
   47: 
   48: # ifdef DEBUG_THREADS
   49:     /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
   50: # endif
   51:   do {
   52:     if (frame->savedSP == 0) break;
   53:                 /* if there are no more stack frames, stop */
   54: 
   55:     frame = (StackFrame*)frame->savedSP;
   56: 
   57:     /* we do these next two checks after going to the next frame
   58:        because the LR for the first stack frame in the loop
   59:        is not set up on purpose, so we shouldn't check it. */
   60:     if ((frame->savedLR & ~3) == 0) break; /* if the next LR is bogus, stop */
   61:     if ((~(frame->savedLR) & ~3) == 0) break; /* ditto */
   62:   } while (1); 
   63: 
   64: # ifdef DEBUG_THREADS
   65:     /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
   66: # endif
   67: 
   68:   return (unsigned long)frame;
   69: }       
   70: 
   71: #ifdef DARWIN_DONT_PARSE_STACK
   72: void GC_push_all_stacks() {
   73:   int i;
   74:   kern_return_t r;
   75:   GC_thread p;
   76:   pthread_t me;
   77:   ptr_t lo, hi;
   78: #if defined(POWERPC)
   79:   ppc_thread_state_t state;
   80:   mach_msg_type_number_t thread_state_count = PPC_THREAD_STATE_COUNT;
   81: #elif defined(I386)
   82:   i386_thread_state_t state;
   83:   mach_msg_type_number_t thread_state_count = i386_THREAD_STATE_COUNT;
   84: #else
   85: # error FIXME for non-x86 || ppc architectures
   86:   mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
   87: #endif
   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:                              GC_MACH_THREAD_STATE_FLAVOR,
  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, GC_MACH_THREAD_STATE_FLAVOR,
  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, GC_MACH_THREAD_STATE_FLAVOR,
  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:                   
  420:                   if(kern_result == KERN_SUCCESS) {   
  421:                           result = GC_suspend_thread_list(act_list, listcount,
  422:                                                                                           prev_list, prevcount);
  423:                           changes = result;
  424:                           
  425:                           if(prev_list != NULL) {
  426:                                   for(i = 0; i < prevcount; i++)
  427:                                           mach_port_deallocate(my_task, prev_list[i]);
  428:                                   
  429:                                   vm_deallocate(my_task, (vm_address_t)prev_list, sizeof(thread_t) * prevcount);
  430:                           }
  431:                           
  432:                           prev_list = act_list;
  433:                           prevcount = listcount;
  434:                   }           
  435:       } while (changes);
  436:           
  437:           for(i = 0; i < listcount; i++)
  438:                   mach_port_deallocate(my_task, act_list[i]);
  439:           
  440:           vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount);
  441:       
  442:  
  443: #   ifdef MPROTECT_VDB
  444:       if(GC_incremental) {
  445:         extern void GC_mprotect_stop();
  446:         GC_mprotect_stop();
  447:       }
  448: #   endif
  449:     
  450: #   ifdef PARALLEL_MARK
  451:       GC_release_mark_lock();
  452: #   endif
  453:     #if DEBUG_THREADS
  454:       GC_printf1("World stopped from 0x%lx\n", my_thread);
  455:     #endif
  456:           
  457:           mach_port_deallocate(my_task, my_thread);
  458: }
  459: 
  460: /* Caller holds allocation lock, and has held it continuously since     */
  461: /* the world stopped.                                                   */
  462: void GC_start_world()
  463: {
  464:   task_t my_task = current_task();
  465:   mach_port_t my_thread = mach_thread_self();
  466:   int i, j;
  467:   GC_thread p;
  468:   kern_return_t kern_result;
  469:   thread_act_array_t act_list;
  470:   mach_msg_type_number_t listcount;
  471:   struct thread_basic_info info;
  472:   mach_msg_type_number_t outCount = THREAD_INFO_MAX;
  473:   
  474: #   if DEBUG_THREADS
  475:       GC_printf0("World starting\n");
  476: #   endif
  477: 
  478: #   ifdef MPROTECT_VDB
  479:       if(GC_incremental) {
  480:         extern void GC_mprotect_resume();
  481:         GC_mprotect_resume();
  482:       }
  483: #   endif
  484: 
  485:     kern_result = task_threads(my_task, &act_list, &listcount);
  486:     for(i = 0; i < listcount; i++) {
  487:       thread_act_t thread = act_list[i];
  488:       if (thread != my_thread &&
  489:           (!GC_use_mach_handler_thread ||
  490:            (GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
  491:         for(j = 0; j < GC_mach_threads_count; j++) {
  492:           if (thread == GC_mach_threads[j].thread) {
  493:             if (GC_mach_threads[j].already_suspended) {
  494: #             if DEBUG_THREADS
  495:                 GC_printf1("Not resuming already suspended thread %p\n", thread);
  496: #             endif
  497:               continue;
  498:             }
  499:             kern_result = thread_info(thread, THREAD_BASIC_INFO,
  500:                                       (thread_info_t)&info, &outCount);
  501:             if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
  502: #           if DEBUG_THREADS
  503:               GC_printf2("Thread state for 0x%lx = %d\n", thread,
  504:                          info.run_state);
  505:               GC_printf1("Resuming 0x%lx\n", thread);
  506: #           endif
  507:             /* Resume the thread */
  508:             kern_result = thread_resume(thread);
  509:             if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
  510:           } 
  511:         }
  512:       }
  513:           
  514:           mach_port_deallocate(my_task, thread);
  515:     }
  516:     vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount);
  517:         
  518:         mach_port_deallocate(my_task, my_thread);
  519: #   if DEBUG_THREADS
  520:      GC_printf0("World started\n");
  521: #   endif
  522: }
  523: 
  524: void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
  525:   GC_mach_handler_thread = thread;
  526:   GC_use_mach_handler_thread = 1;
  527: }
  528: 
  529: #endif
1