Revert "[threads] Make OSEvent alertable to fix bug #51653" (#4346)
[mono.git] / mono / utils / os-event-unix.c
index 12ef05c26978b023e3fee21fe8b40be76bbafa4e..611cb76ffbc69038a739470bcad367cd6df8102a 100644 (file)
 static mono_lazy_init_t status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
 
 static mono_mutex_t signal_mutex;
-static mono_cond_t signal_cond;
 
 static void
 initialize (void)
 {
        mono_os_mutex_init (&signal_mutex);
-       mono_os_cond_init (&signal_cond);
 }
 
 void
-mono_os_event_init (MonoOSEvent *event, gboolean manual, gboolean initial)
+mono_os_event_init (MonoOSEvent *event, gboolean initial)
 {
        g_assert (event);
 
        mono_lazy_initialize (&status, initialize);
 
-       mono_os_mutex_init (&event->mutex);
-       mono_os_cond_init (&event->cond);
+       event->conds = g_ptr_array_new ();
        event->signalled = initial;
-       event->manual = manual;
-       event->set_count = (initial && !manual) ? 1 : 0;
 }
 
 void
@@ -47,46 +42,35 @@ mono_os_event_destroy (MonoOSEvent *event)
 
        g_assert (event);
 
-       mono_os_mutex_destroy (&event->mutex);
-       mono_os_cond_destroy (&event->cond);
+       if (event->conds->len > 0)
+               g_error ("%s: cannot destroy osevent, there are still %d threads waiting on it", __func__, event->conds->len);
+
+       g_ptr_array_free (event->conds, TRUE);
 }
 
-static void
-mono_os_event_signal (MonoOSEvent *event, gboolean broadcast)
+static gboolean
+mono_os_event_is_signalled (MonoOSEvent *event)
 {
-       g_assert (event);
-
-       mono_os_mutex_lock (&signal_mutex);
-
-       event->signalled = TRUE;
-
-       if (broadcast)
-               mono_os_cond_broadcast (&event->cond);
-       else
-               mono_os_cond_signal (&event->cond);
-
-       mono_os_cond_broadcast (&signal_cond);
-
-       mono_os_mutex_unlock (&signal_mutex);
+       return event->signalled;
 }
 
 void
 mono_os_event_set (MonoOSEvent *event)
 {
+       gsize i;
+
        g_assert (mono_lazy_is_initialized (&status));
 
        g_assert (event);
 
-       mono_os_mutex_lock (&event->mutex);
+       mono_os_mutex_lock (&signal_mutex);
+
+       event->signalled = TRUE;
 
-       if (event->manual) {
-               mono_os_event_signal (event, FALSE);
-       } else {
-               event->set_count = 1;
-               mono_os_event_signal (event, TRUE);
-       }
+       for (i = 0; i < event->conds->len; ++i)
+               mono_os_cond_signal ((mono_cond_t*) event->conds->pdata [i]);
 
-       mono_os_mutex_unlock (&event->mutex);
+       mono_os_mutex_unlock (&signal_mutex);
 }
 
 void
@@ -96,116 +80,45 @@ mono_os_event_reset (MonoOSEvent *event)
 
        g_assert (event);
 
-       mono_os_mutex_lock (&event->mutex);
-
-       if (event->signalled)
-               event->signalled = FALSE;
-
-       event->set_count = 0;
-
-       mono_os_mutex_unlock (&event->mutex);
-}
-
-static gboolean
-mono_os_event_own (MonoOSEvent *event)
-{
-       g_assert (event);
-
-       if (!event->signalled)
-               return FALSE;
-
-       if (!event->manual) {
-               g_assert (event->set_count > 0);
-               event->set_count -= 1;
+       mono_os_mutex_lock (&signal_mutex);
 
-               if (event->set_count == 0)
-                       mono_os_event_signal (event, FALSE);
-       }
+       event->signalled = FALSE;
 
-       return TRUE;
+       mono_os_mutex_unlock (&signal_mutex);
 }
 
 MonoOSEventWaitRet
 mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
 {
-       MonoOSEventWaitRet ret;
-       gint64 start;
-
-       g_assert (mono_lazy_is_initialized (&status));
-
-       g_assert (event);
-
-       mono_os_mutex_lock (&event->mutex);
-
-       if (timeout != MONO_INFINITE_WAIT)
-               start = mono_msec_ticks ();
-
-       for (;;) {
-               if (mono_os_event_own (event)) {
-                       ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0;
-                       goto done;
-               }
-
-               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;
-                       }
-               }
-       }
-
-done:
-       mono_os_mutex_unlock (&event->mutex);
-
-       return ret;
+       return mono_os_event_wait_multiple (&event, 1, TRUE, timeout);
 }
 
+typedef struct {
+       guint32 ref;
+       MonoOSEvent event;
+} OSEventWaitData;
+
 static void
-mono_os_event_lock_events (MonoOSEvent **events, gsize nevents)
+signal_and_unref (gpointer user_data)
 {
-       gint i, j;
-
-retry:
-       for (i = 0; i < nevents; ++i) {
-               gint res;
+       OSEventWaitData *data;
 
-               res = mono_os_mutex_trylock (&events [i]->mutex);
-               if (res != 0) {
-                       for (j = i - 1; j >= 0; j--)
-                               mono_os_mutex_unlock (&events [j]->mutex);
+       data = (OSEventWaitData*) user_data;
 
-                       mono_thread_info_yield ();
-
-                       goto retry;
-               }
+       mono_os_event_set (&data->event);
+       if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
+               mono_os_event_destroy (&data->event);
+               g_free (data);
        }
 }
 
-static void
-mono_os_event_unlock_events (MonoOSEvent **events, gsize nevents)
-{
-       gint i;
-
-       for (i = 0; i < nevents; ++i)
-               mono_os_mutex_unlock (&events [i]->mutex);
-}
-
 MonoOSEventWaitRet
 mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
 {
        MonoOSEventWaitRet ret;
+       mono_cond_t signal_cond;
+       OSEventWaitData *data;
+       gboolean alerted;
        gint64 start;
        gint i;
 
@@ -215,72 +128,60 @@ 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]);
+
+       data = g_new0 (OSEventWaitData, 1);
+       data->ref = 2;
+       mono_os_event_init (&data->event, FALSE);
+
+       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)
                start = mono_msec_ticks ();
 
+       mono_os_cond_init (&signal_cond);
+
+       mono_os_mutex_lock (&signal_mutex);
+
+       for (i = 0; i < nevents; ++i)
+               g_ptr_array_add (events [i]->conds, &signal_cond);
+
+       g_ptr_array_add (data->event.conds, &signal_cond);
+
        for (;;) {
                gint count, lowest;
                gboolean signalled;
 
-               mono_os_event_lock_events (events, nevents);
-
                count = 0;
                lowest = -1;
 
                for (i = 0; i < nevents; ++i) {
-                       if (events [i]->signalled) {
+                       if (mono_os_event_is_signalled (events [i])) {
                                count += 1;
                                if (lowest == -1)
                                        lowest = i;
                        }
                }
 
-               signalled = (waitall && count == nevents) || (!waitall && count > 0);
-
-               if (signalled) {
-                       for (i = 0; i < nevents; ++i)
-                               mono_os_event_own (events [i]);
-               }
-
-               mono_os_event_unlock_events (events, nevents);
+               if (mono_os_event_is_signalled (&data->event))
+                       signalled = TRUE;
+               else if (waitall)
+                       signalled = (count == nevents);
+               else /* waitany */
+                       signalled = (count > 0);
 
                if (signalled) {
                        ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + lowest;
                        goto done;
                }
 
-               mono_os_mutex_lock (&signal_mutex);
-
-               if (waitall) {
-                       signalled = TRUE;
-                       for (i = 0; i < nevents; ++i) {
-                               if (!events [i]->signalled) {
-                                       signalled = FALSE;
-                                       break;
-                               }
-                       }
-               } else {
-                       signalled = FALSE;
-                       for (i = 0; i < nevents; ++i) {
-                               if (events [i]->signalled) {
-                                       signalled = TRUE;
-                                       break;
-                               }
-                       }
-               }
-
-               if (signalled) {
-                       mono_os_mutex_unlock (&signal_mutex);
-                       continue;
-               }
-
                if (timeout == MONO_INFINITE_WAIT) {
                        mono_os_cond_wait (&signal_cond, &signal_mutex);
                } else {
@@ -289,24 +190,39 @@ mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waita
 
                        elapsed = mono_msec_ticks () - start;
                        if (elapsed >= timeout) {
-                               mono_os_mutex_unlock (&signal_mutex);
-
                                ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
                                goto done;
                        }
 
                        res = mono_os_cond_timedwait (&signal_cond, &signal_mutex, timeout - elapsed);
                        if (res != 0) {
-                               mono_os_mutex_unlock (&signal_mutex);
-
                                ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
                                goto done;
                        }
                }
-
-               mono_os_mutex_unlock (&signal_mutex);
        }
 
 done:
+       for (i = 0; i < nevents; ++i)
+               g_ptr_array_remove (events [i]->conds, &signal_cond);
+
+       g_ptr_array_remove (data->event.conds, &signal_cond);
+
+       mono_os_mutex_unlock (&signal_mutex);
+
+       mono_os_cond_destroy (&signal_cond);
+
+       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;
 }