From: Ivan Maidanski Date: Fri, 24 Feb 2012 10:34:34 +0000 (+0400) Subject: Fix Cygwin support to handle fork() properly X-Git-Tag: gc7_3alpha2~96 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=11e95a1dfdd8ac1ed729d822a2968cb389e253d1;p=gc Fix Cygwin support to handle fork() properly * include/private/gcconfig.h (HANDLE_FORK): Define also for Cygwin (but not for win32-pthreads and not if Win32 memory allocation used). * win32_threads.c: Include unistd.h if HANDLE_FORK (for pthread_atfork). * win32_threads.c (GC_wait_for_gc_completion): Add wait_for_all argument. * win32_threads.c (GC_unregister_my_thread, GC_thread_exit_proc): Pass FALSE ("wait_for_all" argument) to GC_wait_for_gc_completion. * win32_threads.c (GC_remove_all_threads_but_me, GC_fork_prepare_proc, GC_fork_parent_proc, GC_fork_child_proc): New functions (similar to that in pthread_support.c) if HANDLE_FORK. * win32_threads.c (GC_thr_init): Invoke pthread_atfork if HANDLE_FORK. --- diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index a2ba20d3..3597de22 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -2612,9 +2612,9 @@ # define IF_CANCEL(x) /* empty */ #endif -#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) \ - && !defined(HANDLE_FORK) && !defined(NO_HANDLE_FORK) \ - && !defined(HURD) && !defined(NACL) && !defined(PLATFORM_ANDROID) +#if !defined(HANDLE_FORK) && !defined(NO_HANDLE_FORK) && defined(GC_PTHREADS) \ + && !defined(HURD) && !defined(NACL) && !defined(PLATFORM_ANDROID) \ + && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC) /* Attempts (where supported) to make GC_malloc work in a child */ /* process fork'ed from a multi-threaded parent. */ # define HANDLE_FORK diff --git a/win32_threads.c b/win32_threads.c index 09bfbd41..1682b2cc 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -55,6 +55,9 @@ STATIC void GC_thread_exit_proc(void *arg); # include +# ifdef HANDLE_FORK +# include +# endif #else @@ -759,8 +762,8 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) } } -/* Similar to that in pthread_support.c (wait_for_all is always FALSE). */ -STATIC void GC_wait_for_gc_completion(void) +/* Similar to that in pthread_support.c. */ +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all) { GC_ASSERT(I_HOLD_LOCK()); if (GC_incremental && GC_collection_in_progress()) { @@ -779,7 +782,7 @@ STATIC void GC_wait_for_gc_completion(void) Sleep(0); /* yield */ LOCK(); } while (GC_incremental && GC_collection_in_progress() - && old_gc_no == GC_gc_no); + && (wait_for_all || old_gc_no == GC_gc_no)); } } @@ -809,7 +812,7 @@ GC_API int GC_CALL GC_unregister_my_thread(void) DWORD thread_id = GetCurrentThreadId(); LOCK(); - GC_wait_for_gc_completion(); + GC_wait_for_gc_completion(FALSE); # if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS) me = GC_lookup_thread_inner(thread_id); CHECK_LOOKUP_MY_THREAD(me); @@ -982,6 +985,83 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, } } +# ifdef HANDLE_FORK + /* Similar to that in pthread_support.c. */ + STATIC void GC_remove_all_threads_but_me(void) + { + pthread_t id = pthread_self(); + int hv; + GC_thread p, next, me; + + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + me = 0; + for (p = GC_threads[hv]; 0 != p; p = next) { + next = p -> tm.next; + if (THREAD_EQUAL(p -> pthread_id, id)) { + me = p; + p -> tm.next = 0; + /* Update Win32 thread Id and handle. */ + me -> id = GetCurrentThreadId(); +# ifndef MSWINCE + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), + (HANDLE *)&me->handle, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + ABORT("DuplicateHandle failed"); +# endif + } else { +# ifdef THREAD_LOCAL_ALLOC + if ((p -> flags & FINISHED) == 0) { + GC_destroy_thread_local(&p->tlfs); + } +# endif + if (&first_thread != p) + GC_INTERNAL_FREE(p); + } + } + GC_threads[hv] = me; + } + } + + STATIC void GC_fork_prepare_proc(void) + { + LOCK(); +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + GC_wait_for_gc_completion(TRUE); +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_acquire_mark_lock(); +# endif + } + + STATIC void GC_fork_parent_proc(void) + { +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif + UNLOCK(); + } + + STATIC void GC_fork_child_proc(void) + { +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_release_mark_lock(); + GC_parallel = FALSE; /* or GC_markers_m1 = 0 */ + /* Turn off parallel marking in the child, since we are */ + /* probably just going to exec, and we would have to */ + /* restart mark threads. */ + } +# endif + GC_remove_all_threads_but_me(); + UNLOCK(); + } +# endif /* HANDLE_FORK */ + #endif /* GC_PTHREADS */ void GC_push_thread_structures(void) @@ -1593,7 +1673,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # endif /* start_mark_threads() is the same as in pthread_support.c except for: */ - /* - GC_markers_m1 value is adjusted already; */ + /* - GC_markers_m1 value is adjusted already; */ /* - thread stack is assumed to be large enough; and */ /* - statistics about the number of marker threads is printed outside. */ static void start_mark_threads(void) @@ -2280,6 +2360,13 @@ GC_INNER void GC_thr_init(void) GC_main_thread = GetCurrentThreadId(); GC_thr_initialized = TRUE; +# if defined(GC_PTHREADS) && defined(HANDLE_FORK) + /* Prepare for a possible fork. */ + if (pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc, + GC_fork_child_proc) != 0) + ABORT("pthread_atfork failed"); +# endif + /* Add the initial thread, so we can stop it. */ # ifdef GC_ASSERTIONS sb_result = @@ -2544,7 +2631,7 @@ GC_INNER void GC_thr_init(void) # endif LOCK(); - GC_wait_for_gc_completion(); + GC_wait_for_gc_completion(FALSE); # if defined(THREAD_LOCAL_ALLOC) GC_destroy_thread_local(&(me->tlfs)); # endif