611cb76ffbc69038a739470bcad367cd6df8102a
[mono.git] / mono / utils / os-event-unix.c
1 /*
2  * os-event-unix.c: MonoOSEvent on Unix
3  *
4  * Author:
5  *      Ludovic Henry (luhenry@microsoft.com)
6  *
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "os-event.h"
11
12 #include "atomic.h"
13 #include "mono-lazy-init.h"
14 #include "mono-threads.h"
15 #include "mono-time.h"
16
17 static mono_lazy_init_t status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
18
19 static mono_mutex_t signal_mutex;
20
21 static void
22 initialize (void)
23 {
24         mono_os_mutex_init (&signal_mutex);
25 }
26
27 void
28 mono_os_event_init (MonoOSEvent *event, gboolean initial)
29 {
30         g_assert (event);
31
32         mono_lazy_initialize (&status, initialize);
33
34         event->conds = g_ptr_array_new ();
35         event->signalled = initial;
36 }
37
38 void
39 mono_os_event_destroy (MonoOSEvent *event)
40 {
41         g_assert (mono_lazy_is_initialized (&status));
42
43         g_assert (event);
44
45         if (event->conds->len > 0)
46                 g_error ("%s: cannot destroy osevent, there are still %d threads waiting on it", __func__, event->conds->len);
47
48         g_ptr_array_free (event->conds, TRUE);
49 }
50
51 static gboolean
52 mono_os_event_is_signalled (MonoOSEvent *event)
53 {
54         return event->signalled;
55 }
56
57 void
58 mono_os_event_set (MonoOSEvent *event)
59 {
60         gsize i;
61
62         g_assert (mono_lazy_is_initialized (&status));
63
64         g_assert (event);
65
66         mono_os_mutex_lock (&signal_mutex);
67
68         event->signalled = TRUE;
69
70         for (i = 0; i < event->conds->len; ++i)
71                 mono_os_cond_signal ((mono_cond_t*) event->conds->pdata [i]);
72
73         mono_os_mutex_unlock (&signal_mutex);
74 }
75
76 void
77 mono_os_event_reset (MonoOSEvent *event)
78 {
79         g_assert (mono_lazy_is_initialized (&status));
80
81         g_assert (event);
82
83         mono_os_mutex_lock (&signal_mutex);
84
85         event->signalled = FALSE;
86
87         mono_os_mutex_unlock (&signal_mutex);
88 }
89
90 MonoOSEventWaitRet
91 mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
92 {
93         return mono_os_event_wait_multiple (&event, 1, TRUE, timeout);
94 }
95
96 typedef struct {
97         guint32 ref;
98         MonoOSEvent event;
99 } OSEventWaitData;
100
101 static void
102 signal_and_unref (gpointer user_data)
103 {
104         OSEventWaitData *data;
105
106         data = (OSEventWaitData*) user_data;
107
108         mono_os_event_set (&data->event);
109         if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
110                 mono_os_event_destroy (&data->event);
111                 g_free (data);
112         }
113 }
114
115 MonoOSEventWaitRet
116 mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
117 {
118         MonoOSEventWaitRet ret;
119         mono_cond_t signal_cond;
120         OSEventWaitData *data;
121         gboolean alerted;
122         gint64 start;
123         gint i;
124
125         g_assert (mono_lazy_is_initialized (&status));
126
127         g_assert (events);
128         g_assert (nevents > 0);
129         g_assert (nevents <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
130
131         for (i = 0; i < nevents; ++i)
132                 g_assert (events [i]);
133
134         data = g_new0 (OSEventWaitData, 1);
135         data->ref = 2;
136         mono_os_event_init (&data->event, FALSE);
137
138         alerted = FALSE;
139         mono_thread_info_install_interrupt (signal_and_unref, data, &alerted);
140         if (alerted) {
141                 mono_os_event_destroy (&data->event);
142                 g_free (data);
143                 return MONO_OS_EVENT_WAIT_RET_ALERTED;
144         }
145
146         if (timeout != MONO_INFINITE_WAIT)
147                 start = mono_msec_ticks ();
148
149         mono_os_cond_init (&signal_cond);
150
151         mono_os_mutex_lock (&signal_mutex);
152
153         for (i = 0; i < nevents; ++i)
154                 g_ptr_array_add (events [i]->conds, &signal_cond);
155
156         g_ptr_array_add (data->event.conds, &signal_cond);
157
158         for (;;) {
159                 gint count, lowest;
160                 gboolean signalled;
161
162                 count = 0;
163                 lowest = -1;
164
165                 for (i = 0; i < nevents; ++i) {
166                         if (mono_os_event_is_signalled (events [i])) {
167                                 count += 1;
168                                 if (lowest == -1)
169                                         lowest = i;
170                         }
171                 }
172
173                 if (mono_os_event_is_signalled (&data->event))
174                         signalled = TRUE;
175                 else if (waitall)
176                         signalled = (count == nevents);
177                 else /* waitany */
178                         signalled = (count > 0);
179
180                 if (signalled) {
181                         ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + lowest;
182                         goto done;
183                 }
184
185                 if (timeout == MONO_INFINITE_WAIT) {
186                         mono_os_cond_wait (&signal_cond, &signal_mutex);
187                 } else {
188                         gint64 elapsed;
189                         gint res;
190
191                         elapsed = mono_msec_ticks () - start;
192                         if (elapsed >= timeout) {
193                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
194                                 goto done;
195                         }
196
197                         res = mono_os_cond_timedwait (&signal_cond, &signal_mutex, timeout - elapsed);
198                         if (res != 0) {
199                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
200                                 goto done;
201                         }
202                 }
203         }
204
205 done:
206         for (i = 0; i < nevents; ++i)
207                 g_ptr_array_remove (events [i]->conds, &signal_cond);
208
209         g_ptr_array_remove (data->event.conds, &signal_cond);
210
211         mono_os_mutex_unlock (&signal_mutex);
212
213         mono_os_cond_destroy (&signal_cond);
214
215         mono_thread_info_uninstall_interrupt (&alerted);
216         if (alerted) {
217                 if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
218                         mono_os_event_destroy (&data->event);
219                         g_free (data);
220                 }
221                 return MONO_OS_EVENT_WAIT_RET_ALERTED;
222         }
223
224         mono_os_event_destroy (&data->event);
225         g_free (data);
226
227         return ret;
228 }