Back to index

plt-scheme  4.2.1
darwin_stop_world.c
Go to the documentation of this file.
00001 #include "private/pthread_support.h"
00002 
00003 /* This probably needs more porting work to ppc64. */
00004 
00005 # if defined(GC_DARWIN_THREADS)
00006 
00007 /* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple
00008    Page 49:
00009    "The space beneath the stack pointer, where a new stack frame would normally
00010    be allocated, is called the red zone. This area as shown in Figure 3-2 may
00011    be used for any purpose as long as a new stack frame does not need to be
00012    added to the stack."
00013    
00014    Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then
00015    it must set up a stack frame just like routines that call other routines."
00016 */
00017 #ifdef POWERPC
00018 # if CPP_WORDSZ == 32
00019 #   define PPC_RED_ZONE_SIZE 224
00020 # elif CPP_WORDSZ == 64
00021 #   define PPC_RED_ZONE_SIZE 320
00022 # endif
00023 #endif
00024 
00025 typedef struct StackFrame {
00026   unsigned long      savedSP;
00027   unsigned long      savedCR;
00028   unsigned long      savedLR;
00029   unsigned long      reserved[2];
00030   unsigned long      savedRTOC;
00031 } StackFrame;
00032 
00033 unsigned long FindTopOfStack(unsigned int stack_start) {
00034   StackFrame  *frame;
00035   
00036   if (stack_start == 0) {
00037 # ifdef POWERPC
00038 #   if CPP_WORDSZ == 32
00039       __asm__ volatile("lwz %0,0(r1)" : "=r" (frame));
00040 #   else
00041       __asm__ volatile("ld  %0,0(r1)" : "=r" (frame));
00042 #   endif
00043 # endif
00044   } else {
00045     frame = (StackFrame *)stack_start;
00046   }
00047 
00048 # ifdef DEBUG_THREADS
00049     /* GC_printf1("FindTopOfStack start at sp = %p\n", frame); */
00050 # endif
00051   do {
00052     if (frame->savedSP == 0) break;
00053               /* if there are no more stack frames, stop */
00054 
00055     frame = (StackFrame*)frame->savedSP;
00056 
00057     /* we do these next two checks after going to the next frame
00058        because the LR for the first stack frame in the loop
00059        is not set up on purpose, so we shouldn't check it. */
00060     if ((frame->savedLR & ~3) == 0) break; /* if the next LR is bogus, stop */
00061     if ((~(frame->savedLR) & ~3) == 0) break; /* ditto */
00062   } while (1); 
00063 
00064 # ifdef DEBUG_THREADS
00065     /* GC_printf1("FindTopOfStack finish at sp = %p\n", frame); */
00066 # endif
00067 
00068   return (unsigned long)frame;
00069 }      
00070 
00071 #ifdef DARWIN_DONT_PARSE_STACK
00072 void GC_push_all_stacks() {
00073   int i;
00074   kern_return_t r;
00075   GC_thread p;
00076   pthread_t me;
00077   ptr_t lo, hi;
00078 #if defined(POWERPC)
00079   ppc_thread_state_t state;
00080 #elif defined(I386)
00081   i386_thread_state_t state;
00082 #else
00083 # error FIXME for non-x86 || ppc architectures
00084 #endif
00085   mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
00086   
00087   me = pthread_self();
00088   if (!GC_thr_initialized) GC_thr_init();
00089   
00090   for(i=0;i<THREAD_TABLE_SZ;i++) {
00091     for(p=GC_threads[i];p!=0;p=p->next) {
00092       if(p -> flags & FINISHED) continue;
00093       if(pthread_equal(p->id,me)) {
00094        lo = GC_approx_sp();
00095       } else {
00096        /* Get the thread state (registers, etc) */
00097        r = thread_get_state(
00098                           p->stop_info.mach_thread,
00099                           MACHINE_THREAD_STATE,
00100                           (natural_t*)&state,
00101                           &thread_state_count);
00102        if(r != KERN_SUCCESS) ABORT("thread_get_state failed");
00103        
00104 #if defined(I386)
00105        lo = state.__esp;
00106 
00107        GC_push_one(state.__eax); 
00108        GC_push_one(state.__ebx); 
00109        GC_push_one(state.__ecx); 
00110        GC_push_one(state.__edx); 
00111        GC_push_one(state.__edi); 
00112        GC_push_one(state.__esi); 
00113        GC_push_one(state.__ebp); 
00114 #elif defined(POWERPC)
00115        lo = (void*)(state.r1 - PPC_RED_ZONE_SIZE);
00116         
00117        GC_push_one(state.r0); 
00118        GC_push_one(state.r2); 
00119        GC_push_one(state.r3); 
00120        GC_push_one(state.r4); 
00121        GC_push_one(state.r5); 
00122        GC_push_one(state.r6); 
00123        GC_push_one(state.r7); 
00124        GC_push_one(state.r8); 
00125        GC_push_one(state.r9); 
00126        GC_push_one(state.r10); 
00127        GC_push_one(state.r11); 
00128        GC_push_one(state.r12); 
00129        GC_push_one(state.r13); 
00130        GC_push_one(state.r14); 
00131        GC_push_one(state.r15); 
00132        GC_push_one(state.r16); 
00133        GC_push_one(state.r17); 
00134        GC_push_one(state.r18); 
00135        GC_push_one(state.r19); 
00136        GC_push_one(state.r20); 
00137        GC_push_one(state.r21); 
00138        GC_push_one(state.r22); 
00139        GC_push_one(state.r23); 
00140        GC_push_one(state.r24); 
00141        GC_push_one(state.r25); 
00142        GC_push_one(state.r26); 
00143        GC_push_one(state.r27); 
00144        GC_push_one(state.r28); 
00145        GC_push_one(state.r29); 
00146        GC_push_one(state.r30); 
00147        GC_push_one(state.r31);
00148 #else
00149 # error FIXME for non-x86 || ppc architectures
00150 #endif
00151       } /* p != me */
00152       if(p->flags & MAIN_THREAD)
00153        hi = GC_stackbottom;
00154       else
00155        hi = p->stack_end;
00156 #if DEBUG_THREADS
00157       GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
00158                (unsigned long) p -> id,
00159                (unsigned long) lo,
00160                (unsigned long) hi
00161                );
00162 #endif
00163       GC_push_all_stack(lo,hi);
00164     } /* for(p=GC_threads[i]...) */
00165   } /* for(i=0;i<THREAD_TABLE_SZ...) */
00166 }
00167 
00168 #else /* !DARWIN_DONT_PARSE_STACK; Use FindTopOfStack() */
00169 
00170 void GC_push_all_stacks() {
00171     int i;
00172     kern_return_t r;
00173     mach_port_t me;
00174     ptr_t lo, hi;
00175     thread_act_array_t act_list = 0;
00176     mach_msg_type_number_t listcount = 0;
00177 
00178     me = mach_thread_self();
00179     if (!GC_thr_initialized) GC_thr_init();
00180     
00181     r = task_threads(current_task(), &act_list, &listcount);
00182     if(r != KERN_SUCCESS) ABORT("task_threads failed");
00183     for(i = 0; i < listcount; i++) {
00184       thread_act_t thread = act_list[i];
00185       if (thread == me) {
00186        lo = GC_approx_sp();
00187        hi = (ptr_t)FindTopOfStack(0);
00188       } else {
00189 #     if defined(POWERPC)
00190 #      if CPP_WORDSZ == 32
00191        ppc_thread_state_t info;
00192 #      else
00193        ppc_thread_state64_t info;
00194 #      endif
00195        mach_msg_type_number_t outCount = THREAD_STATE_MAX;
00196        r = thread_get_state(thread, MACHINE_THREAD_STATE,
00197                           (natural_t *)&info, &outCount);
00198        if(r != KERN_SUCCESS) ABORT("task_get_state failed");
00199 
00200        lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
00201        hi = (ptr_t)FindTopOfStack(info.r1);
00202 
00203        GC_push_one(info.r0); 
00204        GC_push_one(info.r2); 
00205        GC_push_one(info.r3); 
00206        GC_push_one(info.r4); 
00207        GC_push_one(info.r5); 
00208        GC_push_one(info.r6); 
00209        GC_push_one(info.r7); 
00210        GC_push_one(info.r8); 
00211        GC_push_one(info.r9); 
00212        GC_push_one(info.r10); 
00213        GC_push_one(info.r11); 
00214        GC_push_one(info.r12); 
00215        GC_push_one(info.r13); 
00216        GC_push_one(info.r14); 
00217        GC_push_one(info.r15); 
00218        GC_push_one(info.r16); 
00219        GC_push_one(info.r17); 
00220        GC_push_one(info.r18); 
00221        GC_push_one(info.r19); 
00222        GC_push_one(info.r20); 
00223        GC_push_one(info.r21); 
00224        GC_push_one(info.r22); 
00225        GC_push_one(info.r23); 
00226        GC_push_one(info.r24); 
00227        GC_push_one(info.r25); 
00228        GC_push_one(info.r26); 
00229        GC_push_one(info.r27); 
00230        GC_push_one(info.r28); 
00231        GC_push_one(info.r29); 
00232        GC_push_one(info.r30); 
00233        GC_push_one(info.r31);
00234 #      else
00235        /* FIXME: Remove after testing:    */
00236        WARN("This is completely untested and likely will not work\n", 0);
00237        i386_thread_state_t info;
00238        mach_msg_type_number_t outCount = THREAD_STATE_MAX;
00239        r = thread_get_state(thread, MACHINE_THREAD_STATE,
00240                           (natural_t *)&info, &outCount);
00241        if(r != KERN_SUCCESS) ABORT("task_get_state failed");
00242 
00243        lo = (void*)info.esp;
00244        hi = (ptr_t)FindTopOfStack(info.esp);
00245 
00246        GC_push_one(info.eax); 
00247        GC_push_one(info.ebx); 
00248        GC_push_one(info.ecx); 
00249        GC_push_one(info.edx); 
00250        GC_push_one(info.edi); 
00251        GC_push_one(info.esi); 
00252        /* GC_push_one(info.ebp);  */
00253        /* GC_push_one(info.esp);  */
00254        GC_push_one(info.ss); 
00255        GC_push_one(info.eip); 
00256        GC_push_one(info.cs); 
00257        GC_push_one(info.ds); 
00258        GC_push_one(info.es); 
00259        GC_push_one(info.fs); 
00260        GC_push_one(info.gs); 
00261 #      endif /* !POWERPC */
00262       }
00263 #     if DEBUG_THREADS
00264        GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
00265                 (unsigned long) thread,
00266                 (unsigned long) lo,
00267                 (unsigned long) hi
00268                );
00269 #     endif
00270       GC_push_all_stack(lo, hi); 
00271     } /* for(p=GC_threads[i]...) */
00272     vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
00273 }
00274 #endif /* !DARWIN_DONT_PARSE_STACK */
00275 
00276 static mach_port_t GC_mach_handler_thread;
00277 static int GC_use_mach_handler_thread = 0;
00278 
00279 static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
00280 static int GC_mach_threads_count;
00281 
00282 void GC_stop_init() {
00283   int i;
00284 
00285   for (i = 0; i < THREAD_TABLE_SZ; i++) {
00286     GC_mach_threads[i].thread = 0;
00287     GC_mach_threads[i].already_suspended = 0;
00288   }
00289   GC_mach_threads_count = 0;
00290 }
00291 
00292 /* returns true if there's a thread in act_list that wasn't in old_list */
00293 int GC_suspend_thread_list(thread_act_array_t act_list, int count, 
00294                         thread_act_array_t old_list, int old_count) {
00295   mach_port_t my_thread = mach_thread_self();
00296   int i, j;
00297 
00298   int changed = 0;
00299 
00300   for(i = 0; i < count; i++) {
00301     thread_act_t thread = act_list[i];
00302 #   if DEBUG_THREADS 
00303       GC_printf1("Attempting to suspend thread %p\n", thread);
00304 #   endif
00305     /* find the current thread in the old list */
00306     int found = 0;
00307     for(j = 0; j < old_count; j++) {
00308       thread_act_t old_thread = old_list[j];
00309       if (old_thread == thread) {
00310        found = 1;
00311        break;
00312       }
00313     }
00314     if (!found) {
00315       /* add it to the GC_mach_threads list */
00316       GC_mach_threads[GC_mach_threads_count].thread = thread;
00317       /* default is not suspended */
00318       GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
00319       changed = 1;
00320     }      
00321 
00322     if (thread != my_thread &&
00323        (!GC_use_mach_handler_thread
00324         || (GC_use_mach_handler_thread
00325             && GC_mach_handler_thread != thread))) {
00326       struct thread_basic_info info;
00327       mach_msg_type_number_t outCount = THREAD_INFO_MAX;
00328       kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
00329                             (thread_info_t)&info, &outCount);
00330       if(kern_result != KERN_SUCCESS) {
00331        /* the thread may have quit since the thread_threads () call 
00332         * we mark already_suspended so it's not dealt with anymore later
00333         */
00334         if (!found) {
00335          GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
00336          GC_mach_threads_count++;
00337        }
00338        continue;
00339       }
00340 #     if DEBUG_THREADS
00341         GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
00342 #     endif
00343       if (!found) {
00344        GC_mach_threads[GC_mach_threads_count].already_suspended = info.suspend_count;
00345       }
00346       if (info.suspend_count) continue;
00347       
00348 #     if DEBUG_THREADS
00349         GC_printf1("Suspending 0x%lx\n", thread);
00350 #     endif
00351       /* Suspend the thread */
00352       kern_result = thread_suspend(thread);
00353       if(kern_result != KERN_SUCCESS) {
00354        /* the thread may have quit since the thread_threads () call 
00355         * we mark already_suspended so it's not dealt with anymore later
00356         */
00357         if (!found) {
00358          GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
00359          GC_mach_threads_count++;
00360        }
00361        continue;
00362       }
00363     } 
00364     if (!found) GC_mach_threads_count++;
00365   }
00366   return changed;
00367 }
00368 
00369 
00370 /* Caller holds allocation lock.   */
00371 void GC_stop_world()
00372 {
00373   int i, changes;
00374     GC_thread p;
00375     mach_port_t my_thread = mach_thread_self();
00376     kern_return_t kern_result;
00377     thread_act_array_t act_list, prev_list;
00378     mach_msg_type_number_t listcount, prevcount;
00379     
00380 #   if DEBUG_THREADS
00381       GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
00382 #   endif
00383 
00384     /* clear out the mach threads list table */
00385     GC_stop_init(); 
00386        
00387     /* Make sure all free list construction has stopped before we start. */
00388     /* No new construction can start, since free list construction is */
00389     /* required to acquire and release the GC lock before it starts,  */
00390     /* and we have the lock.                                          */
00391 #   ifdef PARALLEL_MARK
00392       GC_acquire_mark_lock();
00393       GC_ASSERT(GC_fl_builder_count == 0);
00394       /* We should have previously waited for it to become zero. */
00395 #   endif /* PARALLEL_MARK */
00396 
00397       /* Loop stopping threads until you have gone over the whole list
00398         twice without a new one appearing. thread_create() won't
00399         return (and thus the thread stop) until the new thread
00400         exists, so there is no window whereby you could stop a
00401         thread, recognise it is stopped, but then have a new thread
00402         it created before stopping show up later.
00403       */
00404       
00405       changes = 1;
00406       prev_list = NULL;
00407       prevcount = 0;
00408       do {
00409        int result;
00410        kern_result = task_threads(current_task(), &act_list, &listcount);
00411        result = GC_suspend_thread_list(act_list, listcount,
00412                                    prev_list, prevcount);
00413        changes = result;
00414        prev_list = act_list;
00415        prevcount = listcount;
00416         vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
00417       } while (changes);
00418       
00419  
00420 #   ifdef MPROTECT_VDB
00421       if(GC_incremental) {
00422         extern void GC_mprotect_stop();
00423         GC_mprotect_stop();
00424       }
00425 #   endif
00426     
00427 #   ifdef PARALLEL_MARK
00428       GC_release_mark_lock();
00429 #   endif
00430     #if DEBUG_THREADS
00431       GC_printf1("World stopped from 0x%lx\n", my_thread);
00432     #endif
00433 }
00434 
00435 /* Caller holds allocation lock, and has held it continuously since   */
00436 /* the world stopped.                                                 */
00437 void GC_start_world()
00438 {
00439   mach_port_t my_thread = mach_thread_self();
00440   int i, j;
00441   GC_thread p;
00442   kern_return_t kern_result;
00443   thread_act_array_t act_list;
00444   mach_msg_type_number_t listcount;
00445   struct thread_basic_info info;
00446   mach_msg_type_number_t outCount = THREAD_INFO_MAX;
00447   
00448 #   if DEBUG_THREADS
00449       GC_printf0("World starting\n");
00450 #   endif
00451 
00452 #   ifdef MPROTECT_VDB
00453       if(GC_incremental) {
00454         extern void GC_mprotect_resume();
00455         GC_mprotect_resume();
00456       }
00457 #   endif
00458 
00459     kern_result = task_threads(current_task(), &act_list, &listcount);
00460     for(i = 0; i < listcount; i++) {
00461       thread_act_t thread = act_list[i];
00462       if (thread != my_thread &&
00463          (!GC_use_mach_handler_thread ||
00464           (GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
00465        for(j = 0; j < GC_mach_threads_count; j++) {
00466          if (thread == GC_mach_threads[j].thread) {
00467            if (GC_mach_threads[j].already_suspended) {
00468 #             if DEBUG_THREADS
00469                GC_printf1("Not resuming already suspended thread %p\n", thread);
00470 #             endif
00471              continue;
00472            }
00473            kern_result = thread_info(thread, THREAD_BASIC_INFO,
00474                                   (thread_info_t)&info, &outCount);
00475            if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
00476 #           if DEBUG_THREADS
00477              GC_printf2("Thread state for 0x%lx = %d\n", thread,
00478                       info.run_state);
00479              GC_printf1("Resuming 0x%lx\n", thread);
00480 #           endif
00481            /* Resume the thread */
00482            kern_result = thread_resume(thread);
00483            if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
00484          } 
00485        }
00486       }
00487     }
00488     vm_deallocate(current_task(), (vm_address_t)act_list, sizeof(thread_t) * listcount);
00489 #   if DEBUG_THREADS
00490      GC_printf0("World started\n");
00491 #   endif
00492 }
00493 
00494 void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
00495   GC_mach_handler_thread = thread;
00496   GC_use_mach_handler_thread = 1;
00497 }
00498 
00499 #endif