[os-event] Make unix version alertable (#3909)
authorLudovic Henry <ludovic@xamarin.com>
Wed, 9 Nov 2016 12:50:12 +0000 (07:50 -0500)
committerGitHub <noreply@github.com>
Wed, 9 Nov 2016 12:50:12 +0000 (07:50 -0500)
This fixes `appdomain_unload.exe` test timeout. The following would happen:
 - T1 unload the current domain
  - T1 launch T2 to unload domain
  - T1 waits on T2 to finish
 - T2 tries to abort T1 <-- because the wait on T2 is not alertable, T1 is not going to be aborted

mono/metadata/threads.c
mono/utils/os-event-unix.c

index 315183a7762c421abfc3a07ef7c6c672268d02f8..ab2da86cb8d2c030623f719d4ff011253bab7e9a 100644 (file)
@@ -4872,7 +4872,7 @@ self_suspend_internal (void)
 
                MONO_ENTER_GC_SAFE;
                res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT);
-               g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
+               g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
                MONO_EXIT_GC_SAFE;
        }
 }
index a2678b4f949180c4530bc4a6bab5e009e4b8aad1..1d437cf62e315844a814eee235e90c3e4acf5475 100644 (file)
@@ -51,6 +51,12 @@ mono_os_event_destroy (MonoOSEvent *event)
        mono_os_cond_destroy (&event->cond);
 }
 
+static gboolean
+mono_os_event_is_signalled (MonoOSEvent *event)
+{
+       return event->signalled;
+}
+
 static void
 mono_os_event_signal (MonoOSEvent *event, gboolean broadcast)
 {
@@ -98,7 +104,7 @@ mono_os_event_reset (MonoOSEvent *event)
 
        mono_os_mutex_lock (&event->mutex);
 
-       if (event->signalled)
+       if (mono_os_event_is_signalled (event))
                event->signalled = FALSE;
 
        event->set_count = 0;
@@ -111,7 +117,7 @@ mono_os_event_own (MonoOSEvent *event)
 {
        g_assert (event);
 
-       if (!event->signalled)
+       if (!mono_os_event_is_signalled (event))
                return FALSE;
 
        if (!event->manual) {
@@ -128,48 +134,26 @@ mono_os_event_own (MonoOSEvent *event)
 MonoOSEventWaitRet
 mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
 {
-       MonoOSEventWaitRet ret;
-       gint64 start;
-
-       g_assert (mono_lazy_is_initialized (&status));
+       return mono_os_event_wait_multiple (&event, 1, TRUE, timeout);
+}
 
-       g_assert (event);
+typedef struct {
+       guint32 ref;
+       MonoOSEvent event;
+} OSEventWaitData;
 
-       mono_os_mutex_lock (&event->mutex);
-
-       if (timeout != MONO_INFINITE_WAIT)
-               start = mono_msec_ticks ();
+static void
+signal_and_unref (gpointer user_data)
+{
+       OSEventWaitData *data;
 
-       for (;;) {
-               if (mono_os_event_own (event)) {
-                       ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0;
-                       goto done;
-               }
+       data = (OSEventWaitData*) user_data;
 
-               if (timeout == MONO_INFINITE_WAIT) {
-                       mono_os_cond_wait (&event->cond, &event->mutex);
-               } else {
-                       gint64 elapsed;
-                       gint res;
-
-                       elapsed = mono_msec_ticks () - start;
-                       if (elapsed >= timeout) {
-                               ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
-                               goto done;
-                       }
-
-                       res = mono_os_cond_timedwait (&event->cond, &event->mutex, timeout - elapsed);
-                       if (res != 0) {
-                               ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
-                               goto done;
-                       }
-               }
+       mono_os_event_set (&data->event);
+       if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
+               mono_os_event_destroy (&data->event);
+               g_free (data);
        }
-
-done:
-       mono_os_mutex_unlock (&event->mutex);
-
-       return ret;
 }
 
 static void
@@ -206,6 +190,9 @@ MonoOSEventWaitRet
 mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
 {
        MonoOSEventWaitRet ret;
+       MonoOSEvent *innerevents [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS + 1];
+       OSEventWaitData *data;
+       gboolean alerted;
        gint64 start;
        gint i;
 
@@ -215,11 +202,23 @@ mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waita
        g_assert (nevents > 0);
        g_assert (nevents <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
 
-       if (nevents == 1)
-               return mono_os_event_wait_one (events [0], timeout);
-
-       for (i = 0; i < nevents; ++i) {
+       for (i = 0; i < nevents; ++i)
                g_assert (events [i]);
+
+       memcpy (innerevents, events, sizeof (MonoOSEvent*) * nevents);
+
+       data = g_new0 (OSEventWaitData, 1);
+       data->ref = 2;
+       mono_os_event_init (&data->event, TRUE, FALSE);
+
+       innerevents [nevents ++] = &data->event;
+
+       alerted = FALSE;
+       mono_thread_info_install_interrupt (signal_and_unref, data, &alerted);
+       if (alerted) {
+               mono_os_event_destroy (&data->event);
+               g_free (data);
+               return MONO_OS_EVENT_WAIT_RET_ALERTED;
        }
 
        if (timeout != MONO_INFINITE_WAIT)
@@ -229,27 +228,32 @@ mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waita
                gint count, lowest;
                gboolean signalled;
 
-               mono_os_event_lock_events (events, nevents);
+               mono_os_event_lock_events (innerevents, nevents);
 
                count = 0;
                lowest = -1;
 
-               for (i = 0; i < nevents; ++i) {
-                       if (events [i]->signalled) {
+               for (i = 0; i < nevents - 1; ++i) {
+                       if (mono_os_event_is_signalled (innerevents [i])) {
                                count += 1;
                                if (lowest == -1)
                                        lowest = i;
                        }
                }
 
-               signalled = (waitall && count == nevents) || (!waitall && count > 0);
+               if (mono_os_event_is_signalled (&data->event))
+                       signalled = TRUE;
+               else if (waitall)
+                       signalled = (count == nevents - 1);
+               else /* waitany */
+                       signalled = (count > 0);
 
                if (signalled) {
-                       for (i = 0; i < nevents; ++i)
-                               mono_os_event_own (events [i]);
+                       for (i = 0; i < nevents - 1; ++i)
+                               mono_os_event_own (innerevents [i]);
                }
 
-               mono_os_event_unlock_events (events, nevents);
+               mono_os_event_unlock_events (innerevents, nevents);
 
                if (signalled) {
                        ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + lowest;
@@ -258,18 +262,20 @@ mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waita
 
                mono_os_mutex_lock (&signal_mutex);
 
-               if (waitall) {
+               if (mono_os_event_is_signalled (&data->event)) {
+                       signalled = TRUE;
+               } else if (waitall) {
                        signalled = TRUE;
-                       for (i = 0; i < nevents; ++i) {
-                               if (!events [i]->signalled) {
+                       for (i = 0; i < nevents - 1; ++i) {
+                               if (!mono_os_event_is_signalled (innerevents [i])) {
                                        signalled = FALSE;
                                        break;
                                }
                        }
                } else {
                        signalled = FALSE;
-                       for (i = 0; i < nevents; ++i) {
-                               if (events [i]->signalled) {
+                       for (i = 0; i < nevents - 1; ++i) {
+                               if (mono_os_event_is_signalled (innerevents [i])) {
                                        signalled = TRUE;
                                        break;
                                }
@@ -308,5 +314,17 @@ mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waita
        }
 
 done:
+       mono_thread_info_uninstall_interrupt (&alerted);
+       if (alerted) {
+               if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
+                       mono_os_event_destroy (&data->event);
+                       g_free (data);
+               }
+               return MONO_OS_EVENT_WAIT_RET_ALERTED;
+       }
+
+       mono_os_event_destroy (&data->event);
+       g_free (data);
+
        return ret;
 }