From 644cf3c2f83bd7f6b052d76a2a38fda5a8b6bb8a Mon Sep 17 00:00:00 2001 From: ivmai Date: Thu, 10 Sep 2009 17:30:19 +0000 Subject: [PATCH] 2009-09-10 Ivan Maidanski (diff112) * finalize.c (GC_fail_count): New external variable declaration. * finalize.c (GC_reset_finalizer_nested, GC_check_finalizer_nested): New function declarations (if THREADS only). * finalize.c (GC_finalizer_nested, GC_finalizer_skipped): New static global variables (used internally by GC_finalize() and GC_check_finalizer_nested()). * finalize.c (GC_check_finalizer_nested): New static function definition (only if not THREADS, used internally by GC_notify_or_invoke_finalizers() to minimize the probability of a deep recursion when a client finalizer tries to allocate GC memory). * finalize.c (GC_finalize): Reset GC_finalizer_nested value (or call GC_reset_finalizer_nested()) if last heap expansion failed. * finalize.c (GC_notify_or_invoke_finalizers): Access GC_gc_no, GC_finalizer_now, GC_finalize_on_demand, GC_finalizer_notifier, last_finalizer_notification variables holding the lock (to avoid data races). * finalize.c (GC_finalizer_notifier): Add comment. * finalize.c (GC_notify_or_invoke_finalizers): Add "quick" check for an empty finalization queue (only if THREADS and not KEEP_BACK_PTRS/MAKE_BACK_GRAPH). * finalize.c (GC_notify_or_invoke_finalizers): Call GC_check_finalizer_nested() and skip GC_invoke_finalizers() call if appropriate. * include/private/pthread_support.h (GC_Thread_Rep): Add unsigned finalizer_nested and finalizer_skipped fields (for internal use by the multi-threaded GC_check_finalizer_nested()). * win32_threads.c (GC_Thread_Rep): Ditto. * pthread_support.c (GC_reset_finalizer_nested, GC_check_finalizer_nested): New function definitions (the multi-threaded variants of that in finalize.c). * win32_threads.c (GC_reset_finalizer_nested, GC_check_finalizer_nested): Ditto. --- ChangeLog | 38 +++++++++++++++++ finalize.c | 86 ++++++++++++++++++++++++++++++++++----- include/private/pthread_support.h | 7 ++++ pthread_support.c | 29 ++++++++++++- win32_threads.c | 35 ++++++++++++++++ 5 files changed, 184 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8c19ea1..e1d02da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,43 @@ 2009-09-10 Ivan Maidanski + (diff112) + + * finalize.c (GC_fail_count): New external variable declaration. + * finalize.c (GC_reset_finalizer_nested, + GC_check_finalizer_nested): New function declarations (if THREADS + only). + * finalize.c (GC_finalizer_nested, GC_finalizer_skipped): New + static global variables (used internally by GC_finalize() and + GC_check_finalizer_nested()). + * finalize.c (GC_check_finalizer_nested): New static function + definition (only if not THREADS, used internally by + GC_notify_or_invoke_finalizers() to minimize the probability of + a deep recursion when a client finalizer tries to allocate GC + memory). + * finalize.c (GC_finalize): Reset GC_finalizer_nested value (or + call GC_reset_finalizer_nested()) if last heap expansion failed. + * finalize.c (GC_notify_or_invoke_finalizers): Access GC_gc_no, + GC_finalizer_now, GC_finalize_on_demand, GC_finalizer_notifier, + last_finalizer_notification variables holding the lock (to avoid + data races). + * finalize.c (GC_finalizer_notifier): Add comment. + * finalize.c (GC_notify_or_invoke_finalizers): Add "quick" check + for an empty finalization queue (only if THREADS and not + KEEP_BACK_PTRS/MAKE_BACK_GRAPH). + * finalize.c (GC_notify_or_invoke_finalizers): Call + GC_check_finalizer_nested() and skip GC_invoke_finalizers() call + if appropriate. + * include/private/pthread_support.h (GC_Thread_Rep): Add unsigned + finalizer_nested and finalizer_skipped fields (for internal use + by the multi-threaded GC_check_finalizer_nested()). + * win32_threads.c (GC_Thread_Rep): Ditto. + * pthread_support.c (GC_reset_finalizer_nested, + GC_check_finalizer_nested): New function definitions (the + multi-threaded variants of that in finalize.c). + * win32_threads.c (GC_reset_finalizer_nested, + GC_check_finalizer_nested): Ditto. + +2009-09-10 Ivan Maidanski (diff103_cvs) * alloc.c (GC_stopped_mark): Remove GC_log_printf("") (not needed diff --git a/finalize.c b/finalize.c index 4e6d567..8e5736b 100644 --- a/finalize.c +++ b/finalize.c @@ -480,6 +480,40 @@ void GC_dump_finalization(void) } #endif +extern unsigned GC_fail_count; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk(); defined in alloc.c. */ + +#ifdef THREADS + /* Defined in pthread_support.c or win32_threads.c. Called with the */ + /* allocation lock held. */ + void GC_reset_finalizer_nested(void); + unsigned *GC_check_finalizer_nested(void); +#else + /* Global variables to minimize the level of recursion when a client */ + /* finalizer allocates memory. */ + STATIC unsigned GC_finalizer_nested = 0; + STATIC unsigned GC_finalizer_skipped = 0; + + /* Checks and updates the level of finalizers recursion. */ + /* Returns NULL if GC_invoke_finalizers() should not be called by the */ + /* collector (to minimize the risk of a deep finalizers recursion), */ + /* otherwise returns a pointer to GC_finalizer_nested. */ + STATIC unsigned *GC_check_finalizer_nested(void) + { + unsigned nesting_level = GC_finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL; + GC_finalizer_skipped = 0; + } + GC_finalizer_nested = nesting_level + 1; + return &GC_finalizer_nested; + } +#endif /* THREADS */ + /* Called with held lock (but the world is running). */ /* Cause disappearing links to disappear and unreachable objects to be */ /* enqueued for finalization. */ @@ -644,6 +678,15 @@ void GC_finalize(void) } } } + if (GC_fail_count) { + /* Don't prevent running finalizers if there has been an allocation */ + /* failure recently. */ +# ifdef THREADS + GC_reset_finalizer_nested(); +# else + GC_finalizer_nested = 0; +# endif + } } #ifndef JAVA_FINALIZATION_NOT_NEEDED @@ -780,6 +823,7 @@ GC_API int GC_CALL GC_invoke_finalizers(void) return count; } +/* All accesses to it should be synchronized to avoid data races. */ GC_finalizer_notifier_proc GC_finalizer_notifier = (GC_finalizer_notifier_proc)0; @@ -787,15 +831,21 @@ static GC_word last_finalizer_notification = 0; void GC_notify_or_invoke_finalizers(void) { - /* This is a convenient place to generate backtraces if appropriate, */ - /* since that code is not callable with the allocation lock. */ + GC_finalizer_notifier_proc notifier_fn = 0; # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) static word last_back_trace_gc_no = 1; /* Skip first one. */ +# elif defined(THREADS) + /* Quick check (while unlocked) for an empty finalization queue. */ + if (GC_finalize_now == 0) return; +# endif + LOCK(); + /* This is a convenient place to generate backtraces if appropriate, */ + /* since that code is not callable with the allocation lock. */ +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) if (GC_gc_no > last_back_trace_gc_no) { # ifdef KEEP_BACK_PTRS long i; - LOCK(); /* Stops when GC_gc_no wraps; that's OK. */ last_back_trace_gc_no = (word)(-1); /* disable others. */ for (i = 0; i < GC_backtraces; ++i) { @@ -808,27 +858,43 @@ void GC_notify_or_invoke_finalizers(void) LOCK(); } last_back_trace_gc_no = GC_gc_no; - UNLOCK(); # endif # ifdef MAKE_BACK_GRAPH - if (GC_print_back_height) + if (GC_print_back_height) { + UNLOCK(); GC_print_back_graph_stats(); + LOCK(); + } # endif } # endif - if (GC_finalize_now == 0) return; + if (GC_finalize_now == 0) { + UNLOCK(); + return; + } + if (!GC_finalize_on_demand) { + unsigned *pnested = GC_check_finalizer_nested(); + UNLOCK(); + /* Skip GC_invoke_finalizers() if nested */ + if (pnested != NULL) { (void) GC_invoke_finalizers(); + *pnested = 0; /* Reset since no more finalizers. */ # ifndef THREADS GC_ASSERT(GC_finalize_now == 0); # endif /* Otherwise GC can run concurrently and add more */ - return; + } + return; } - if (GC_finalizer_notifier != (GC_finalizer_notifier_proc)0 - && last_finalizer_notification != GC_gc_no) { + + /* These variables require synchronization to avoid data races. */ + if (last_finalizer_notification != GC_gc_no) { last_finalizer_notification = GC_gc_no; - GC_finalizer_notifier(); + notifier_fn = GC_finalizer_notifier; } + UNLOCK(); + if (notifier_fn != 0) + (*notifier_fn)(); /* Invoke the notifier */ } GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn, diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h index 43248bf..f347605 100644 --- a/include/private/pthread_support.h +++ b/include/private/pthread_support.h @@ -62,6 +62,13 @@ typedef struct GC_Thread_Rep { /* This is unfortunately also the */ /* reason we need to intercept join */ /* and detach. */ + + unsigned finalizer_nested; + unsigned finalizer_skipped; /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ + # ifdef THREAD_LOCAL_ALLOC struct thread_local_freelists tlfs; # endif diff --git a/pthread_support.c b/pthread_support.c index e01d970..93128df 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -451,7 +451,7 @@ STATIC void GC_delete_gc_thread(GC_thread gc_id) /* Return a GC_thread corresponding to a given pthread_t. */ /* Returns 0 if it's not there. */ -/* Caller holds allocation lock or otherwise inhibits */ +/* Caller holds allocation lock or otherwise inhibits */ /* updates. */ /* If there is more than one thread with the given id we */ /* return the most recent one. */ @@ -464,6 +464,33 @@ GC_thread GC_lookup_thread(pthread_t id) return(p); } +/* Called by GC_finalize() (in case of an allocation failure observed). */ +void GC_reset_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread(pthread_self()); + me->finalizer_nested = 0; +} + +/* Checks and updates the thread-local level of finalizers recursion. */ +/* Returns NULL if GC_invoke_finalizers() should not be called by the */ +/* collector (to minimize the risk of a deep finalizers recursion), */ +/* otherwise returns a pointer to the thread-local finalizer_nested. */ +/* Called by GC_notify_or_invoke_finalizers() only (the lock is held). */ +unsigned *GC_check_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread(pthread_self()); + unsigned nesting_level = me->finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++me->finalizer_skipped < (1U << nesting_level)) return NULL; + me->finalizer_skipped = 0; + } + me->finalizer_nested = nesting_level + 1; + return &me->finalizer_nested; +} + #ifdef HANDLE_FORK /* Remove all entries from the GC_threads table, except the */ /* one for the current thread. We need to do this in the child */ diff --git a/win32_threads.c b/win32_threads.c index 1ec73b8..75865da 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -210,6 +210,12 @@ struct GC_Thread_Rep { ptr_t backing_store_ptr; # endif + unsigned finalizer_nested; + unsigned finalizer_skipped; /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ + GC_bool suspended; # ifdef GC_PTHREADS @@ -495,6 +501,35 @@ GC_thread GC_lookup_thread_inner(DWORD thread_id) { } } +/* Called by GC_finalize() (in case of an allocation failure observed). */ +/* GC_reset_finalizer_nested() is the same as in pthread_support.c. */ +void GC_reset_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + me->finalizer_nested = 0; +} + +/* Checks and updates the thread-local level of finalizers recursion. */ +/* Returns NULL if GC_invoke_finalizers() should not be called by the */ +/* collector (to minimize the risk of a deep finalizers recursion), */ +/* otherwise returns a pointer to the thread-local finalizer_nested. */ +/* Called by GC_notify_or_invoke_finalizers() only (the lock is held). */ +/* GC_check_finalizer_nested() is the same as in pthread_support.c. */ +unsigned *GC_check_finalizer_nested(void) +{ + GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + unsigned nesting_level = me->finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++me->finalizer_skipped < (1U << nesting_level)) return NULL; + me->finalizer_skipped = 0; + } + me->finalizer_nested = nesting_level + 1; + return &me->finalizer_nested; +} + /* Make sure thread descriptor t is not protected by the VDB */ /* implementation. */ /* Used to prevent write faults when the world is (partially) stopped, */ -- 2.7.4