[runtime] Add MonoOSEvent
authorLudovic Henry <ludovic@xamarin.com>
Tue, 27 Sep 2016 19:08:27 +0000 (21:08 +0200)
committerLudovic Henry <ludovic@xamarin.com>
Mon, 24 Oct 2016 17:27:03 +0000 (13:27 -0400)
mono/utils/Makefile.am
mono/utils/os-event-unix.c [new file with mode: 0644]
mono/utils/os-event-win32.c [new file with mode: 0644]
mono/utils/os-event.h [new file with mode: 0644]

index 5bc5bbd600326c90c774a43f7280e805ff0e1c04..b569936d4066c7af6e50baa50633ebed7e29d016 100644 (file)
@@ -11,7 +11,20 @@ mono-dtrace.h: $(top_srcdir)/data/mono.d
 
 endif
 
+if HOST_WIN32
+win32_sources = \
+       os-event-win32.c
+
+platform_sources = $(win32_sources)
+else
+unix_sources = \
+       os-event-unix.c
+
+platform_sources = $(unix_sources)
+endif
+
 monoutils_sources = \
+       $(platform_sources)     \
        mono-md5.c              \
        mono-sha1.c             \
        mono-logger.c           \
@@ -164,7 +177,8 @@ monoutils_sources = \
        checked-build.c \
        checked-build.h \
        w32handle.c \
-       w32handle.h
+       w32handle.h \
+       os-event.h
 
 arch_sources = 
 
diff --git a/mono/utils/os-event-unix.c b/mono/utils/os-event-unix.c
new file mode 100644 (file)
index 0000000..12ef05c
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * os-event-unix.c: MonoOSEvent on Unix
+ *
+ * Author:
+ *     Ludovic Henry (luhenry@microsoft.com)
+ *
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include "os-event.h"
+
+#include "atomic.h"
+#include "mono-lazy-init.h"
+#include "mono-threads.h"
+#include "mono-time.h"
+
+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)
+{
+       g_assert (event);
+
+       mono_lazy_initialize (&status, initialize);
+
+       mono_os_mutex_init (&event->mutex);
+       mono_os_cond_init (&event->cond);
+       event->signalled = initial;
+       event->manual = manual;
+       event->set_count = (initial && !manual) ? 1 : 0;
+}
+
+void
+mono_os_event_destroy (MonoOSEvent *event)
+{
+       g_assert (mono_lazy_is_initialized (&status));
+
+       g_assert (event);
+
+       mono_os_mutex_destroy (&event->mutex);
+       mono_os_cond_destroy (&event->cond);
+}
+
+static void
+mono_os_event_signal (MonoOSEvent *event, gboolean broadcast)
+{
+       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);
+}
+
+void
+mono_os_event_set (MonoOSEvent *event)
+{
+       g_assert (mono_lazy_is_initialized (&status));
+
+       g_assert (event);
+
+       mono_os_mutex_lock (&event->mutex);
+
+       if (event->manual) {
+               mono_os_event_signal (event, FALSE);
+       } else {
+               event->set_count = 1;
+               mono_os_event_signal (event, TRUE);
+       }
+
+       mono_os_mutex_unlock (&event->mutex);
+}
+
+void
+mono_os_event_reset (MonoOSEvent *event)
+{
+       g_assert (mono_lazy_is_initialized (&status));
+
+       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;
+
+               if (event->set_count == 0)
+                       mono_os_event_signal (event, FALSE);
+       }
+
+       return TRUE;
+}
+
+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;
+}
+
+static void
+mono_os_event_lock_events (MonoOSEvent **events, gsize nevents)
+{
+       gint i, j;
+
+retry:
+       for (i = 0; i < nevents; ++i) {
+               gint res;
+
+               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);
+
+                       mono_thread_info_yield ();
+
+                       goto retry;
+               }
+       }
+}
+
+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;
+       gint64 start;
+       gint i;
+
+       g_assert (mono_lazy_is_initialized (&status));
+
+       g_assert (events);
+       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) {
+               g_assert (events [i]);
+       }
+
+       if (timeout != MONO_INFINITE_WAIT)
+               start = mono_msec_ticks ();
+
+       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) {
+                               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 (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 {
+                       gint64 elapsed;
+                       gint res;
+
+                       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:
+       return ret;
+}
diff --git a/mono/utils/os-event-win32.c b/mono/utils/os-event-win32.c
new file mode 100644 (file)
index 0000000..26ce59e
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * os-event-win32.c: MonoOSEvent on Win32
+ *
+ * Author:
+ *     Ludovic Henry (luhenry@microsoft.com)
+ *
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+#include "os-event.h"
+
+#include <windows.h>
+#include <winbase.h>
+
+#include "atomic.h"
+
+void
+mono_os_event_init (MonoOSEvent *event, gboolean manual, gboolean initial, MonoOSEventFreeCb free_cb)
+{
+       g_assert (event);
+
+       event->handle = CreateEvent (NULL, manual, initial, NULL);
+       if (G_UNLIKELY (!event->handle))
+               g_error ("%s: CreateEvent failed with error %d", __func__, GetLastError ());
+}
+
+void
+mono_os_event_destroy (MonoOSEvent *event)
+{
+       BOOL res;
+
+       g_assert (event);
+       g_assert (event->handle);
+
+       res = CloseHandle (event->handle);
+       if (G_UNLIKELY (res == 0))
+               g_error ("%s: CloseHandle failed with error %d", __func__, GetLastError ());
+}
+
+void
+mono_os_event_set (MonoOSEvent *event)
+{
+       BOOL res;
+
+       g_assert (event);
+       g_assert (event->handle);
+
+       res = SetEvent (event->handle);
+       if (G_UNLIKELY (res == 0))
+               g_error ("%s: SetEvent failed with error %d", __func__, GetLastError ());
+}
+
+void
+mono_os_event_reset (MonoOSEvent *event)
+{
+       BOOL res;
+
+       g_assert (event);
+       g_assert (event->handle);
+
+       res = ResetEvent (event->handle);
+       if (G_UNLIKELY (res == 0))
+               g_error ("%s: ResetEvent failed with error %d", __func__, GetLastError ());
+}
+
+MonoOSEventWaitRet
+mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
+{
+       DWORD res;
+
+       g_assert (event);
+       g_assert (event->handle);
+
+       res = WaitForSingleObjectEx (event->handle, timeout, TRUE);
+       if (res == WAIT_OBJECT_0)
+               return MONO_OS_EVENT_WAIT_RET_SUCCESS_0;
+       else if (res == WAIT_IO_COMPLETION)
+               return MONO_OS_EVENT_WAIT_RET_ALERTED;
+       else if (res == WAIT_TIMEOUT)
+               return MONO_OS_EVENT_WAIT_RET_TIMEOUT;
+       else if (res == WAIT_FAILED)
+               g_error ("%s: WaitForSingleObjectEx failed with error %d", __func__, GetLastError ());
+       else
+               g_error ("%s: unknown res value %d", __func__, res);
+}
+
+MonoOSEventWaitRet
+mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
+{
+       DWORD res;
+       gpointer handles [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
+       gint i;
+
+       g_assert (events);
+       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) {
+               g_assert (events [i]);
+               g_assert (events [i]->handle);
+               handles [i] = events [i]->handle;
+       }
+
+       res = WaitForMultipleObjectsEx (nevents, handles, waitall, timeout, TRUE);
+       if (res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS)
+               return MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + (res - WAIT_OBJECT_0);
+       else if (res == WAIT_IO_COMPLETION)
+               return MONO_OS_EVENT_WAIT_RET_ALERTED;
+       else if (res == WAIT_TIMEOUT)
+               return MONO_OS_EVENT_WAIT_RET_TIMEOUT;
+       else if (res == WAIT_FAILED)
+               g_error ("%s: WaitForSingleObjectEx failed with error %d", __func__, GetLastError ());
+       else
+               g_error ("%s: unknown res value %d", __func__, res);
+}
diff --git a/mono/utils/os-event.h b/mono/utils/os-event.h
new file mode 100644 (file)
index 0000000..81260b9
--- /dev/null
@@ -0,0 +1,56 @@
+
+#ifndef _MONO_UTILS_OS_EVENT_H_
+#define _MONO_UTILS_OS_EVENT_H_
+
+#include <config.h>
+#include <glib.h>
+
+#include "mono-os-mutex.h"
+
+#ifndef MONO_INFINITE_WAIT
+#define MONO_INFINITE_WAIT ((guint32) 0xFFFFFFFF)
+#endif
+
+#define MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS 64
+
+typedef enum {
+       MONO_OS_EVENT_WAIT_RET_SUCCESS_0 =  0,
+       MONO_OS_EVENT_WAIT_RET_ALERTED   = -1,
+       MONO_OS_EVENT_WAIT_RET_TIMEOUT   = -2,
+} MonoOSEventWaitRet;
+
+typedef struct _MonoOSEvent MonoOSEvent;
+
+typedef void (*MonoOSEventFreeCb) (MonoOSEvent*);
+
+struct _MonoOSEvent {
+#ifdef HOST_WIN32
+       gpointer handle;
+#else
+       mono_mutex_t mutex;
+       mono_cond_t cond;
+       gboolean manual;
+       gboolean signalled;
+       guint32 set_count;
+#endif
+};
+
+void
+mono_os_event_init (MonoOSEvent *event, gboolean manual, gboolean initial);
+
+void
+mono_os_event_destroy (MonoOSEvent *event);
+
+void
+mono_os_event_set (MonoOSEvent *event);
+
+void
+mono_os_event_reset (MonoOSEvent *event);
+
+MonoOSEventWaitRet
+mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout);
+
+MonoOSEventWaitRet
+mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout);
+
+#endif /* _MONO_UTILS_OS_EVENT_H_ */