Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / utils / mono-os-semaphore.h
1 /**
2  * \file
3  * Definitions for generic semaphore usage
4  *
5  * Author:
6  *      Geoff Norton  <gnorton@novell.com>
7  *
8  * (C) 2009 Novell, Inc.
9  */
10
11 #ifndef _MONO_SEMAPHORE_H_
12 #define _MONO_SEMAPHORE_H_
13
14 #include <config.h>
15 #include <glib.h>
16
17 #include <errno.h>
18
19 #ifdef HAVE_SYS_TIME_H
20 #include <sys/time.h>
21 #endif
22
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26
27 #if defined(USE_MACH_SEMA)
28 #include <mach/mach_init.h>
29 #include <mach/task.h>
30 #include <mach/semaphore.h>
31 #elif !defined(HOST_WIN32) && defined(HAVE_SEMAPHORE_H)
32 #include <semaphore.h>
33 #else
34 #include <mono/utils/mono-os-wait.h>
35 #endif
36
37 #define MONO_HAS_SEMAPHORES 1
38
39 #ifndef NSEC_PER_SEC
40 #define NSEC_PER_SEC (1000 * 1000 * 1000)
41 #endif
42
43 #ifndef MONO_INFINITE_WAIT
44 #define MONO_INFINITE_WAIT ((guint32) 0xFFFFFFFF)
45 #endif
46
47 G_BEGIN_DECLS
48
49 typedef enum {
50         MONO_SEM_FLAGS_NONE      = 0,
51         MONO_SEM_FLAGS_ALERTABLE = 1 << 0,
52 } MonoSemFlags;
53
54 typedef enum {
55         MONO_SEM_TIMEDWAIT_RET_SUCCESS  =  0,
56         MONO_SEM_TIMEDWAIT_RET_ALERTED  = -1,
57         MONO_SEM_TIMEDWAIT_RET_TIMEDOUT = -2,
58 } MonoSemTimedwaitRet;
59
60 #if defined(USE_MACH_SEMA)
61
62 typedef semaphore_t MonoSemType;
63
64 static inline void
65 mono_os_sem_init (MonoSemType *sem, int value)
66 {
67         kern_return_t res;
68
69         res = semaphore_create (current_task (), sem, SYNC_POLICY_FIFO, value);
70         if (G_UNLIKELY (res != KERN_SUCCESS))
71                 g_error ("%s: semaphore_create failed with error %d", __func__, res);
72 }
73
74 static inline void
75 mono_os_sem_destroy (MonoSemType *sem)
76 {
77         kern_return_t res;
78
79         res = semaphore_destroy (current_task (), *sem);
80         if (G_UNLIKELY (res != KERN_SUCCESS))
81                 g_error ("%s: semaphore_destroy failed with error %d", __func__, res);
82 }
83
84 static inline int
85 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
86 {
87         kern_return_t res;
88
89 retry:
90         res = semaphore_wait (*sem);
91         if (G_UNLIKELY (res != KERN_SUCCESS && res != KERN_ABORTED))
92                 g_error ("%s: semaphore_wait failed with error %d", __func__, res);
93
94         if (res == KERN_ABORTED && !(flags & MONO_SEM_FLAGS_ALERTABLE))
95                 goto retry;
96
97         return res != KERN_SUCCESS ? -1 : 0;
98 }
99
100 static inline MonoSemTimedwaitRet
101 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
102 {
103         kern_return_t res;
104         int resint;
105         mach_timespec_t ts, copy;
106         struct timeval start, current;
107
108         if (timeout_ms == MONO_INFINITE_WAIT)
109                 return (MonoSemTimedwaitRet) mono_os_sem_wait (sem, flags);
110
111         ts.tv_sec = timeout_ms / 1000;
112         ts.tv_nsec = (timeout_ms % 1000) * 1000000;
113         while (ts.tv_nsec >= NSEC_PER_SEC) {
114                 ts.tv_nsec -= NSEC_PER_SEC;
115                 ts.tv_sec++;
116         }
117
118         copy = ts;
119         resint = gettimeofday (&start, NULL);
120         if (G_UNLIKELY (resint != 0))
121                 g_error ("%s: gettimeofday failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
122
123 retry:
124         res = semaphore_timedwait (*sem, ts);
125         if (G_UNLIKELY (res != KERN_SUCCESS && res != KERN_ABORTED && res != KERN_OPERATION_TIMED_OUT))
126                 g_error ("%s: semaphore_timedwait failed with error %d", __func__, res);
127
128         if (res == KERN_ABORTED && !(flags & MONO_SEM_FLAGS_ALERTABLE)) {
129                 ts = copy;
130
131                 resint = gettimeofday (&current, NULL);
132                 if (G_UNLIKELY (resint != 0))
133                         g_error ("%s: gettimeofday failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
134
135                 ts.tv_sec -= (current.tv_sec - start.tv_sec);
136                 ts.tv_nsec -= (current.tv_usec - start.tv_usec) * 1000;
137                 if (ts.tv_nsec < 0) {
138                         if (ts.tv_sec <= 0) {
139                                 ts.tv_nsec = 0;
140                         } else {
141                                 ts.tv_sec--;
142                                 ts.tv_nsec += NSEC_PER_SEC;
143                         }
144                 }
145                 if (ts.tv_sec < 0) {
146                         ts.tv_sec = 0;
147                         ts.tv_nsec = 0;
148                 }
149
150                 goto retry;
151         }
152
153         switch (res) {
154         case KERN_SUCCESS:
155                 return MONO_SEM_TIMEDWAIT_RET_SUCCESS;
156         case KERN_ABORTED:
157                 return MONO_SEM_TIMEDWAIT_RET_ALERTED;
158         case KERN_OPERATION_TIMED_OUT:
159                 return MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
160         default:
161                 g_assert_not_reached ();
162         }
163 }
164
165 static inline void
166 mono_os_sem_post (MonoSemType *sem)
167 {
168         kern_return_t res;
169
170 retry:
171         res = semaphore_signal (*sem);
172         if (G_UNLIKELY (res != KERN_SUCCESS && res != KERN_ABORTED))
173                 g_error ("%s: semaphore_signal failed with error %d", __func__, res);
174
175         if (res == KERN_ABORTED)
176                 goto retry;
177 }
178
179 #elif !defined(HOST_WIN32) && defined(HAVE_SEMAPHORE_H)
180
181 typedef sem_t MonoSemType;
182
183 static inline void
184 mono_os_sem_init (MonoSemType *sem, int value)
185 {
186         int res;
187
188         res = sem_init (sem, 0, value);
189         if (G_UNLIKELY (res != 0))
190                 g_error ("%s: sem_init failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
191 }
192
193 static inline void
194 mono_os_sem_destroy (MonoSemType *sem)
195 {
196         int res;
197
198         res = sem_destroy (sem);
199         if (G_UNLIKELY (res != 0))
200                 g_error ("%s: sem_destroy failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
201 }
202
203 static inline int
204 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
205 {
206         int res;
207
208 retry:
209         res = sem_wait (sem);
210         if (G_UNLIKELY (res != 0 && errno != EINTR))
211                 g_error ("%s: sem_wait failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
212
213         if (res != 0 && errno == EINTR && !(flags & MONO_SEM_FLAGS_ALERTABLE))
214                 goto retry;
215
216         return res != 0 ? -1 : 0;
217 }
218
219 static inline MonoSemTimedwaitRet
220 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
221 {
222         struct timespec ts, copy;
223         struct timeval t;
224         int res;
225
226         if (timeout_ms == 0) {
227                 res = sem_trywait (sem);
228                 if (G_UNLIKELY (res != 0 && errno != EINTR && errno != EAGAIN))
229                         g_error ("%s: sem_trywait failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
230
231                 if (res == 0)
232                         return MONO_SEM_TIMEDWAIT_RET_SUCCESS;
233                 else if (errno == EINTR)
234                         return MONO_SEM_TIMEDWAIT_RET_ALERTED;
235                 else if (errno == EAGAIN)
236                         return MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
237                 else
238                         g_assert_not_reached ();
239         }
240
241         if (timeout_ms == MONO_INFINITE_WAIT)
242                 return (MonoSemTimedwaitRet) mono_os_sem_wait (sem, flags);
243
244         res = gettimeofday (&t, NULL);
245         if (G_UNLIKELY (res != 0))
246                 g_error ("%s: gettimeofday failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
247
248         ts.tv_sec = timeout_ms / 1000 + t.tv_sec;
249         ts.tv_nsec = (timeout_ms % 1000) * 1000000 + t.tv_usec * 1000;
250         while (ts.tv_nsec >= NSEC_PER_SEC) {
251                 ts.tv_nsec -= NSEC_PER_SEC;
252                 ts.tv_sec++;
253         }
254
255         copy = ts;
256
257 retry:
258         res = sem_timedwait (sem, &ts);
259         if (G_UNLIKELY (res != 0 && errno != EINTR && errno != ETIMEDOUT))
260                 g_error ("%s: sem_timedwait failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
261
262         if (res != 0 && errno == EINTR && !(flags & MONO_SEM_FLAGS_ALERTABLE)) {
263                 ts = copy;
264                 goto retry;
265         }
266
267         if (res == 0)
268                 return MONO_SEM_TIMEDWAIT_RET_SUCCESS;
269         else if (errno == EINTR)
270                 return MONO_SEM_TIMEDWAIT_RET_ALERTED;
271         else if (errno == ETIMEDOUT)
272                 return MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
273         else
274                 g_assert_not_reached ();
275 }
276
277 static inline void
278 mono_os_sem_post (MonoSemType *sem)
279 {
280         int res;
281
282         res = sem_post (sem);
283         if (G_UNLIKELY (res != 0))
284                 g_error ("%s: sem_post failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
285 }
286
287 #else
288
289 typedef HANDLE MonoSemType;
290
291 static inline void
292 mono_os_sem_init (MonoSemType *sem, int value)
293 {
294 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
295         *sem = CreateSemaphore (NULL, value, 0x7FFFFFFF, NULL);
296 #else
297         *sem = CreateSemaphoreEx (NULL, value, 0x7FFFFFFF, NULL, 0, SEMAPHORE_ALL_ACCESS);
298 #endif
299
300         if (G_UNLIKELY (*sem == NULL))
301                 g_error ("%s: CreateSemaphore failed with error %d", __func__, GetLastError ());
302 }
303
304 static inline void
305 mono_os_sem_destroy (MonoSemType *sem)
306 {
307         BOOL res;
308
309         res = CloseHandle (*sem);
310         if (G_UNLIKELY (res == 0))
311                 g_error ("%s: CloseHandle failed with error %d", __func__, GetLastError ());
312 }
313
314 static inline MonoSemTimedwaitRet
315 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
316 {
317         BOOL res;
318
319 retry:
320         res = mono_win32_wait_for_single_object_ex (*sem, timeout_ms, flags & MONO_SEM_FLAGS_ALERTABLE);
321         if (G_UNLIKELY (res != WAIT_OBJECT_0 && res != WAIT_IO_COMPLETION && res != WAIT_TIMEOUT))
322                 g_error ("%s: mono_win32_wait_for_single_object_ex failed with error %d", __func__, GetLastError ());
323
324         if (res == WAIT_IO_COMPLETION && !(flags & MONO_SEM_FLAGS_ALERTABLE))
325                 goto retry;
326
327         switch (res) {
328         case WAIT_OBJECT_0:
329                 return MONO_SEM_TIMEDWAIT_RET_SUCCESS;
330         case WAIT_IO_COMPLETION:
331                 return MONO_SEM_TIMEDWAIT_RET_ALERTED;
332         case WAIT_TIMEOUT:
333                 return MONO_SEM_TIMEDWAIT_RET_TIMEDOUT;
334         default:
335                 g_assert_not_reached ();
336         }
337 }
338
339 static inline int
340 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
341 {
342         return mono_os_sem_timedwait (sem, MONO_INFINITE_WAIT, flags) != 0 ? -1 : 0;
343 }
344
345 static inline void
346 mono_os_sem_post (MonoSemType *sem)
347 {
348         BOOL res;
349
350         res = ReleaseSemaphore (*sem, 1, NULL);
351         if (G_UNLIKELY (res == 0))
352                 g_error ("%s: ReleaseSemaphore failed with error %d", __func__, GetLastError ());
353 }
354
355 #endif
356
357 G_END_DECLS
358
359 #endif /* _MONO_SEMAPHORE_H_ */