Merge pull request #3262 from lindenlab/add_continuations_test
[mono.git] / mono / utils / mono-os-semaphore.h
1 /*
2  * mono-os-semaphore.h:  Definitions for generic semaphore usage
3  *
4  * Author:
5  *      Geoff Norton  <gnorton@novell.com>
6  *
7  * (C) 2009 Novell, Inc.
8  */
9
10 #ifndef _MONO_SEMAPHORE_H_
11 #define _MONO_SEMAPHORE_H_
12
13 #include <config.h>
14 #include <glib.h>
15
16 #include <errno.h>
17
18 #ifdef HAVE_SYS_TIME_H
19 #include <sys/time.h>
20 #endif
21
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25
26 #if defined(USE_MACH_SEMA)
27 #include <mach/mach_init.h>
28 #include <mach/task.h>
29 #include <mach/semaphore.h>
30 #elif !defined(HOST_WIN32) && defined(HAVE_SEMAPHORE_H)
31 #include <semaphore.h>
32 #else
33 #include <winsock2.h>
34 #include <windows.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 #if defined(USE_MACH_SEMA)
55
56 typedef semaphore_t MonoSemType;
57
58 static inline int
59 mono_os_sem_init (MonoSemType *sem, int value)
60 {
61         return semaphore_create (current_task (), sem, SYNC_POLICY_FIFO, value) != KERN_SUCCESS ? -1 : 0;
62 }
63
64 static inline int
65 mono_os_sem_destroy (MonoSemType *sem)
66 {
67         return semaphore_destroy (current_task (), *sem) != KERN_SUCCESS ? -1 : 0;
68 }
69
70 static inline int
71 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
72 {
73         int res;
74
75 retry:
76         res = semaphore_wait (*sem);
77         g_assert (res != KERN_INVALID_ARGUMENT);
78
79         if (res == KERN_ABORTED && !(flags & MONO_SEM_FLAGS_ALERTABLE))
80                 goto retry;
81
82         return res != KERN_SUCCESS ? -1 : 0;
83 }
84
85 static inline int
86 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
87 {
88         mach_timespec_t ts, copy;
89         struct timeval start, current;
90         int res = 0;
91
92         if (timeout_ms == MONO_INFINITE_WAIT)
93                 return mono_os_sem_wait (sem, flags);
94
95         ts.tv_sec = timeout_ms / 1000;
96         ts.tv_nsec = (timeout_ms % 1000) * 1000000;
97         while (ts.tv_nsec >= NSEC_PER_SEC) {
98                 ts.tv_nsec -= NSEC_PER_SEC;
99                 ts.tv_sec++;
100         }
101
102         copy = ts;
103         gettimeofday (&start, NULL);
104
105 retry:
106         res = semaphore_timedwait (*sem, ts);
107         g_assert (res != KERN_INVALID_ARGUMENT);
108
109         if (res == KERN_ABORTED && !(flags & MONO_SEM_FLAGS_ALERTABLE)) {
110                 ts = copy;
111
112                 gettimeofday (&current, NULL);
113                 ts.tv_sec -= (current.tv_sec - start.tv_sec);
114                 ts.tv_nsec -= (current.tv_usec - start.tv_usec) * 1000;
115                 if (ts.tv_nsec < 0) {
116                         if (ts.tv_sec <= 0) {
117                                 ts.tv_nsec = 0;
118                         } else {
119                                 ts.tv_sec--;
120                                 ts.tv_nsec += NSEC_PER_SEC;
121                         }
122                 }
123                 if (ts.tv_sec < 0) {
124                         ts.tv_sec = 0;
125                         ts.tv_nsec = 0;
126                 }
127
128                 goto retry;
129         }
130
131         return res != KERN_SUCCESS ? -1 : 0;
132 }
133
134 static inline int
135 mono_os_sem_post (MonoSemType *sem)
136 {
137         int res;
138 retry:
139         res = semaphore_signal (*sem);
140         g_assert (res != KERN_INVALID_ARGUMENT);
141
142         if (res == KERN_ABORTED)
143                 goto retry;
144
145         return res != KERN_SUCCESS ? -1 : 0;
146 }
147
148 #elif !defined(HOST_WIN32) && defined(HAVE_SEMAPHORE_H)
149
150 typedef sem_t MonoSemType;
151
152 static inline int
153 mono_os_sem_init (MonoSemType *sem, int value)
154 {
155         return sem_init (sem, 0, value);
156 }
157
158 static inline int
159 mono_os_sem_destroy (MonoSemType *sem)
160 {
161         return sem_destroy (sem);
162 }
163
164 static inline int
165 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
166 {
167         int res;
168
169 retry:
170         res = sem_wait (sem);
171         if (res == -1)
172                 g_assert (errno != EINVAL);
173
174         if (res == -1 && errno == EINTR && !(flags & MONO_SEM_FLAGS_ALERTABLE))
175                 goto retry;
176
177         return res != 0 ? -1 : 0;
178 }
179
180 static inline int
181 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
182 {
183         struct timespec ts, copy;
184         struct timeval t;
185         int res = 0;
186
187         if (timeout_ms == 0) {
188                 res = sem_trywait (sem) != 0 ? -1 : 0;
189                 if (res == -1)
190                         g_assert (errno != EINVAL);
191
192                 return res != 0 ? -1 : 0;
193         }
194
195         if (timeout_ms == MONO_INFINITE_WAIT)
196                 return mono_os_sem_wait (sem, flags);
197
198         gettimeofday (&t, NULL);
199         ts.tv_sec = timeout_ms / 1000 + t.tv_sec;
200         ts.tv_nsec = (timeout_ms % 1000) * 1000000 + t.tv_usec * 1000;
201         while (ts.tv_nsec >= NSEC_PER_SEC) {
202                 ts.tv_nsec -= NSEC_PER_SEC;
203                 ts.tv_sec++;
204         }
205
206         copy = ts;
207
208 retry:
209 #if defined(__native_client__) && defined(USE_NEWLIB)
210         res = sem_trywait (sem);
211 #else
212         res = sem_timedwait (sem, &ts);
213 #endif
214         if (res == -1)
215                 g_assert (errno != EINVAL);
216
217         if (res == -1 && errno == EINTR && !(flags & MONO_SEM_FLAGS_ALERTABLE)) {
218                 ts = copy;
219                 goto retry;
220         }
221
222         return res != 0 ? -1 : 0;
223 }
224
225 static inline int
226 mono_os_sem_post (MonoSemType *sem)
227 {
228         int res;
229
230         res = sem_post (sem);
231         if (res == -1)
232                 g_assert (errno != EINVAL);
233
234         return res;
235 }
236
237 #else
238
239 typedef HANDLE MonoSemType;
240
241 static inline int
242 mono_os_sem_init (MonoSemType *sem, int value)
243 {
244         *sem = CreateSemaphore (NULL, value, 0x7FFFFFFF, NULL);
245         return *sem == NULL ? -1 : 0;
246 }
247
248 static inline int
249 mono_os_sem_destroy (MonoSemType *sem)
250 {
251         return !CloseHandle (*sem) ? -1 : 0;
252 }
253
254 static inline int
255 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
256 {
257         gboolean res;
258
259 retry:
260         res = WaitForSingleObjectEx (*sem, timeout_ms, flags & MONO_SEM_FLAGS_ALERTABLE);
261
262         if (res == WAIT_IO_COMPLETION && !(flags & MONO_SEM_FLAGS_ALERTABLE))
263                 goto retry;
264
265         return res != WAIT_OBJECT_0 ? -1 : 0;
266 }
267
268 static inline int
269 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
270 {
271         return mono_os_sem_timedwait (sem, INFINITE, flags);
272 }
273
274 static inline int
275 mono_os_sem_post (MonoSemType *sem)
276 {
277         return !ReleaseSemaphore (*sem, 1, NULL) ? -1 : 0;
278 }
279
280 #endif
281
282 G_END_DECLS
283
284 #endif /* _MONO_SEMAPHORE_H_ */