[utils] Move mutex and semaphore to mono_os_*
[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 G_BEGIN_DECLS
44
45 typedef enum {
46         MONO_SEM_FLAGS_NONE      = 0,
47         MONO_SEM_FLAGS_ALERTABLE = 1 << 0,
48 } MonoSemFlags;
49
50 #if defined(USE_MACH_SEMA)
51
52 typedef semaphore_t MonoSemType;
53
54 static inline int
55 mono_os_sem_init (MonoSemType *sem, int value)
56 {
57         return semaphore_create (current_task (), sem, SYNC_POLICY_FIFO, value) != KERN_SUCCESS ? -1 : 0;
58 }
59
60 static inline int
61 mono_os_sem_destroy (MonoSemType *sem)
62 {
63         return semaphore_destroy (current_task (), *sem) != KERN_SUCCESS ? -1 : 0;
64 }
65
66 static inline int
67 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
68 {
69         int res;
70
71 retry:
72         res = semaphore_wait (*sem);
73         g_assert (res != KERN_INVALID_ARGUMENT);
74
75         if (res == KERN_ABORTED && !(flags & MONO_SEM_FLAGS_ALERTABLE))
76                 goto retry;
77
78         return res != KERN_SUCCESS ? -1 : 0;
79 }
80
81 static inline int
82 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
83 {
84         mach_timespec_t ts, copy;
85         struct timeval start, current;
86         int res = 0;
87
88         if (timeout_ms == (guint32) 0xFFFFFFFF)
89                 return mono_os_sem_wait (sem, flags);
90
91         ts.tv_sec = timeout_ms / 1000;
92         ts.tv_nsec = (timeout_ms % 1000) * 1000000;
93         while (ts.tv_nsec >= NSEC_PER_SEC) {
94                 ts.tv_nsec -= NSEC_PER_SEC;
95                 ts.tv_sec++;
96         }
97
98         copy = ts;
99         gettimeofday (&start, NULL);
100
101 retry:
102         res = semaphore_timedwait (*sem, ts);
103         g_assert (res != KERN_INVALID_ARGUMENT);
104
105         if (res == KERN_ABORTED && !(flags & MONO_SEM_FLAGS_ALERTABLE)) {
106                 ts = copy;
107
108                 gettimeofday (&current, NULL);
109                 ts.tv_sec -= (current.tv_sec - start.tv_sec);
110                 ts.tv_nsec -= (current.tv_usec - start.tv_usec) * 1000;
111                 if (ts.tv_nsec < 0) {
112                         if (ts.tv_sec <= 0) {
113                                 ts.tv_nsec = 0;
114                         } else {
115                                 ts.tv_sec--;
116                                 ts.tv_nsec += NSEC_PER_SEC;
117                         }
118                 }
119                 if (ts.tv_sec < 0) {
120                         ts.tv_sec = 0;
121                         ts.tv_nsec = 0;
122                 }
123
124                 goto retry;
125         }
126
127         return res != KERN_SUCCESS ? -1 : 0;
128 }
129
130 static inline int
131 mono_os_sem_post (MonoSemType *sem)
132 {
133         int res;
134
135         res = semaphore_signal (*sem);
136         g_assert (res != KERN_INVALID_ARGUMENT);
137
138         return res != KERN_SUCCESS ? -1 : 0;
139 }
140
141 #elif !defined(HOST_WIN32) && defined(HAVE_SEMAPHORE_H)
142
143 typedef sem_t MonoSemType;
144
145 static inline int
146 mono_os_sem_init (MonoSemType *sem, int value)
147 {
148         return sem_init (sem, 0, value);
149 }
150
151 static inline int
152 mono_os_sem_destroy (MonoSemType *sem)
153 {
154         return sem_destroy (sem);
155 }
156
157 static inline int
158 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
159 {
160         int res;
161
162 retry:
163         res = sem_wait (sem);
164         if (res == -1)
165                 g_assert (errno != EINVAL);
166
167         if (res == -1 && errno == EINTR && !(flags & MONO_SEM_FLAGS_ALERTABLE))
168                 goto retry;
169
170         return res != 0 ? -1 : 0;
171 }
172
173 static inline int
174 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
175 {
176         struct timespec ts, copy;
177         struct timeval t;
178         int res = 0;
179
180         if (timeout_ms == 0) {
181                 res = sem_trywait (sem) != 0 ? -1 : 0;
182                 if (res == -1)
183                         g_assert (errno != EINVAL);
184
185                 return res != 0 ? -1 : 0;
186         }
187
188         if (timeout_ms == (guint32) 0xFFFFFFFF)
189                 return mono_os_sem_wait (sem, flags);
190
191         gettimeofday (&t, NULL);
192         ts.tv_sec = timeout_ms / 1000 + t.tv_sec;
193         ts.tv_nsec = (timeout_ms % 1000) * 1000000 + t.tv_usec * 1000;
194         while (ts.tv_nsec >= NSEC_PER_SEC) {
195                 ts.tv_nsec -= NSEC_PER_SEC;
196                 ts.tv_sec++;
197         }
198
199         copy = ts;
200
201 retry:
202 #if defined(__native_client__) && defined(USE_NEWLIB)
203         res = sem_trywait (sem);
204 #else
205         res = sem_timedwait (sem, &ts);
206 #endif
207         if (res == -1)
208                 g_assert (errno != EINVAL);
209
210         if (res == -1 && errno == EINTR && !(flags & MONO_SEM_FLAGS_ALERTABLE)) {
211                 ts = copy;
212                 goto retry;
213         }
214
215         return res != 0 ? -1 : 0;
216 }
217
218 static inline int
219 mono_os_sem_post (MonoSemType *sem)
220 {
221         int res;
222
223         res = sem_post (sem);
224         if (res == -1)
225                 g_assert (errno != EINVAL);
226
227         return res;
228 }
229
230 #else
231
232 typedef HANDLE MonoSemType;
233
234 static inline int
235 mono_os_sem_init (MonoSemType *sem, int value)
236 {
237         *sem = CreateSemaphore (NULL, value, 0x7FFFFFFF, NULL);
238         return *sem == NULL ? -1 : 0;
239 }
240
241 static inline int
242 mono_os_sem_destroy (MonoSemType *sem)
243 {
244         return !CloseHandle (*sem) ? -1 : 0;
245 }
246
247 static inline int
248 mono_os_sem_wait (MonoSemType *sem, MonoSemFlags flags)
249 {
250         return mono_os_sem_timedwait (sem, INFINITE, flags);
251 }
252
253 static inline int
254 mono_os_sem_timedwait (MonoSemType *sem, guint32 timeout_ms, MonoSemFlags flags)
255 {
256         gboolean res;
257
258 retry:
259         res = WaitForSingleObjectEx (*sem, timeout_ms, flags & MONO_SEM_FLAGS_ALERTABLE);
260
261         if (res == WAIT_IO_COMPLETION && !(flags & MONO_SEM_FLAGS_ALERTABLE))
262                 goto retry;
263
264         return res != WAIT_OBJECT_0 ? -1 : 0;
265 }
266
267 static inline int
268 mono_os_sem_post (MonoSemType *sem)
269 {
270         return !ReleaseSemaphore (*sem, 1, NULL) ? -1 : 0;
271 }
272
273 #endif
274
275 G_END_DECLS
276
277 #endif /* _MONO_SEMAPHORE_H_ */