2009-09-10 Ivan Maidanski <ivmai@mail.ru>
authorivmai <ivmai>
Thu, 10 Sep 2009 17:30:19 +0000 (17:30 +0000)
committerIvan Maidanski <ivmai@mail.ru>
Tue, 26 Jul 2011 17:06:46 +0000 (21:06 +0400)
(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
finalize.c
include/private/pthread_support.h
pthread_support.c
win32_threads.c

index 8c19ea1..e1d02da 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,43 @@
 
 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
index 4e6d567..8e5736b 100644 (file)
@@ -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,
index 43248bf..f347605 100644 (file)
@@ -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
index e01d970..93128df 100644 (file)
@@ -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        */
index 1ec73b8..75865da 100644 (file)
@@ -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, */