darwin_stop_world.c [plain text]
#include "private/pthread_support.h"
# if defined(GC_DARWIN_THREADS)
#define PPC_RED_ZONE_SIZE 224
typedef struct StackFrame {
unsigned int savedSP;
unsigned int savedCR;
unsigned int savedLR;
unsigned int reserved[2];
unsigned int savedRTOC;
} StackFrame;
unsigned int FindTopOfStack(unsigned int stack_start) {
StackFrame *frame;
if (stack_start == 0) {
__asm__ volatile("lwz %0,0(r1)" : "=r" (frame));
} else {
frame = (StackFrame *)stack_start;
}
# ifdef DEBUG_THREADS
# endif
do {
if (frame->savedSP == NULL) break;
frame = (StackFrame*)frame->savedSP;
if ((frame->savedLR & ~3) == 0) break;
if ((~(frame->savedLR) & ~3) == 0) break;
} while (1);
# ifdef DEBUG_THREADS
# endif
return (unsigned int)frame;
}
void GC_push_all_stacks() {
int i;
kern_return_t r;
mach_port_t me;
ptr_t lo, hi;
thread_act_array_t act_list = 0;
mach_msg_type_number_t listcount = 0;
me = mach_thread_self();
if (!GC_thr_initialized) GC_thr_init();
r = task_threads(current_task(), &act_list, &listcount);
if(r != KERN_SUCCESS) ABORT("task_threads failed");
for(i = 0; i < listcount; i++) {
thread_act_t thread = act_list[i];
if (thread == me) {
lo = GC_approx_sp();
hi = (ptr_t)FindTopOfStack(0);
} else {
# ifdef POWERPC
ppc_thread_state_t info;
mach_msg_type_number_t outCount = THREAD_STATE_MAX;
r = thread_get_state(thread, MACHINE_THREAD_STATE,
(natural_t *)&info, &outCount);
if(r != KERN_SUCCESS) ABORT("task_get_state failed");
lo = (void*)(info.r1 - PPC_RED_ZONE_SIZE);
hi = (ptr_t)FindTopOfStack(info.r1);
GC_push_one(info.r0);
GC_push_one(info.r2);
GC_push_one(info.r3);
GC_push_one(info.r4);
GC_push_one(info.r5);
GC_push_one(info.r6);
GC_push_one(info.r7);
GC_push_one(info.r8);
GC_push_one(info.r9);
GC_push_one(info.r10);
GC_push_one(info.r11);
GC_push_one(info.r12);
GC_push_one(info.r13);
GC_push_one(info.r14);
GC_push_one(info.r15);
GC_push_one(info.r16);
GC_push_one(info.r17);
GC_push_one(info.r18);
GC_push_one(info.r19);
GC_push_one(info.r20);
GC_push_one(info.r21);
GC_push_one(info.r22);
GC_push_one(info.r23);
GC_push_one(info.r24);
GC_push_one(info.r25);
GC_push_one(info.r26);
GC_push_one(info.r27);
GC_push_one(info.r28);
GC_push_one(info.r29);
GC_push_one(info.r30);
GC_push_one(info.r31);
# else
WARN("This is completely untested and likely will not work\n", 0);
i386_thread_state_t info;
mach_msg_type_number_t outCount = THREAD_STATE_MAX;
r = thread_get_state(thread, MACHINE_THREAD_STATE,
(natural_t *)&info, &outCount);
if(r != KERN_SUCCESS) ABORT("task_get_state failed");
lo = (void*)info.esp;
hi = (ptr_t)FindTopOfStack(info.esp);
GC_push_one(info.eax);
GC_push_one(info.ebx);
GC_push_one(info.ecx);
GC_push_one(info.edx);
GC_push_one(info.edi);
GC_push_one(info.esi);
GC_push_one(info.ss);
GC_push_one(info.eip);
GC_push_one(info.cs);
GC_push_one(info.ds);
GC_push_one(info.es);
GC_push_one(info.fs);
GC_push_one(info.gs);
# endif
}
# if DEBUG_THREADS
GC_printf3("Darwin: Stack for thread 0x%lx = [%lx,%lx)\n",
(unsigned long) thread,
(unsigned long) lo,
(unsigned long) hi
);
# endif
GC_push_all_stack(lo, hi);
}
}
static mach_port_t GC_mach_handler_thread;
static int GC_use_mach_handler_thread = 0;
static struct GC_mach_thread GC_mach_threads[THREAD_TABLE_SZ];
static int GC_mach_threads_count;
void GC_stop_init() {
int i;
for (i = 0; i < THREAD_TABLE_SZ; i++) {
GC_mach_threads[i].thread = 0;
GC_mach_threads[i].already_suspended = 0;
}
GC_mach_threads_count = 0;
}
int GC_suspend_thread_list(thread_act_array_t act_list, int count,
thread_act_array_t old_list, int old_count) {
mach_port_t my_thread = mach_thread_self();
int i, j;
int changed = 0;
for(i = 0; i < count; i++) {
thread_act_t thread = act_list[i];
# if DEBUG_THREADS
GC_printf1("Attempting to suspend thread %p\n", thread);
# endif
int found = 0;
for(j = 0; j < old_count; j++) {
thread_act_t old_thread = old_list[j];
if (old_thread == thread) {
found = 1;
break;
}
}
if (!found) {
GC_mach_threads[GC_mach_threads_count].thread = thread;
GC_mach_threads[GC_mach_threads_count].already_suspended = 0;
changed = 1;
}
if (thread != my_thread &&
(!GC_use_mach_handler_thread
|| (GC_use_mach_handler_thread
&& GC_mach_handler_thread != thread))) {
struct thread_basic_info info;
mach_msg_type_number_t outCount = THREAD_INFO_MAX;
kern_return_t kern_result = thread_info(thread, THREAD_BASIC_INFO,
(thread_info_t)&info, &outCount);
if(kern_result != KERN_SUCCESS) {
if (!found) {
GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
GC_mach_threads_count++;
}
continue;
}
# if DEBUG_THREADS
GC_printf2("Thread state for 0x%lx = %d\n", thread, info.run_state);
# endif
if (!found) {
GC_mach_threads[GC_mach_threads_count].already_suspended = info.suspend_count;
}
if (info.suspend_count) continue;
# if DEBUG_THREADS
GC_printf1("Suspending 0x%lx\n", thread);
# endif
kern_result = thread_suspend(thread);
if(kern_result != KERN_SUCCESS) {
if (!found) {
GC_mach_threads[GC_mach_threads_count].already_suspended = TRUE;
GC_mach_threads_count++;
}
continue;
}
}
if (!found) GC_mach_threads_count++;
}
return changed;
}
void GC_stop_world()
{
int i, changes;
GC_thread p;
mach_port_t my_thread = mach_thread_self();
kern_return_t kern_result;
thread_act_array_t act_list, prev_list;
mach_msg_type_number_t listcount, prevcount;
# if DEBUG_THREADS
GC_printf1("Stopping the world from 0x%lx\n", mach_thread_self());
# endif
GC_stop_init();
# ifdef PARALLEL_MARK
GC_acquire_mark_lock();
GC_ASSERT(GC_fl_builder_count == 0);
# endif
changes = 1;
prev_list = NULL;
prevcount = 0;
do {
int result;
kern_result = task_threads(current_task(), &act_list, &listcount);
result = GC_suspend_thread_list(act_list, listcount,
prev_list, prevcount);
changes = result;
prev_list = act_list;
prevcount = listcount;
} while (changes);
# ifdef MPROTECT_VDB
if(GC_incremental) {
extern void GC_mprotect_stop();
GC_mprotect_stop();
}
# endif
# ifdef PARALLEL_MARK
GC_release_mark_lock();
# endif
#if DEBUG_THREADS
GC_printf1("World stopped from 0x%lx\n", my_thread);
#endif
}
void GC_start_world()
{
mach_port_t my_thread = mach_thread_self();
int i, j;
GC_thread p;
kern_return_t kern_result;
thread_act_array_t act_list;
mach_msg_type_number_t listcount;
# if DEBUG_THREADS
GC_printf0("World starting\n");
# endif
# ifdef MPROTECT_VDB
if(GC_incremental) {
extern void GC_mprotect_resume();
GC_mprotect_resume();
}
# endif
kern_result = task_threads(current_task(), &act_list, &listcount);
for(i = 0; i < listcount; i++) {
thread_act_t thread = act_list[i];
if (thread != my_thread &&
(!GC_use_mach_handler_thread ||
(GC_use_mach_handler_thread && GC_mach_handler_thread != thread))) {
for(j = 0; j < GC_mach_threads_count; j++) {
if (thread == GC_mach_threads[j].thread) {
if (GC_mach_threads[j].already_suspended) {
# if DEBUG_THREADS
GC_printf1("Not resuming already suspended thread %p\n", thread);
# endif
continue;
}
struct thread_basic_info info;
mach_msg_type_number_t outCount = THREAD_INFO_MAX;
kern_result = thread_info(thread, THREAD_BASIC_INFO,
(thread_info_t)&info, &outCount);
if(kern_result != KERN_SUCCESS) ABORT("thread_info failed");
# if DEBUG_THREADS
GC_printf2("Thread state for 0x%lx = %d\n", thread,
info.run_state);
GC_printf1("Resuming 0x%lx\n", thread);
# endif
kern_result = thread_resume(thread);
if(kern_result != KERN_SUCCESS) ABORT("thread_resume failed");
}
}
}
}
# if DEBUG_THREADS
GC_printf0("World started\n");
# endif
}
void GC_darwin_register_mach_handler_thread(mach_port_t thread) {
GC_mach_handler_thread = thread;
GC_use_mach_handler_thread = 1;
}
#endif