[os-event] Make unix version alertable (#3909)
[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 static mono_cond_t signal_cond;
21
22 static void
23 initialize (void)
24 {
25         mono_os_mutex_init (&signal_mutex);
26         mono_os_cond_init (&signal_cond);
27 }
28
29 void
30 mono_os_event_init (MonoOSEvent *event, gboolean manual, gboolean initial)
31 {
32         g_assert (event);
33
34         mono_lazy_initialize (&status, initialize);
35
36         mono_os_mutex_init (&event->mutex);
37         mono_os_cond_init (&event->cond);
38         event->signalled = initial;
39         event->manual = manual;
40         event->set_count = (initial && !manual) ? 1 : 0;
41 }
42
43 void
44 mono_os_event_destroy (MonoOSEvent *event)
45 {
46         g_assert (mono_lazy_is_initialized (&status));
47
48         g_assert (event);
49
50         mono_os_mutex_destroy (&event->mutex);
51         mono_os_cond_destroy (&event->cond);
52 }
53
54 static gboolean
55 mono_os_event_is_signalled (MonoOSEvent *event)
56 {
57         return event->signalled;
58 }
59
60 static void
61 mono_os_event_signal (MonoOSEvent *event, gboolean broadcast)
62 {
63         g_assert (event);
64
65         mono_os_mutex_lock (&signal_mutex);
66
67         event->signalled = TRUE;
68
69         if (broadcast)
70                 mono_os_cond_broadcast (&event->cond);
71         else
72                 mono_os_cond_signal (&event->cond);
73
74         mono_os_cond_broadcast (&signal_cond);
75
76         mono_os_mutex_unlock (&signal_mutex);
77 }
78
79 void
80 mono_os_event_set (MonoOSEvent *event)
81 {
82         g_assert (mono_lazy_is_initialized (&status));
83
84         g_assert (event);
85
86         mono_os_mutex_lock (&event->mutex);
87
88         if (event->manual) {
89                 mono_os_event_signal (event, TRUE);
90         } else {
91                 event->set_count = 1;
92                 mono_os_event_signal (event, FALSE);
93         }
94
95         mono_os_mutex_unlock (&event->mutex);
96 }
97
98 void
99 mono_os_event_reset (MonoOSEvent *event)
100 {
101         g_assert (mono_lazy_is_initialized (&status));
102
103         g_assert (event);
104
105         mono_os_mutex_lock (&event->mutex);
106
107         if (mono_os_event_is_signalled (event))
108                 event->signalled = FALSE;
109
110         event->set_count = 0;
111
112         mono_os_mutex_unlock (&event->mutex);
113 }
114
115 static gboolean
116 mono_os_event_own (MonoOSEvent *event)
117 {
118         g_assert (event);
119
120         if (!mono_os_event_is_signalled (event))
121                 return FALSE;
122
123         if (!event->manual) {
124                 g_assert (event->set_count > 0);
125                 event->set_count -= 1;
126
127                 if (event->set_count == 0)
128                         mono_os_event_signal (event, FALSE);
129         }
130
131         return TRUE;
132 }
133
134 MonoOSEventWaitRet
135 mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
136 {
137         return mono_os_event_wait_multiple (&event, 1, TRUE, timeout);
138 }
139
140 typedef struct {
141         guint32 ref;
142         MonoOSEvent event;
143 } OSEventWaitData;
144
145 static void
146 signal_and_unref (gpointer user_data)
147 {
148         OSEventWaitData *data;
149
150         data = (OSEventWaitData*) user_data;
151
152         mono_os_event_set (&data->event);
153         if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
154                 mono_os_event_destroy (&data->event);
155                 g_free (data);
156         }
157 }
158
159 static void
160 mono_os_event_lock_events (MonoOSEvent **events, gsize nevents)
161 {
162         gint i, j;
163
164 retry:
165         for (i = 0; i < nevents; ++i) {
166                 gint res;
167
168                 res = mono_os_mutex_trylock (&events [i]->mutex);
169                 if (res != 0) {
170                         for (j = i - 1; j >= 0; j--)
171                                 mono_os_mutex_unlock (&events [j]->mutex);
172
173                         mono_thread_info_yield ();
174
175                         goto retry;
176                 }
177         }
178 }
179
180 static void
181 mono_os_event_unlock_events (MonoOSEvent **events, gsize nevents)
182 {
183         gint i;
184
185         for (i = 0; i < nevents; ++i)
186                 mono_os_mutex_unlock (&events [i]->mutex);
187 }
188
189 MonoOSEventWaitRet
190 mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
191 {
192         MonoOSEventWaitRet ret;
193         MonoOSEvent *innerevents [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS + 1];
194         OSEventWaitData *data;
195         gboolean alerted;
196         gint64 start;
197         gint i;
198
199         g_assert (mono_lazy_is_initialized (&status));
200
201         g_assert (events);
202         g_assert (nevents > 0);
203         g_assert (nevents <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
204
205         for (i = 0; i < nevents; ++i)
206                 g_assert (events [i]);
207
208         memcpy (innerevents, events, sizeof (MonoOSEvent*) * nevents);
209
210         data = g_new0 (OSEventWaitData, 1);
211         data->ref = 2;
212         mono_os_event_init (&data->event, TRUE, FALSE);
213
214         innerevents [nevents ++] = &data->event;
215
216         alerted = FALSE;
217         mono_thread_info_install_interrupt (signal_and_unref, data, &alerted);
218         if (alerted) {
219                 mono_os_event_destroy (&data->event);
220                 g_free (data);
221                 return MONO_OS_EVENT_WAIT_RET_ALERTED;
222         }
223
224         if (timeout != MONO_INFINITE_WAIT)
225                 start = mono_msec_ticks ();
226
227         for (;;) {
228                 gint count, lowest;
229                 gboolean signalled;
230
231                 mono_os_event_lock_events (innerevents, nevents);
232
233                 count = 0;
234                 lowest = -1;
235
236                 for (i = 0; i < nevents - 1; ++i) {
237                         if (mono_os_event_is_signalled (innerevents [i])) {
238                                 count += 1;
239                                 if (lowest == -1)
240                                         lowest = i;
241                         }
242                 }
243
244                 if (mono_os_event_is_signalled (&data->event))
245                         signalled = TRUE;
246                 else if (waitall)
247                         signalled = (count == nevents - 1);
248                 else /* waitany */
249                         signalled = (count > 0);
250
251                 if (signalled) {
252                         for (i = 0; i < nevents - 1; ++i)
253                                 mono_os_event_own (innerevents [i]);
254                 }
255
256                 mono_os_event_unlock_events (innerevents, nevents);
257
258                 if (signalled) {
259                         ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + lowest;
260                         goto done;
261                 }
262
263                 mono_os_mutex_lock (&signal_mutex);
264
265                 if (mono_os_event_is_signalled (&data->event)) {
266                         signalled = TRUE;
267                 } else if (waitall) {
268                         signalled = TRUE;
269                         for (i = 0; i < nevents - 1; ++i) {
270                                 if (!mono_os_event_is_signalled (innerevents [i])) {
271                                         signalled = FALSE;
272                                         break;
273                                 }
274                         }
275                 } else {
276                         signalled = FALSE;
277                         for (i = 0; i < nevents - 1; ++i) {
278                                 if (mono_os_event_is_signalled (innerevents [i])) {
279                                         signalled = TRUE;
280                                         break;
281                                 }
282                         }
283                 }
284
285                 if (signalled) {
286                         mono_os_mutex_unlock (&signal_mutex);
287                         continue;
288                 }
289
290                 if (timeout == MONO_INFINITE_WAIT) {
291                         mono_os_cond_wait (&signal_cond, &signal_mutex);
292                 } else {
293                         gint64 elapsed;
294                         gint res;
295
296                         elapsed = mono_msec_ticks () - start;
297                         if (elapsed >= timeout) {
298                                 mono_os_mutex_unlock (&signal_mutex);
299
300                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
301                                 goto done;
302                         }
303
304                         res = mono_os_cond_timedwait (&signal_cond, &signal_mutex, timeout - elapsed);
305                         if (res != 0) {
306                                 mono_os_mutex_unlock (&signal_mutex);
307
308                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
309                                 goto done;
310                         }
311                 }
312
313                 mono_os_mutex_unlock (&signal_mutex);
314         }
315
316 done:
317         mono_thread_info_uninstall_interrupt (&alerted);
318         if (alerted) {
319                 if (InterlockedDecrement ((gint32*) &data->ref) == 0) {
320                         mono_os_event_destroy (&data->event);
321                         g_free (data);
322                 }
323                 return MONO_OS_EVENT_WAIT_RET_ALERTED;
324         }
325
326         mono_os_event_destroy (&data->event);
327         g_free (data);
328
329         return ret;
330 }