X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=libgc%2Fpthread_stop_world.c;h=3bfdfbf35eddc525cb5667d1e6a258babf555ea5;hb=b0e91acb611bd7e6c39bc8756b38b41809a727a3;hp=1ed16132897ef4498bc890a2544c3912c58a7c92;hpb=baf32dc6786bba9b544eaffb31184d1397301b51;p=mono.git diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c index 1ed16132897..3bfdfbf35ed 100644 --- a/libgc/pthread_stop_world.c +++ b/libgc/pthread_stop_world.c @@ -2,12 +2,14 @@ #if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \ && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \ - && !defined(GC_DARWIN_THREADS) && !defined(GC_AIX_THREADS) + && !defined(GC_DARWIN_THREADS) && !defined(GC_AIX_THREADS) \ + && !defined(GC_OPENBSD_THREADS) #include #include #include #include +#include /* work around a dlopen issue (bug #75390), undefs to avoid warnings with redefinitions */ #undef PACKAGE_BUGREPORT @@ -17,8 +19,17 @@ #undef PACKAGE_VERSION #include "mono/utils/mono-compiler.h" -#ifdef MONO_DEBUGGER_SUPPORTED -#include "include/libgc-mono-debugger.h" +#ifdef NACL +volatile int __nacl_thread_suspension_needed = 0; +pthread_t nacl_thread_parker = -1; + +volatile int nacl_thread_parked[MAX_NACL_GC_THREADS]; +volatile int nacl_thread_used[MAX_NACL_GC_THREADS]; +volatile int nacl_thread_parking_inited = 0; +volatile int nacl_num_gc_threads = 0; +pthread_mutex_t nacl_thread_alloc_lock = PTHREAD_MUTEX_INITIALIZER; +__thread int nacl_thread_idx = -1; +__thread GC_thread nacl_gc_thread_self = NULL; #endif #if DEBUG_THREADS @@ -35,6 +46,7 @@ # endif #endif +#ifndef NACL void GC_print_sig_mask() { sigset_t blocked; @@ -48,7 +60,7 @@ void GC_print_sig_mask() } GC_printf0("\n"); } - +#endif /* NACL */ #endif /* Remove the signals that we want to allow in thread stopping */ @@ -115,6 +127,7 @@ sem_t GC_suspend_ack_sem; static void _GC_suspend_handler(int sig) { +#ifndef NACL int dummy; pthread_t my_thread = pthread_self(); GC_thread me; @@ -184,6 +197,8 @@ static void _GC_suspend_handler(int sig) #if DEBUG_THREADS GC_printf1("Continuing 0x%lx\n", my_thread); #endif + +#endif /* NACL */ } void GC_suspend_handler(int sig) @@ -271,12 +286,20 @@ static void pthread_push_all_stacks() (unsigned long) lo, (unsigned long) hi); #endif if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n"); + if (p->altstack && lo >= p->altstack && lo <= p->altstack + p->altstack_size) + hi = p->altstack + p->altstack_size; + /* FIXME: Need to scan the normal stack too, but how ? */ + # ifdef STACK_GROWS_UP /* We got them backwards! */ GC_push_all_stack(hi, lo); # else GC_push_all_stack(lo, hi); # endif +# ifdef NACL + /* Push reg_storage as roots, this will cover the reg context */ + GC_push_all_stack(p -> stop_info.reg_storage, p -> stop_info.reg_storage + NACL_GC_REG_STORAGE_SIZE); +# endif # ifdef IA64 # if DEBUG_THREADS GC_printf3("Reg stack for thread 0x%lx = [%lx,%lx)\n", @@ -314,11 +337,29 @@ void GC_push_all_stacks() pthread_t GC_stopping_thread; int GC_stopping_pid; +#ifdef PLATFORM_ANDROID +static +int android_thread_kill(pid_t tid, int sig) +{ + int ret; + int old_errno = errno; + + ret = tkill(tid, sig); + if (ret < 0) { + ret = errno; + errno = old_errno; + } + + return ret; +} +#endif + /* We hold the allocation lock. Suspend all threads that might */ /* still be running. Return the number of suspend signals that */ /* were sent. */ int GC_suspend_all() { +#ifndef NACL int n_live_threads = 0; int i; GC_thread p; @@ -337,8 +378,12 @@ int GC_suspend_all() #if DEBUG_THREADS GC_printf1("Sending suspend signal to 0x%lx\n", p -> id); #endif - + +#ifndef PLATFORM_ANDROID result = pthread_kill(p -> id, SIG_SUSPEND); +#else + result = android_thread_kill(p -> kernel_id, SIG_SUSPEND); +#endif switch(result) { case ESRCH: /* Not really there anymore. Possible? */ @@ -353,11 +398,15 @@ int GC_suspend_all() } } return n_live_threads; +#else /* NACL */ + return 0; +#endif } /* Caller holds allocation lock. */ static void pthread_stop_world() { +#ifndef NACL int i; int n_live_threads; int code; @@ -409,11 +458,160 @@ static void pthread_stop_world() GC_printf1("World stopped from 0x%lx\n", pthread_self()); #endif GC_stopping_thread = 0; /* debugging only */ +#else /* NACL */ + GC_thread p; + int i; + int num_sleeps = 0; + + #if DEBUG_THREADS + GC_printf1("pthread_stop_world: num_threads %d\n", nacl_num_gc_threads - 1); + #endif + nacl_thread_parker = pthread_self(); + __nacl_thread_suspension_needed = 1; + + while (1) { + #define NACL_PARK_WAIT_NANOSECONDS 100000 + #define NANOS_PER_SECOND 1000000000 + int num_threads_parked = 0; + struct timespec ts; + int num_used = 0; + /* Check the 'parked' flag for each thread the GC knows about */ + for (i = 0; i < MAX_NACL_GC_THREADS && num_used < nacl_num_gc_threads; i++) { + if (nacl_thread_used[i] == 1) { + num_used++; + if (nacl_thread_parked[i] == 1) { + num_threads_parked++; + } + } + } + /* -1 for the current thread */ + if (num_threads_parked >= nacl_num_gc_threads - 1) + break; + ts.tv_sec = 0; + ts.tv_nsec = NACL_PARK_WAIT_NANOSECONDS; + #if DEBUG_THREADS + GC_printf1("sleeping waiting for %d threads to park...\n", nacl_num_gc_threads - num_threads_parked - 1); + #endif + nanosleep(&ts, 0); + if (++num_sleeps > NANOS_PER_SECOND / NACL_PARK_WAIT_NANOSECONDS) { + GC_printf1("GC appears stalled waiting for %d threads to park...\n", nacl_num_gc_threads - num_threads_parked - 1); + num_sleeps = 0; + } + } + +#endif /* NACL */ +} + + +#ifdef NACL + +#if __x86_64__ + +#define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %rbx");\ + __asm__ __volatile__ ("push %rbp");\ + __asm__ __volatile__ ("push %r12");\ + __asm__ __volatile__ ("push %r13");\ + __asm__ __volatile__ ("push %r14");\ + __asm__ __volatile__ ("push %r15");\ + __asm__ __volatile__ ("mov %%esp, %0" : "=m" (nacl_gc_thread_self->stop_info.stack_ptr));\ + memcpy(nacl_gc_thread_self->stop_info.reg_storage, nacl_gc_thread_self->stop_info.stack_ptr, NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));\ + __asm__ __volatile__ ("naclasp $48, %r15");\ + } while (0) + +#elif __i386__ + +#define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %ebx");\ + __asm__ __volatile__ ("push %ebp");\ + __asm__ __volatile__ ("push %esi");\ + __asm__ __volatile__ ("push %edi");\ + __asm__ __volatile__ ("mov %%esp, %0" : "=m" (nacl_gc_thread_self->stop_info.stack_ptr));\ + memcpy(nacl_gc_thread_self->stop_info.reg_storage, nacl_gc_thread_self->stop_info.stack_ptr, NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));\ + __asm__ __volatile__ ("add $16, %esp");\ + } while (0) + +#elif __arm__ + +#define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ( \ + ".align 4\n\t" \ + "bic %0, %0, #0xc0000000\n\t" \ + "str sp, [%0]\n\t" \ + "bic %1, %1, #0xc0000000\n\t" \ + "stm %1, {r4-r8,r10-r12,lr}\n\t" \ + : \ + : "r" (&nacl_gc_thread_self->stop_info.stack_ptr), \ + "r"(nacl_gc_thread_self->stop_info.reg_storage) \ + : "memory"); \ + } while (0) +#else + +#error "Please port NACL_STORE_REGS" + +#endif + +void nacl_pre_syscall_hook() +{ + int local_dummy = 0; + if (nacl_thread_idx != -1) { + NACL_STORE_REGS(); + nacl_gc_thread_self->stop_info.stack_ptr = (ptr_t)(&local_dummy); + nacl_thread_parked[nacl_thread_idx] = 1; + } +} + +void __nacl_suspend_thread_if_needed(); + +void nacl_post_syscall_hook() +{ + /* Calling __nacl_suspend_thread_if_needed() right away should guarantee we don't mutate the GC set. */ + __nacl_suspend_thread_if_needed(); + if (nacl_thread_idx != -1) { + nacl_thread_parked[nacl_thread_idx] = 0; + } +} + +void __nacl_suspend_thread_if_needed() { + if (__nacl_thread_suspension_needed) { + pthread_t self = pthread_self(); + int local_dummy = 0; + /* Don't try to park the thread parker. */ + if (nacl_thread_parker == self) + return; + + /* This can happen when a thread is created */ + /* outside of the GC system (wthread mostly). */ + if (nacl_thread_idx < 0) + return; + + /* If it was already 'parked', we're returning from a syscall, */ + /* so don't bother storing registers again, the GC has a set. */ + if (!nacl_thread_parked[nacl_thread_idx]) { + NACL_STORE_REGS(); + nacl_gc_thread_self->stop_info.stack_ptr = (ptr_t)(&local_dummy); + } + nacl_thread_parked[nacl_thread_idx] = 1; + while (__nacl_thread_suspension_needed) + ; /* spin */ + nacl_thread_parked[nacl_thread_idx] = 0; + + /* Clear out the reg storage for next suspend. */ + memset(nacl_gc_thread_self->stop_info.reg_storage, 0, NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); + } } +#endif /* NACL */ + /* Caller holds allocation lock. */ void GC_stop_world() { + if (GC_notify_event) + GC_notify_event (GC_EVENT_PRE_STOP_WORLD); + GC_process_togglerefs (); /* 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, */ @@ -424,21 +622,19 @@ void GC_stop_world() /* We should have previously waited for it to become zero. */ # endif /* PARALLEL_MARK */ ++GC_stop_count; -#ifdef MONO_DEBUGGER_SUPPORTED - if (gc_thread_vtable && gc_thread_vtable->stop_world) - gc_thread_vtable->stop_world (); - else -#endif pthread_stop_world (); # ifdef PARALLEL_MARK GC_release_mark_lock(); # endif + if (GC_notify_event) + GC_notify_event (GC_EVENT_POST_STOP_WORLD); } /* Caller holds allocation lock, and has held it continuously since */ /* the world stopped. */ static void pthread_start_world() { +#ifndef NACL pthread_t my_thread = pthread_self(); register int i; register GC_thread p; @@ -449,6 +645,8 @@ static void pthread_start_world() # if DEBUG_THREADS GC_printf0("World starting\n"); # endif + if (GC_notify_event) + GC_notify_event (GC_EVENT_PRE_START_WORLD); for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { @@ -459,8 +657,12 @@ static void pthread_start_world() #if DEBUG_THREADS GC_printf1("Sending restart signal to 0x%lx\n", p -> id); #endif - + +#ifndef PLATFORM_ANDROID result = pthread_kill(p -> id, SIG_THR_RESTART); +#else + result = android_thread_kill(p -> kernel_id, SIG_THR_RESTART); +#endif switch(result) { case ESRCH: /* Not really there anymore. Possible? */ @@ -488,22 +690,30 @@ static void pthread_start_world() } } + if (GC_notify_event) + GC_notify_event (GC_EVENT_POST_START_WORLD); #if DEBUG_THREADS GC_printf0("World started\n"); #endif +#else /* NACL */ + if (GC_notify_event) + GC_notify_event (GC_EVENT_PRE_START_WORLD); +# if DEBUG_THREADS + GC_printf0("World starting\n"); +# endif + __nacl_thread_suspension_needed = 0; + if (GC_notify_event) + GC_notify_event (GC_EVENT_POST_START_WORLD); +#endif /* NACL */ } void GC_start_world() { -#ifdef MONO_DEBUGGER_SUPPORTED - if (gc_thread_vtable && gc_thread_vtable->start_world) - gc_thread_vtable->start_world(); - else -#endif pthread_start_world (); } static void pthread_stop_init() { +#ifndef NACL struct sigaction act; if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0) @@ -544,32 +754,13 @@ static void pthread_stop_init() { GC_printf0("Will retry suspend signal if necessary.\n"); } # endif +#endif /* NACL */ } /* We hold the allocation lock. */ void GC_stop_init() { -#ifdef MONO_DEBUGGER_SUPPORTED - if (gc_thread_vtable && gc_thread_vtable->initialize) - gc_thread_vtable->initialize (); - else -#endif pthread_stop_init (); } -#ifdef MONO_DEBUGGER_SUPPORTED - -GCThreadFunctions *gc_thread_vtable = NULL; - -void * -GC_mono_debugger_get_stack_ptr (void) -{ - GC_thread me; - - me = GC_lookup_thread (pthread_self ()); - return &me->stop_info.stack_ptr; -} - -#endif - #endif