2009-09-10 Ivan Maidanski <ivmai@mail.ru>
+ (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 <ivmai@mail.ru>
(diff103_cvs)
* alloc.c (GC_stopped_mark): Remove GC_log_printf("") (not needed
}
#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. */
}
}
}
+ 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
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;
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) {
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,
/* 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
/* 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. */
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 */
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
}
}
+/* 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, */