[os-event] Make unix version alertable (#3909)
[mono-project.git] / mono / utils / os-event-unix.c
blob1d437cf62e315844a814eee235e90c3e4acf5475
1 /*
2 * os-event-unix.c: MonoOSEvent on Unix
4 * Author:
5 * Ludovic Henry (luhenry@microsoft.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8 */
10 #include "os-event.h"
12 #include "atomic.h"
13 #include "mono-lazy-init.h"
14 #include "mono-threads.h"
15 #include "mono-time.h"
17 static mono_lazy_init_t status = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
19 static mono_mutex_t signal_mutex;
20 static mono_cond_t signal_cond;
22 static void
23 initialize (void)
25 mono_os_mutex_init (&signal_mutex);
26 mono_os_cond_init (&signal_cond);
29 void
30 mono_os_event_init (MonoOSEvent *event, gboolean manual, gboolean initial)
32 g_assert (event);
34 mono_lazy_initialize (&status, initialize);
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;
43 void
44 mono_os_event_destroy (MonoOSEvent *event)
46 g_assert (mono_lazy_is_initialized (&status));
48 g_assert (event);
50 mono_os_mutex_destroy (&event->mutex);
51 mono_os_cond_destroy (&event->cond);
54 static gboolean
55 mono_os_event_is_signalled (MonoOSEvent *event)
57 return event->signalled;
60 static void
61 mono_os_event_signal (MonoOSEvent *event, gboolean broadcast)
63 g_assert (event);
65 mono_os_mutex_lock (&signal_mutex);
67 event->signalled = TRUE;
69 if (broadcast)
70 mono_os_cond_broadcast (&event->cond);
71 else
72 mono_os_cond_signal (&event->cond);
74 mono_os_cond_broadcast (&signal_cond);
76 mono_os_mutex_unlock (&signal_mutex);
79 void
80 mono_os_event_set (MonoOSEvent *event)
82 g_assert (mono_lazy_is_initialized (&status));
84 g_assert (event);
86 mono_os_mutex_lock (&event->mutex);
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);
95 mono_os_mutex_unlock (&event->mutex);
98 void
99 mono_os_event_reset (MonoOSEvent *event)
101 g_assert (mono_lazy_is_initialized (&status));
103 g_assert (event);
105 mono_os_mutex_lock (&event->mutex);
107 if (mono_os_event_is_signalled (event))
108 event->signalled = FALSE;
110 event->set_count = 0;
112 mono_os_mutex_unlock (&event->mutex);
115 static gboolean
116 mono_os_event_own (MonoOSEvent *event)
118 g_assert (event);
120 if (!mono_os_event_is_signalled (event))
121 return FALSE;
123 if (!event->manual) {
124 g_assert (event->set_count > 0);
125 event->set_count -= 1;
127 if (event->set_count == 0)
128 mono_os_event_signal (event, FALSE);
131 return TRUE;
134 MonoOSEventWaitRet
135 mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
137 return mono_os_event_wait_multiple (&event, 1, TRUE, timeout);
140 typedef struct {
141 guint32 ref;
142 MonoOSEvent event;
143 } OSEventWaitData;
145 static void
146 signal_and_unref (gpointer user_data)
148 OSEventWaitData *data;
150 data = (OSEventWaitData*) user_data;
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);
159 static void
160 mono_os_event_lock_events (MonoOSEvent **events, gsize nevents)
162 gint i, j;
164 retry:
165 for (i = 0; i < nevents; ++i) {
166 gint res;
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);
173 mono_thread_info_yield ();
175 goto retry;
180 static void
181 mono_os_event_unlock_events (MonoOSEvent **events, gsize nevents)
183 gint i;
185 for (i = 0; i < nevents; ++i)
186 mono_os_mutex_unlock (&events [i]->mutex);
189 MonoOSEventWaitRet
190 mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
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;
199 g_assert (mono_lazy_is_initialized (&status));
201 g_assert (events);
202 g_assert (nevents > 0);
203 g_assert (nevents <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
205 for (i = 0; i < nevents; ++i)
206 g_assert (events [i]);
208 memcpy (innerevents, events, sizeof (MonoOSEvent*) * nevents);
210 data = g_new0 (OSEventWaitData, 1);
211 data->ref = 2;
212 mono_os_event_init (&data->event, TRUE, FALSE);
214 innerevents [nevents ++] = &data->event;
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;
224 if (timeout != MONO_INFINITE_WAIT)
225 start = mono_msec_ticks ();
227 for (;;) {
228 gint count, lowest;
229 gboolean signalled;
231 mono_os_event_lock_events (innerevents, nevents);
233 count = 0;
234 lowest = -1;
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;
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);
251 if (signalled) {
252 for (i = 0; i < nevents - 1; ++i)
253 mono_os_event_own (innerevents [i]);
256 mono_os_event_unlock_events (innerevents, nevents);
258 if (signalled) {
259 ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + lowest;
260 goto done;
263 mono_os_mutex_lock (&signal_mutex);
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;
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;
285 if (signalled) {
286 mono_os_mutex_unlock (&signal_mutex);
287 continue;
290 if (timeout == MONO_INFINITE_WAIT) {
291 mono_os_cond_wait (&signal_cond, &signal_mutex);
292 } else {
293 gint64 elapsed;
294 gint res;
296 elapsed = mono_msec_ticks () - start;
297 if (elapsed >= timeout) {
298 mono_os_mutex_unlock (&signal_mutex);
300 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
301 goto done;
304 res = mono_os_cond_timedwait (&signal_cond, &signal_mutex, timeout - elapsed);
305 if (res != 0) {
306 mono_os_mutex_unlock (&signal_mutex);
308 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
309 goto done;
313 mono_os_mutex_unlock (&signal_mutex);
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);
323 return MONO_OS_EVENT_WAIT_RET_ALERTED;
326 mono_os_event_destroy (&data->event);
327 g_free (data);
329 return ret;