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