Merge pull request #3328 from BrzVlad/finalizer-thread-exited2
authorVlad Brezae <brezaevlad@gmail.com>
Wed, 27 Jul 2016 19:59:27 +0000 (22:59 +0300)
committerGitHub <noreply@github.com>
Wed, 27 Jul 2016 19:59:27 +0000 (22:59 +0300)
[sgen] Avoid popping the entire finalizer queues if finalizers are su…

1  2 
mono/metadata/gc.c

diff --combined mono/metadata/gc.c
index 1b4c054b463088d401d6d9227355bc3dd1f06b15,c3d240c0d77a0a841a75b450af4a153b54e532b7..a7230d05a3e97dc7e1f4e8ade74618ef0f828fe7
  
  typedef struct DomainFinalizationReq {
        MonoDomain *domain;
 +#ifdef TARGET_WIN32
        HANDLE done_event;
 +#else
 +      gboolean done;
 +      MonoCoopCond cond;
 +      MonoCoopMutex mutex;
 +#endif
  } DomainFinalizationReq;
  
  static gboolean gc_disabled = FALSE;
@@@ -64,6 -58,7 +64,7 @@@ static gboolean finalizing_root_domain 
  
  gboolean log_finalizers = FALSE;
  gboolean mono_do_not_finalize = FALSE;
+ volatile gboolean suspend_finalizers = FALSE;
  gchar **mono_do_not_finalize_class_names = NULL;
  
  #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
@@@ -99,51 -94,6 +100,51 @@@ guarded_wait (HANDLE handle, guint32 ti
        return result;
  }
  
 +typedef struct {
 +      MonoCoopCond *cond;
 +      MonoCoopMutex *mutex;
 +} BreakCoopAlertableWaitUD;
 +
 +static inline void
 +break_coop_alertable_wait (gpointer user_data)
 +{
 +      BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
 +
 +      mono_coop_mutex_lock (ud->mutex);
 +      mono_coop_cond_signal (ud->cond);
 +      mono_coop_mutex_unlock (ud->mutex);
 +}
 +
 +/*
 + * coop_cond_timedwait_alertable:
 + *
 + *   Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
 + * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
 + */
 +static inline gint
 +coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
 +{
 +      int res;
 +
 +      if (alertable) {
 +              BreakCoopAlertableWaitUD ud;
 +
 +              *alertable = FALSE;
 +              ud.cond = cond;
 +              ud.mutex = mutex;
 +              mono_thread_info_install_interrupt (break_coop_alertable_wait, &ud, alertable);
 +              if (*alertable)
 +                      return 0;
 +      }
 +      res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
 +      if (alertable) {
 +              mono_thread_info_uninstall_interrupt (alertable);
 +              if (*alertable)
 +                      return 0;
 +      }
 +      return res;
 +}
 +
  static gboolean
  add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
  {
        return is_ok (error);
  }
  
- static volatile gboolean suspend_finalizers = FALSE;
  /* 
   * actually, we might want to queue the finalize requests in a separate thread,
   * but we need to be careful about the execution domain of the thread...
@@@ -433,6 -382,8 +433,6 @@@ gboolea
  mono_domain_finalize (MonoDomain *domain, guint32 timeout) 
  {
        DomainFinalizationReq *req;
 -      guint32 res;
 -      HANDLE done_event;
        MonoInternalThread *thread = mono_thread_internal_current ();
  
  #if defined(__native_client__)
  
        mono_gc_collect (mono_gc_max_generation ());
  
 -      done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
 -      if (done_event == NULL) {
 -              return FALSE;
 -      }
 -
        req = g_new0 (DomainFinalizationReq, 1);
        req->domain = domain;
 -      req->done_event = done_event;
 +
 +#ifdef TARGET_WIN32
 +      req->done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
 +      if (req->done_event == NULL) {
 +              g_free (req);
 +              return FALSE;
 +      }
 +#else
 +      mono_coop_cond_init (&req->cond);
 +      mono_coop_mutex_init (&req->mutex);
 +#endif
  
        if (domain == mono_get_root_domain ())
                finalizing_root_domain = TRUE;
        if (timeout == -1)
                timeout = INFINITE;
  
 +#if TARGET_WIN32
        while (TRUE) {
 -              res = guarded_wait (done_event, timeout, TRUE);
 +              guint32 res = guarded_wait (req->done_event, timeout, TRUE);
                /* printf ("WAIT RES: %d.\n", res); */
  
                if (res == WAIT_IO_COMPLETION) {
                }
        }
  
 -      CloseHandle (done_event);
 +      CloseHandle (req->done_event);
 +#else
 +      mono_coop_mutex_lock (&req->mutex);
 +      while (!req->done) {
 +              gboolean alerted;
 +              int res = coop_cond_timedwait_alertable (&req->cond, &req->mutex, timeout, &alerted);
 +              if (alerted) {
 +                      if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
 +                              mono_coop_mutex_unlock (&req->mutex);
 +                              return FALSE;
 +                      }
 +              } else if (res == ETIMEDOUT) {
 +                      /* We leak the cond/mutex here */
 +                      mono_coop_mutex_unlock (&req->mutex);
 +                      return FALSE;
 +              } else {
 +                      break;
 +              }
 +      }
 +      mono_coop_mutex_unlock (&req->mutex);
 +
 +      /* When we reach here, the other thread has already exited the critical section, so this is safe to free */
 +      mono_coop_cond_destroy (&req->cond);
 +      mono_coop_mutex_destroy (&req->mutex);
 +      g_free (req);
 +#endif
  
        if (domain == mono_get_root_domain ()) {
                mono_threadpool_ms_cleanup ();
@@@ -798,7 -718,7 +798,7 @@@ finalize_domain_objects (DomainFinaliza
                g_ptr_array_free (objs, TRUE);
        }
  #elif defined(HAVE_SGEN_GC)
-       mono_gc_finalize_domain (domain, &suspend_finalizers);
+       mono_gc_finalize_domain (domain);
        mono_gc_invoke_finalizers ();
  #endif
  
        reference_queue_clear_for_domain (domain);
        
        /* printf ("DONE.\n"); */
 +#if TARGET_WIN32
        SetEvent (req->done_event);
  
        /* The event is closed in mono_domain_finalize if we get here */
        g_free (req);
 +#else
 +      mono_coop_mutex_lock (&req->mutex);
 +      req->done = TRUE;
 +      mono_coop_cond_signal (&req->cond);
 +      mono_coop_mutex_unlock (&req->mutex);
 +#endif
  }
  
  static guint32
@@@ -878,7 -791,7 +878,7 @@@ finalizer_thread (gpointer unused
                hazard_free_queue_pump ();
  
                /* Avoid posting the pending done event until there are pending finalizers */
 -              if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
 +              if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
                        /* Don't wait again at the start of the loop */
                        wait = FALSE;
                } else {
@@@ -972,6 -885,7 +972,7 @@@ mono_gc_cleanup (void
  
                                /* Set a flag which the finalizer thread can check */
                                suspend_finalizers = TRUE;
+                               mono_gc_suspend_finalizers ();
  
                                /* Try to abort the thread, in the hope that it is running managed code */
                                mono_thread_internal_abort (gc_thread);