/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple Page49: "Thespacebeneaththestackpointer,whereanewstackframewouldnormally beallocated,iscalledtheredzone.ThisareaasshowninFigure3-2may beusedforanypurposeaslongasanewstackframedoesnotneedtobe addedtothestack."
# ifdef DEBUG_THREADS_EXTRA
GC_log_printf("FindTopOfStack start at sp= %p\n", (void *)frame); # endif while (frame->savedSP != 0) { /* if there are no more stack frames, stop */
frame = (StackFrame*)frame->savedSP;
/* we do these next two checks after going to the next frame becausetheLRforthefirststackframeintheloop
is not set up on purpose, so we shouldn't check it. */ if ((frame->savedLR & ~0x3) == 0 || (frame->savedLR & ~0x3) == ~0x3UL) break; /* if the next LR is bogus, stop */
} # ifdef DEBUG_THREADS_EXTRA
GC_log_printf("FindTopOfStack finish at sp= %p\n", (void *)frame); # endif return (ptr_t)frame;
}
#endif/* !DARWIN_DONT_PARSE_STACK */
/* GC_query_task_threads controls whether to obtain the list of */ /* the threads from the kernel or to use GC_threads table. */ #ifdef GC_NO_THREADS_DISCOVERY # define GC_query_task_threads FALSE #elifdefined(GC_DISCOVER_TASK_THREADS) # define GC_query_task_threads TRUE #else STATIC GC_bool GC_query_task_threads = FALSE; #endif/* !GC_NO_THREADS_DISCOVERY */
/* Use implicit threads registration (all task threads excluding the GC */ /* special ones are stopped and scanned). Should be called before */ /* GC_INIT() (or, at least, before going multi-threaded). Deprecated. */
GC_API void GC_CALL GC_use_threads_discovery(void)
{ # ifdefined(GC_NO_THREADS_DISCOVERY) || defined(DARWIN_DONT_PARSE_STACK)
ABORT("Darwin task-threads-based stop and push unsupported"); # else # ifndef GC_ALWAYS_MULTITHREADED
GC_ASSERT(!GC_need_to_lock); # endif # ifndef GC_DISCOVER_TASK_THREADS
GC_query_task_threads = TRUE; # endif
GC_init_parallel(); /* just to be consistent with Win32 one */ # endif
}
/* Evaluates the stack range for a given thread. Returns the lower */ /* bound and sets *phi to the upper one. */ STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p,
GC_bool thread_blocked, mach_port_t my_thread,
ptr_t *paltstack_lo,
ptr_t *paltstack_hi GC_ATTR_UNUSED)
{
ptr_t lo; if (thread == my_thread) {
GC_ASSERT(!thread_blocked);
lo = GC_approx_sp(); # ifndef DARWIN_DONT_PARSE_STACK
*phi = GC_FindTopOfStack(0); # endif
} elseif (thread_blocked) { # ifdefined(CPPCHECK) if (NULL == p) ABORT("Invalid GC_thread passed to GC_stack_range_for"); # endif
lo = p->stop_info.stack_ptr; # ifndef DARWIN_DONT_PARSE_STACK
*phi = p->topOfStack; # endif
} else { /* MACHINE_THREAD_STATE_COUNT does not seem to be defined */ /* everywhere. Hence we use our own version. Alternatively, */ /* we could use THREAD_STATE_MAX (but seems to be not optimal). */
kern_return_t kern_result;
GC_THREAD_STATE_T state;
# ifdefined(ARM32) && defined(ARM_THREAD_STATE32) /* Use ARM_UNIFIED_THREAD_STATE on iOS8+ 32-bit targets and on */ /* 64-bit H/W (iOS7+ 32-bit mode). */
size_t size; static cpu_type_t cputype = 0;
# ifndef DARWIN_DONT_PARSE_STACK if (GC_query_task_threads) { int i;
kern_return_t kern_result;
thread_act_array_t act_list = 0;
/* Obtain the list of the threads from the kernel. */
kern_result = task_threads(my_task, &act_list, &listcount); if (kern_result != KERN_SUCCESS)
ABORT("task_threads failed");
for (i = 0; i < (int)listcount; i++) {
thread_act_t thread = act_list[i];
ptr_t lo = GC_stack_range_for(&hi, thread, NULL, FALSE, my_thread,
&altstack_lo, &altstack_hi);
if (lo) {
GC_ASSERT((word)lo <= (word)hi);
total_size += hi - lo;
GC_push_all_stack(lo, hi);
} /* TODO: Handle altstack */
nthreads++; if (thread == my_thread)
found_me = TRUE;
mach_port_deallocate(my_task, thread);
} /* for (i=0; ...) */
struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS]; STATICint GC_mach_threads_count = 0; /* FIXME: it is better to implement GC_mach_threads as a hash set. */
/* returns true if there's a thread in act_list that wasn't in old_list */ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count,
thread_act_array_t old_list, int old_count, task_t my_task,
mach_port_t my_thread)
{ int i; int j = -1;
GC_bool changed = FALSE;
for (i = 0; i < count; i++) {
thread_act_t thread = act_list[i];
GC_bool found;
kern_return_t kern_result;
if (thread == my_thread # ifdef MPROTECT_VDB
|| (GC_mach_handler_thread == thread && GC_use_mach_handler_thread) # endif # ifdef PARALLEL_MARK
|| GC_is_mach_marker(thread) /* ignore the parallel markers */ # endif
) { /* Do not add our one, parallel marker and the handler threads; */ /* consider it as found (e.g., it was processed earlier). */
mach_port_deallocate(my_task, thread); continue;
}
/* find the current thread in the old list */
found = FALSE;
{ int last_found = j; /* remember the previous found thread index */
/* Search for the thread starting from the last found one first. */ while (++j < old_count) if (old_list[j] == thread) {
found = TRUE; break;
} if (!found) { /* If not found, search in the rest (beginning) of the list. */ for (j = 0; j < last_found; j++) if (old_list[j] == thread) {
found = TRUE; break;
}
}
}
if (found) { /* It is already in the list, skip processing, release mach port. */
mach_port_deallocate(my_task, thread); continue;
}
/* add it to the GC_mach_threads list */ if (GC_mach_threads_count == GC_MAX_MACH_THREADS)
ABORT("Too many threads");
GC_mach_threads[GC_mach_threads_count].thread = thread; /* default is not suspended */
GC_mach_threads[GC_mach_threads_count].suspended = FALSE;
changed = TRUE;
# ifdef DEBUG_THREADS
GC_log_printf("Suspending %p\n", (void *)(word)thread); # endif /* Unconditionally suspend the thread. It will do no */ /* harm if it is already suspended by the client logic. */
GC_acquire_dirty_lock(); do {
kern_result = thread_suspend(thread);
} while (kern_result == KERN_ABORTED);
GC_release_dirty_lock(); if (kern_result != KERN_SUCCESS) { /* The thread may have quit since the thread_threads() call we */ /* mark already suspended so it's not dealt with anymore later. */
GC_mach_threads[GC_mach_threads_count].suspended = FALSE;
} else { /* Mark the thread as suspended and require resume. */
GC_mach_threads[GC_mach_threads_count].suspended = TRUE; if (GC_on_thread_event)
GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)thread);
}
GC_mach_threads_count++;
} return changed;
}
# ifdef DEBUG_THREADS
GC_log_printf("Stopping the world from thread %p\n",
(void *)(word)my_thread); # endif # ifdef PARALLEL_MARK if (GC_parallel) { /* Make sure all free list construction has stopped before we */ /* start. No new construction can start, since free list */ /* construction is required to acquire and release the GC lock */ /* before it starts, and we have the lock. */
GC_acquire_mark_lock();
GC_ASSERT(GC_fl_builder_count == 0); /* We should have previously waited for it to become zero. */
} # endif /* PARALLEL_MARK */
/* Clear out the mach threads list table. We do not need to */ /* really clear GC_mach_threads[] as it is used only in the range */ /* from 0 to GC_mach_threads_count-1, inclusive. */
GC_mach_threads_count = 0;
/* Loop stopping threads until you have gone over the whole list */ /* twice without a new one appearing. thread_create() won't */ /* return (and thus the thread stop) until the new thread exists, */ /* so there is no window whereby you could stop a thread, */ /* recognize it is stopped, but then have a new thread it created */ /* before stopping show up later. */
changed = TRUE;
prev_list = NULL;
prevcount = 0; do {
kern_result = task_threads(my_task, &act_list, &listcount);
if (prev_list != NULL) { /* Thread ports are not deallocated by list, unused ports */ /* deallocated in GC_suspend_thread_list, used - kept in */ /* GC_mach_threads till GC_start_world as otherwise thread */ /* object change can occur and GC_start_world will not */ /* find the thread to resume which will cause app to hang. */
vm_deallocate(my_task, (vm_address_t)prev_list, sizeof(thread_t) * prevcount);
}
/* Repeat while having changes. */
prev_list = act_list;
prevcount = listcount;
}
} while (changed);
GC_ASSERT(prev_list != 0); /* The thread ports are not deallocated by list, see above. */
vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount); # endif /* !GC_NO_THREADS_DISCOVERY */
} else { unsigned i;
for (i = 0; i < THREAD_TABLE_SZ; i++) {
GC_thread p;
for (p = GC_threads[i]; p != NULL; p = p->next) { if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
p->stop_info.mach_thread != my_thread) {
GC_acquire_dirty_lock(); do {
kern_result = thread_suspend(p->stop_info.mach_thread);
} while (kern_result == KERN_ABORTED);
GC_release_dirty_lock(); if (kern_result != KERN_SUCCESS)
ABORT("thread_suspend failed"); if (GC_on_thread_event)
GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED,
(void *)(word)p->stop_info.mach_thread);
}
}
}
}
# ifdef MPROTECT_VDB if (GC_auto_incremental) {
GC_mprotect_stop();
} # endif # ifdef PARALLEL_MARK if (GC_parallel)
GC_release_mark_lock(); # endif
/* Caller holds allocation lock, and has held it continuously since */ /* the world stopped. */
GC_INNER void GC_start_world(void)
{
task_t my_task = current_task(); # ifdef DEBUG_THREADS
GC_log_printf("World starting\n"); # endif # ifdef MPROTECT_VDB if (GC_auto_incremental) {
GC_mprotect_resume();
} # endif
if (GC_query_task_threads) { # ifndef GC_NO_THREADS_DISCOVERY int i, j;
kern_return_t kern_result;
thread_act_array_t act_list;
mach_msg_type_number_t listcount;
j = (int)listcount; for (i = 0; i < GC_mach_threads_count; i++) {
thread_act_t thread = GC_mach_threads[i].thread;
if (GC_mach_threads[i].suspended) { int last_found = j; /* The thread index found during the */ /* previous iteration (count value */ /* means no thread found yet). */
/* Search for the thread starting from the last found one first. */ while (++j < (int)listcount) { if (act_list[j] == thread) break;
} if (j >= (int)listcount) { /* If not found, search in the rest (beginning) of the list. */ for (j = 0; j < last_found; j++) { if (act_list[j] == thread) break;
}
} if (j != last_found) { /* The thread is alive, resume it. */
GC_thread_resume(thread);
}
} else { /* This thread was failed to be suspended by GC_stop_world, */ /* no action needed. */ # ifdef DEBUG_THREADS
GC_log_printf("Not resuming thread %p as it is not suspended\n",
(void *)(word)thread); # endif
}
mach_port_deallocate(my_task, thread);
}
for (i = 0; i < (int)listcount; i++)
mach_port_deallocate(my_task, act_list[i]);
vm_deallocate(my_task, (vm_address_t)act_list, sizeof(thread_t) * listcount); # endif /* !GC_NO_THREADS_DISCOVERY */
} else { int i;
mach_port_t my_thread = mach_thread_self();
for (i = 0; i < THREAD_TABLE_SZ; i++) {
GC_thread p; for (p = GC_threads[i]; p != NULL; p = p->next) { if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
p->stop_info.mach_thread != my_thread)
GC_thread_resume(p->stop_info.mach_thread);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.