Merge pull request #3773 from mono/bockbuild-integration
[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 void
55 mono_os_event_signal (MonoOSEvent *event, gboolean broadcast)
56 {
57         g_assert (event);
58
59         mono_os_mutex_lock (&signal_mutex);
60
61         event->signalled = TRUE;
62
63         if (broadcast)
64                 mono_os_cond_broadcast (&event->cond);
65         else
66                 mono_os_cond_signal (&event->cond);
67
68         mono_os_cond_broadcast (&signal_cond);
69
70         mono_os_mutex_unlock (&signal_mutex);
71 }
72
73 void
74 mono_os_event_set (MonoOSEvent *event)
75 {
76         g_assert (mono_lazy_is_initialized (&status));
77
78         g_assert (event);
79
80         mono_os_mutex_lock (&event->mutex);
81
82         if (event->manual) {
83                 mono_os_event_signal (event, TRUE);
84         } else {
85                 event->set_count = 1;
86                 mono_os_event_signal (event, FALSE);
87         }
88
89         mono_os_mutex_unlock (&event->mutex);
90 }
91
92 void
93 mono_os_event_reset (MonoOSEvent *event)
94 {
95         g_assert (mono_lazy_is_initialized (&status));
96
97         g_assert (event);
98
99         mono_os_mutex_lock (&event->mutex);
100
101         if (event->signalled)
102                 event->signalled = FALSE;
103
104         event->set_count = 0;
105
106         mono_os_mutex_unlock (&event->mutex);
107 }
108
109 static gboolean
110 mono_os_event_own (MonoOSEvent *event)
111 {
112         g_assert (event);
113
114         if (!event->signalled)
115                 return FALSE;
116
117         if (!event->manual) {
118                 g_assert (event->set_count > 0);
119                 event->set_count -= 1;
120
121                 if (event->set_count == 0)
122                         mono_os_event_signal (event, FALSE);
123         }
124
125         return TRUE;
126 }
127
128 MonoOSEventWaitRet
129 mono_os_event_wait_one (MonoOSEvent *event, guint32 timeout)
130 {
131         MonoOSEventWaitRet ret;
132         gint64 start;
133
134         g_assert (mono_lazy_is_initialized (&status));
135
136         g_assert (event);
137
138         mono_os_mutex_lock (&event->mutex);
139
140         if (timeout != MONO_INFINITE_WAIT)
141                 start = mono_msec_ticks ();
142
143         for (;;) {
144                 if (mono_os_event_own (event)) {
145                         ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0;
146                         goto done;
147                 }
148
149                 if (timeout == MONO_INFINITE_WAIT) {
150                         mono_os_cond_wait (&event->cond, &event->mutex);
151                 } else {
152                         gint64 elapsed;
153                         gint res;
154
155                         elapsed = mono_msec_ticks () - start;
156                         if (elapsed >= timeout) {
157                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
158                                 goto done;
159                         }
160
161                         res = mono_os_cond_timedwait (&event->cond, &event->mutex, timeout - elapsed);
162                         if (res != 0) {
163                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
164                                 goto done;
165                         }
166                 }
167         }
168
169 done:
170         mono_os_mutex_unlock (&event->mutex);
171
172         return ret;
173 }
174
175 static void
176 mono_os_event_lock_events (MonoOSEvent **events, gsize nevents)
177 {
178         gint i, j;
179
180 retry:
181         for (i = 0; i < nevents; ++i) {
182                 gint res;
183
184                 res = mono_os_mutex_trylock (&events [i]->mutex);
185                 if (res != 0) {
186                         for (j = i - 1; j >= 0; j--)
187                                 mono_os_mutex_unlock (&events [j]->mutex);
188
189                         mono_thread_info_yield ();
190
191                         goto retry;
192                 }
193         }
194 }
195
196 static void
197 mono_os_event_unlock_events (MonoOSEvent **events, gsize nevents)
198 {
199         gint i;
200
201         for (i = 0; i < nevents; ++i)
202                 mono_os_mutex_unlock (&events [i]->mutex);
203 }
204
205 MonoOSEventWaitRet
206 mono_os_event_wait_multiple (MonoOSEvent **events, gsize nevents, gboolean waitall, guint32 timeout)
207 {
208         MonoOSEventWaitRet ret;
209         gint64 start;
210         gint i;
211
212         g_assert (mono_lazy_is_initialized (&status));
213
214         g_assert (events);
215         g_assert (nevents > 0);
216         g_assert (nevents <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
217
218         if (nevents == 1)
219                 return mono_os_event_wait_one (events [0], timeout);
220
221         for (i = 0; i < nevents; ++i) {
222                 g_assert (events [i]);
223         }
224
225         if (timeout != MONO_INFINITE_WAIT)
226                 start = mono_msec_ticks ();
227
228         for (;;) {
229                 gint count, lowest;
230                 gboolean signalled;
231
232                 mono_os_event_lock_events (events, nevents);
233
234                 count = 0;
235                 lowest = -1;
236
237                 for (i = 0; i < nevents; ++i) {
238                         if (events [i]->signalled) {
239                                 count += 1;
240                                 if (lowest == -1)
241                                         lowest = i;
242                         }
243                 }
244
245                 signalled = (waitall && count == nevents) || (!waitall && count > 0);
246
247                 if (signalled) {
248                         for (i = 0; i < nevents; ++i)
249                                 mono_os_event_own (events [i]);
250                 }
251
252                 mono_os_event_unlock_events (events, nevents);
253
254                 if (signalled) {
255                         ret = MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + lowest;
256                         goto done;
257                 }
258
259                 mono_os_mutex_lock (&signal_mutex);
260
261                 if (waitall) {
262                         signalled = TRUE;
263                         for (i = 0; i < nevents; ++i) {
264                                 if (!events [i]->signalled) {
265                                         signalled = FALSE;
266                                         break;
267                                 }
268                         }
269                 } else {
270                         signalled = FALSE;
271                         for (i = 0; i < nevents; ++i) {
272                                 if (events [i]->signalled) {
273                                         signalled = TRUE;
274                                         break;
275                                 }
276                         }
277                 }
278
279                 if (signalled) {
280                         mono_os_mutex_unlock (&signal_mutex);
281                         continue;
282                 }
283
284                 if (timeout == MONO_INFINITE_WAIT) {
285                         mono_os_cond_wait (&signal_cond, &signal_mutex);
286                 } else {
287                         gint64 elapsed;
288                         gint res;
289
290                         elapsed = mono_msec_ticks () - start;
291                         if (elapsed >= timeout) {
292                                 mono_os_mutex_unlock (&signal_mutex);
293
294                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
295                                 goto done;
296                         }
297
298                         res = mono_os_cond_timedwait (&signal_cond, &signal_mutex, timeout - elapsed);
299                         if (res != 0) {
300                                 mono_os_mutex_unlock (&signal_mutex);
301
302                                 ret = MONO_OS_EVENT_WAIT_RET_TIMEOUT;
303                                 goto done;
304                         }
305                 }
306
307                 mono_os_mutex_unlock (&signal_mutex);
308         }
309
310 done:
311         return ret;
312 }