Merge pull request #1840 from ludovic-henry/iolayer-thread-interrupt
authorRodrigo Kumpera <kumpera@gmail.com>
Thu, 30 Jul 2015 14:48:44 +0000 (10:48 -0400)
committerRodrigo Kumpera <kumpera@gmail.com>
Thu, 30 Jul 2015 14:48:44 +0000 (10:48 -0400)
[runtime] Implement wait interrupt in thread info, instead of in the io-layer

1  2 
mono/io-layer/handles.c
mono/metadata/threads.c
mono/utils/mono-threads-posix.c

diff --combined mono/io-layer/handles.c
index 3ffe49b725d17b4c76c39b7c5bd8f0c765eb1f7a,7378d93894d224ec342208585190b9a87f9f5225..63d225aab6cfa63cf1e7ad9f665a894e4b54bfb3
@@@ -45,6 -45,7 +45,7 @@@
  
  #include <mono/utils/mono-mutex.h>
  #include <mono/utils/mono-proclib.h>
+ #include <mono/utils/mono-threads.h>
  #undef DEBUG_REFS
  
  #if 0
@@@ -1537,29 -1538,50 +1538,50 @@@ static int timedwait_signal_poll_cond (
        return(ret);
  }
  
- int _wapi_handle_wait_signal (gboolean poll)
+ int
+ _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll, gboolean *alerted)
  {
-       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
+       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll, alerted);
  }
  
- int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
+ static void
+ signal_handle_and_unref (gpointer handle)
  {
-       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
- }
+       pthread_cond_t *cond;
+       mono_mutex_t *mutex;
+       guint32 idx;
  
- int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
- {
-       DEBUG ("%s: waiting for %p", __func__, handle);
-       
-       return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
+       g_assert (handle);
+       /* If we reach here, then interrupt token is set to the flag value, which
+        * means that the target thread is either
+        * - before the first CAS in timedwait, which means it won't enter the wait.
+        * - it is after the first CAS, so it is already waiting, or it will enter
+        *    the wait, and it will be interrupted by the broadcast. */
+       idx = GPOINTER_TO_UINT (handle);
+       cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
+       mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
+       mono_mutex_lock (mutex);
+       mono_cond_broadcast (cond);
+       mono_mutex_unlock (mutex);
+       _wapi_handle_unref (handle);
  }
  
- int _wapi_handle_timedwait_signal_handle (gpointer handle,
-                                                                                 struct timespec *timeout, gboolean alertable, gboolean poll)
+ int
+ _wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout,
+               gboolean alertable, gboolean poll, gboolean *alerted)
  {
        DEBUG ("%s: waiting for %p (type %s)", __func__, handle,
                   _wapi_handle_typename[_wapi_handle_type (handle)]);
-       
+       if (alertable)
+               g_assert (alerted);
+       if (alerted)
+               *alerted = FALSE;
        if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
                if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
                        return (0);
                pthread_cond_t *cond;
                mono_mutex_t *mutex;
  
-               if (alertable && !wapi_thread_set_wait_handle (handle))
-                       return 0;
+               if (alertable) {
+                       mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
+                       if (*alerted)
+                               return 0;
+                       _wapi_handle_ref (handle);
+               }
  
                cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
                mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
                                res = mono_cond_wait (cond, mutex);
                }
  
-               if (alertable)
-                       wapi_thread_clear_wait_handle (handle);
+               if (alertable) {
+                       mono_thread_info_uninstall_interrupt (alerted);
+                       if (!*alerted) {
+                               /* if it is alerted, then the handle is unref in the interrupt callback */
+                               _wapi_handle_unref (handle);
+                       }
+               }
  
                return res;
        }
@@@ -1787,7 -1818,7 +1818,7 @@@ static void _wapi_handle_check_share_by
  {
  #if defined(__native_client__)
        g_assert_not_reached ();
 -#else
 +#elif defined(HAVE_KILL)
        if (kill (share_info->opened_by_pid, 0) == -1 &&
            (errno == ESRCH ||
             errno == EPERM)) {
diff --combined mono/metadata/threads.c
index 1370c3d0c55ac01e65e2d930e9223a5c5cfce019,6fc7ca0dde961a637d288d2fcf0c0a459e2d55ac..17246184a8c573982ac41504c16d66e5919b57f8
@@@ -1094,28 -1094,28 +1094,28 @@@ mono_thread_exit (
  }
  
  void
 -ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this)
 +ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
  {
        MonoInternalThread *internal = create_internal_thread ();
  
        internal->state = ThreadState_Unstarted;
  
 -      InterlockedCompareExchangePointer ((gpointer)&this->internal_thread, internal, NULL);
 +      InterlockedCompareExchangePointer ((gpointer)&this_obj->internal_thread, internal, NULL);
  }
  
  HANDLE
 -ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this,
 +ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
                                                                                                   MonoObject *start)
  {
        StartInfo *start_info;
        MonoInternalThread *internal;
        gboolean res;
  
 -      THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this, start));
 +      THREAD_DEBUG (g_message("%s: Trying to start a new thread: this (%p) start (%p)", __func__, this_obj, start));
  
 -      if (!this->internal_thread)
 -              ves_icall_System_Threading_Thread_ConstructInternalThread (this);
 -      internal = this->internal_thread;
 +      if (!this_obj->internal_thread)
 +              ves_icall_System_Threading_Thread_ConstructInternalThread (this_obj);
 +      internal = this_obj->internal_thread;
  
        LOCK_THREAD (internal);
  
  
        if ((internal->state & ThreadState_Aborted) != 0) {
                UNLOCK_THREAD (internal);
 -              return this;
 +              return this_obj;
        }
        /* This is freed in start_wrapper */
        start_info = g_new0 (StartInfo, 1);
        start_info->func = NULL;
 -      start_info->start_arg = this->start_obj; /* FIXME: GC object stored in unmanaged memory */
 +      start_info->start_arg = this_obj->start_obj; /* FIXME: GC object stored in unmanaged memory */
        start_info->delegate = start;
 -      start_info->obj = this;
 -      g_assert (this->obj.vtable->domain == mono_domain_get ());
 +      start_info->obj = this_obj;
 +      g_assert (this_obj->obj.vtable->domain == mono_domain_get ());
  
 -      res = create_thread (this, internal, start_info, FALSE, 0, FALSE);
 +      res = create_thread (this_obj, internal, start_info, FALSE, 0, FALSE);
        if (!res) {
                UNLOCK_THREAD (internal);
                return NULL;
   * This is called from the finalizer of the internal thread object.
   */
  void
 -ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this, HANDLE thread)
 +ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj, HANDLE thread)
  {
        THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
  
        if (thread)
                CloseHandle (thread);
  
 -      if (this->synch_cs) {
 -              mono_mutex_t *synch_cs = this->synch_cs;
 -              this->synch_cs = NULL;
 +      if (this_obj->synch_cs) {
 +              mono_mutex_t *synch_cs = this_obj->synch_cs;
 +              this_obj->synch_cs = NULL;
                mono_mutex_destroy (synch_cs);
                g_free (synch_cs);
        }
  
 -      if (this->name) {
 -              void *name = this->name;
 -              this->name = NULL;
 +      if (this_obj->name) {
 +              void *name = this_obj->name;
 +              this_obj->name = NULL;
                g_free (name);
        }
  }
@@@ -1325,7 -1325,7 +1325,7 @@@ ves_icall_System_Threading_Thread_GetPr
  }
  
  void
 -ves_icall_System_Threading_Thread_SetPriority (MonoThread *this, int priority)
 +ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priority)
  {
  }
  
@@@ -1385,35 -1385,35 +1385,35 @@@ mono_thread_internal_current (void
  }
  
  gboolean
 -ves_icall_System_Threading_Thread_Join_internal(MonoThread *this, int ms)
 +ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
  {
 -      MonoInternalThread *this_obj = this->internal_thread;
 -      HANDLE thread = this_obj->handle;
 +      MonoInternalThread *thread = this_obj->internal_thread;
 +      HANDLE handle = thread->handle;
        MonoInternalThread *cur_thread = mono_thread_internal_current ();
        gboolean ret;
  
        mono_thread_current_check_pending_interrupt ();
  
 -      LOCK_THREAD (this_obj);
 +      LOCK_THREAD (thread);
        
 -      if ((this_obj->state & ThreadState_Unstarted) != 0) {
 -              UNLOCK_THREAD (this_obj);
 +      if ((thread->state & ThreadState_Unstarted) != 0) {
 +              UNLOCK_THREAD (thread);
                
                mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started."));
                return FALSE;
        }
  
 -      UNLOCK_THREAD (this_obj);
 +      UNLOCK_THREAD (thread);
  
        if(ms== -1) {
                ms=INFINITE;
        }
 -      THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, thread, ms));
 +      THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
        
        mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
  
        MONO_PREPARE_BLOCKING
 -      ret=WaitForSingleObjectEx (thread, ms, TRUE);
 +      ret=WaitForSingleObjectEx (handle, ms, TRUE);
        MONO_FINISH_BLOCKING
  
        mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
@@@ -1563,7 -1563,7 +1563,7 @@@ gint32 ves_icall_System_Threading_WaitH
  }
  
  /* FIXME: exitContext isnt documented */
 -gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this, HANDLE handle, gint32 ms, gboolean exitContext)
 +gboolean ves_icall_System_Threading_WaitHandle_WaitOne_internal(MonoObject *this_obj, HANDLE handle, gint32 ms, gboolean exitContext)
  {
        guint32 ret;
        MonoInternalThread *thread = mono_thread_internal_current ();
@@@ -2022,23 -2022,23 +2022,23 @@@ ves_icall_System_Threading_Thread_GetSt
        return state;
  }
  
 -void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this)
 +void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
  {
        MonoInternalThread *current;
        gboolean throw;
 -      MonoInternalThread *this_obj = this->internal_thread;
 +      MonoInternalThread *thread = this_obj->internal_thread;
  
 -      LOCK_THREAD (this_obj);
 +      LOCK_THREAD (thread);
  
        current = mono_thread_internal_current ();
  
 -      this_obj->thread_interrupt_requested = TRUE;
 -      throw = current != this_obj && (this_obj->state & ThreadState_WaitSleepJoin);
 +      thread->thread_interrupt_requested = TRUE;
 +      throw = current != thread && (thread->state & ThreadState_WaitSleepJoin);
  
 -      UNLOCK_THREAD (this_obj);
 +      UNLOCK_THREAD (thread);
        
        if (throw) {
 -              abort_thread_internal (this_obj, TRUE, FALSE);
 +              abort_thread_internal (thread, TRUE, FALSE);
        }
  }
  
@@@ -2104,7 -2104,7 +2104,7 @@@ ves_icall_System_Threading_Thread_Abor
  }
  
  void
 -ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this)
 +ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
  {
        MonoInternalThread *thread = mono_thread_internal_current ();
        gboolean was_aborting;
@@@ -2149,9 -2149,9 +2149,9 @@@ mono_thread_internal_reset_abort (MonoI
  }
  
  MonoObject*
 -ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this)
 +ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *this_obj)
  {
 -      MonoInternalThread *thread = this->internal_thread;
 +      MonoInternalThread *thread = this_obj->internal_thread;
        MonoObject *state, *deserialized = NULL, *exc;
        MonoDomain *domain;
  
@@@ -2208,9 -2208,9 +2208,9 @@@ mono_thread_suspend (MonoInternalThrea
  }
  
  void
 -ves_icall_System_Threading_Thread_Suspend (MonoThread *this)
 +ves_icall_System_Threading_Thread_Suspend (MonoThread *this_obj)
  {
 -      if (!mono_thread_suspend (this->internal_thread)) {
 +      if (!mono_thread_suspend (this_obj->internal_thread)) {
                mono_set_pending_exception (mono_get_exception_thread_state ("Thread has not been started, or is dead."));
                return;
        }
@@@ -2584,24 -2584,6 +2584,24 @@@ ves_icall_System_Runtime_Remoting_Conte
        g_hash_table_insert (contexts, gch, gch);
  
        mono_threads_unlock ();
 +
 +      mono_profiler_context_loaded (ctx);
 +}
 +
 +void
 +ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
 +{
 +      /*
 +       * NOTE: Since finalizers are unreliable for the purposes of ensuring
 +       * cleanup in exceptional circumstances, we don't actually do any
 +       * cleanup work here. We instead do this when we iterate the `contexts`
 +       * hash table. The only purpose of this finalizer, at the moment, is to
 +       * notify the profiler.
 +       */
 +
 +      //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
 +
 +      mono_profiler_context_unloaded (ctx);
  }
  
  void
@@@ -3180,7 -3162,7 +3180,7 @@@ print_stack_frame_to_string (MonoStackF
  {
        GString *p = (GString*)data;
        MonoMethod *method = NULL;
 -      if (frame->ji)
 +      if (frame->type == FRAME_TYPE_MANAGED)
                method = mono_jit_info_get_method (frame->ji);
  
        if (method) {
@@@ -3720,16 -3702,17 +3720,16 @@@ thread_adjust_static_data (MonoInternal
        mono_threads_unlock ();
  }
  
 +/*
 + * LOCKING: requires that threads_mutex is held
 + */
  static void
  context_adjust_static_data (MonoAppContext *ctx)
  {
 -      mono_threads_lock ();
 -
        if (context_static_info.offset || context_static_info.idx > 0) {
                guint32 offset = MAKE_SPECIAL_STATIC_OFFSET (context_static_info.idx, context_static_info.offset, 0);
                mono_alloc_static_data (&ctx->static_data, offset, FALSE);
        }
 -
 -      mono_threads_unlock ();
  }
  
  /*
@@@ -4028,8 -4011,9 +4028,9 @@@ mono_thread_execute_interruption (MonoI
                WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
  #endif
                InterlockedDecrement (&thread_interruption_requested);
                /* Clear the interrupted flag of the thread so it can wait again */
-               mono_thread_info_clear_interruption ();
+               mono_thread_info_clear_self_interrupt ();
        }
  
        if ((thread->state & ThreadState_AbortRequested) != 0) {
@@@ -4399,7 -4383,7 +4400,7 @@@ mono_thread_info_get_last_managed (Mono
  typedef struct {
        MonoInternalThread *thread;
        gboolean install_async_abort;
-       gpointer interrupt_handle;
+       MonoThreadInfoInterruptToken *interrupt_token;
  } AbortThreadData;
  
  static SuspendThreadResult
@@@ -4421,7 -4405,7 +4422,7 @@@ abort_thread_critical (MonoThreadInfo *
        InterlockedIncrement (&thread_interruption_requested);
  
        ji = mono_thread_info_get_last_managed (info);
 -      protected_wrapper = ji && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
 +      protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
  
        if (!protected_wrapper && running_managed) {
                 * functions in the io-layer until the signal handler calls QueueUserAPC which will
                 * make it return.
                 */
-               data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+               data->interrupt_token = mono_thread_info_prepare_interrupt (info);
                return MonoResumeThread;
        }
  }
@@@ -4463,20 -4448,22 +4465,22 @@@ abort_thread_internal (MonoInternalThre
                MonoException *exc = mono_thread_request_interruption (can_raise_exception); 
                if (exc)
                        mono_raise_exception (exc);
-               mono_thread_info_interrupt (thread->handle);
+               mono_thread_info_self_interrupt ();
                return;
        }
  
        mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, TRUE, abort_thread_critical, &data);
-       if (data.interrupt_handle)
-               mono_thread_info_finish_interrupt (data.interrupt_handle);
+       if (data.interrupt_token)
+               mono_thread_info_finish_interrupt (data.interrupt_token);
        /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
  }
  
  typedef struct{
        MonoInternalThread *thread;
        gboolean interrupt;
-       gpointer interrupt_handle;
+       MonoThreadInfoInterruptToken *interrupt_token;
  } SuspendThreadData;
  
  static SuspendThreadResult
@@@ -4489,7 -4476,7 +4493,7 @@@ suspend_thread_critical (MonoThreadInf
        gboolean running_managed;
  
        ji = mono_thread_info_get_last_managed (info);
 -      protected_wrapper = ji && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
 +      protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
  
        if (running_managed && !protected_wrapper) {
                if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
                        InterlockedIncrement (&thread_interruption_requested);
                if (data->interrupt)
-                       data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+                       data->interrupt_token = mono_thread_info_prepare_interrupt (thread->thread_info);
                
                if (mono_thread_notify_pending_exc_fn && !running_managed)
                        /* The JIT will notify the thread about the interruption */
@@@ -4526,8 -4513,8 +4530,8 @@@ suspend_thread_internal (MonoInternalTh
                data.interrupt = interrupt;
  
                mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, interrupt, suspend_thread_critical, &data);
-               if (data.interrupt_handle)
-                       mono_thread_info_finish_interrupt (data.interrupt_handle);
+               if (data.interrupt_token)
+                       mono_thread_info_finish_interrupt (data.interrupt_token);
                UNLOCK_THREAD (thread);
        }
  }
index e9ca1415c04eee3f37dfd855b451b738d72f7ab6,e0f93c9aafd705caad3eb082c7dae3d0a00d6a2d..b2e4bd37bcc93a8ec42eb3e9a6f844ecf36e353e
@@@ -236,30 -236,6 +236,6 @@@ mono_threads_core_open_thread_handle (H
        return handle;
  }
  
- gpointer
- mono_threads_core_prepare_interrupt (HANDLE thread_handle)
- {
-       return wapi_prepare_interrupt_thread (thread_handle);
- }
- void
- mono_threads_core_finish_interrupt (gpointer wait_handle)
- {
-       wapi_finish_interrupt_thread (wait_handle);
- }
- void
- mono_threads_core_self_interrupt (void)
- {
-       wapi_self_interrupt ();
- }
- void
- mono_threads_core_clear_interruption (void)
- {
-       wapi_clear_interruption ();
- }
  int
  mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
  {
  #elif defined(__native_client__)
        /* Workaround pthread_kill abort() in NaCl glibc. */
        return 0;
 +#elif !defined(HAVE_PTHREAD_KILL)
 +      g_error ("pthread_kill() is not supported by this platform");
  #else
        return pthread_kill (mono_thread_info_get_tid (info), signum);
  #endif