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