86b6926dc3ec8de59f36cce2b10901114cd4ef19
[mono.git] / mono / utils / mono-os-mutex.h
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /**
3  * \file
4  * Portability wrappers around POSIX Mutexes
5  *
6  * Authors: Jeffrey Stedfast <fejj@ximian.com>
7  *
8  * Copyright 2002 Ximian, Inc. (www.ximian.com)
9  *
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12
13 #ifndef __MONO_OS_MUTEX_H__
14 #define __MONO_OS_MUTEX_H__
15
16 #include <config.h>
17 #include <glib.h>
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22
23 #if !defined(HOST_WIN32)
24 #include <pthread.h>
25 #include <errno.h>
26 #else
27 #include <winsock2.h>
28 #include <windows.h>
29 #endif
30
31 #ifdef HAVE_SYS_TIME_H
32 #include <sys/time.h>
33 #endif
34
35 G_BEGIN_DECLS
36
37 #ifndef MONO_INFINITE_WAIT
38 #define MONO_INFINITE_WAIT ((guint32) 0xFFFFFFFF)
39 #endif
40
41
42 #if !defined(HOST_WIN32)
43
44 typedef pthread_mutex_t mono_mutex_t;
45 typedef pthread_cond_t mono_cond_t;
46
47 static inline void
48 mono_os_mutex_init (mono_mutex_t *mutex)
49 {
50         int res;
51
52         res = pthread_mutex_init (mutex, NULL);
53         if (G_UNLIKELY (res != 0))
54                 g_error ("%s: pthread_mutex_init failed with \"%s\" (%d)", __func__, g_strerror (res), res);
55 }
56
57 static inline void
58 mono_os_mutex_init_recursive (mono_mutex_t *mutex)
59 {
60         int res;
61         pthread_mutexattr_t attr;
62
63         res = pthread_mutexattr_init (&attr);
64         if (G_UNLIKELY (res != 0))
65                 g_error ("%s: pthread_mutexattr_init failed with \"%s\" (%d)", __func__, g_strerror (res), res);
66
67         res = pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
68         if (G_UNLIKELY (res != 0))
69                 g_error ("%s: pthread_mutexattr_settype failed with \"%s\" (%d)", __func__, g_strerror (res), res);
70
71         res = pthread_mutex_init (mutex, &attr);
72         if (G_UNLIKELY (res != 0))
73                 g_error ("%s: pthread_mutex_init failed with \"%s\" (%d)", __func__, g_strerror (res), res);
74
75         res = pthread_mutexattr_destroy (&attr);
76         if (G_UNLIKELY (res != 0))
77                 g_error ("%s: pthread_mutexattr_destroy failed with \"%s\" (%d)", __func__, g_strerror (res), res);
78 }
79
80 static inline void
81 mono_os_mutex_destroy (mono_mutex_t *mutex)
82 {
83         int res;
84
85         res = pthread_mutex_destroy (mutex);
86         if (G_UNLIKELY (res != 0))
87                 g_error ("%s: pthread_mutex_destroy failed with \"%s\" (%d)", __func__, g_strerror (res), res);
88 }
89
90 static inline void
91 mono_os_mutex_lock (mono_mutex_t *mutex)
92 {
93         int res;
94
95         res = pthread_mutex_lock (mutex);
96         if (G_UNLIKELY (res != 0))
97                 g_error ("%s: pthread_mutex_lock failed with \"%s\" (%d)", __func__, g_strerror (res), res);
98 }
99
100 static inline int
101 mono_os_mutex_trylock (mono_mutex_t *mutex)
102 {
103         int res;
104
105         res = pthread_mutex_trylock (mutex);
106         if (G_UNLIKELY (res != 0 && res != EBUSY))
107                 g_error ("%s: pthread_mutex_trylock failed with \"%s\" (%d)", __func__, g_strerror (res), res);
108
109         return res != 0 ? -1 : 0;
110 }
111
112 static inline void
113 mono_os_mutex_unlock (mono_mutex_t *mutex)
114 {
115         int res;
116
117         res = pthread_mutex_unlock (mutex);
118         if (G_UNLIKELY (res != 0))
119                 g_error ("%s: pthread_mutex_unlock failed with \"%s\" (%d)", __func__, g_strerror (res), res);
120 }
121
122 static inline void
123 mono_os_cond_init (mono_cond_t *cond)
124 {
125         int res;
126
127 #if !defined(CLOCK_MONOTONIC) || defined(PLATFORM_MACOSX)
128         res = pthread_cond_init (cond, NULL);
129         if (G_UNLIKELY (res != 0))
130                 g_error ("%s: pthread_cond_init failed with \"%s\" (%d)", __func__, g_strerror (res), res);
131 #else
132         /* POSIX standard does not compel to have CLOCK_MONOTONIC */
133         pthread_condattr_t attr;
134
135         res = pthread_condattr_init (&attr);
136         if (G_UNLIKELY (res != 0))
137                 g_error ("%s: pthread_condattr_init failed with \"%s\" (%d)", __func__, g_strerror (res), res);
138
139         res = pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
140         if (G_UNLIKELY (res != 0))
141                 g_error ("%s: pthread_condattr_setclock failed with \"%s\" (%d)", __func__, g_strerror (res), res);
142
143         /* Attach an attribute having CLOCK_MONOTONIC to condition */
144         res = pthread_cond_init (cond, &attr);
145         if (G_UNLIKELY (res != 0))
146                 g_error ("%s: pthread_cond_init failed with \"%s\" (%d)", __func__, g_strerror (res), res);
147
148         res = pthread_condattr_destroy (&attr);
149         if (G_UNLIKELY (res != 0))
150                 g_error ("%s: pthread_condattr_destroy failed with \"%s\" (%d)", __func__, g_strerror (res), res);
151 #endif
152 }
153
154 static inline void
155 mono_os_cond_destroy (mono_cond_t *cond)
156 {
157         int res;
158
159         res = pthread_cond_destroy (cond);
160         if (G_UNLIKELY (res != 0))
161                 g_error ("%s: pthread_cond_destroy failed with \"%s\" (%d)", __func__, g_strerror (res), res);
162 }
163
164 static inline void
165 mono_os_cond_wait (mono_cond_t *cond, mono_mutex_t *mutex)
166 {
167         int res;
168
169         res = pthread_cond_wait (cond, mutex);
170         if (G_UNLIKELY (res != 0))
171                 g_error ("%s: pthread_cond_wait failed with \"%s\" (%d)", __func__, g_strerror (res), res);
172 }
173
174 static inline int
175 mono_os_cond_timedwait (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout_ms)
176 {
177 #if !defined(CLOCK_MONOTONIC) || defined(PLATFORM_MACOSX)
178         struct timeval tv;
179 #endif
180         struct timespec ts;
181         int res;
182
183         if (timeout_ms == MONO_INFINITE_WAIT) {
184                 mono_os_cond_wait (cond, mutex);
185                 return 0;
186         }
187
188         /* ms = 10^-3, us = 10^-6, ns = 10^-9 */
189
190 #if !defined(CLOCK_MONOTONIC) || defined(PLATFORM_MACOSX)
191         /* clock_gettime is not supported in MAC OS x */
192         res = gettimeofday (&tv, NULL);
193         if (G_UNLIKELY (res != 0))
194                 g_error ("%s: gettimeofday failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
195
196         ts.tv_sec = tv.tv_sec;
197         ts.tv_nsec = tv.tv_usec * 1000;
198 #else
199         /* cond is using CLOCK_MONOTONIC as time source */
200         res = clock_gettime (CLOCK_MONOTONIC, &ts);
201         if (G_UNLIKELY (res != 0))
202                 g_error ("%s: clock_gettime failed with \"%s\" (%d)", __func__, g_strerror (errno), errno);
203 #endif
204
205         ts.tv_sec += timeout_ms / 1000;
206         ts.tv_nsec += (timeout_ms % 1000) * 1000 * 1000;
207         if (ts.tv_nsec >= 1000 * 1000 * 1000) {
208                 ts.tv_nsec -= 1000 * 1000 * 1000;
209                 ts.tv_sec ++;
210         }
211
212         res = pthread_cond_timedwait (cond, mutex, &ts);
213         if (G_UNLIKELY (res != 0 && res != ETIMEDOUT))
214                 g_error ("%s: pthread_cond_timedwait failed with \"%s\" (%d)", __func__, g_strerror (res), res);
215
216         return res != 0 ? -1 : 0;
217 }
218
219 static inline void
220 mono_os_cond_signal (mono_cond_t *cond)
221 {
222         int res;
223
224         res = pthread_cond_signal (cond);
225         if (G_UNLIKELY (res != 0))
226                 g_error ("%s: pthread_cond_signal failed with \"%s\" (%d)", __func__, g_strerror (res), res);
227 }
228
229 static inline void
230 mono_os_cond_broadcast (mono_cond_t *cond)
231 {
232         int res;
233
234         res = pthread_cond_broadcast (cond);
235         if (G_UNLIKELY (res != 0))
236                 g_error ("%s: pthread_cond_broadcast failed with \"%s\" (%d)", __func__, g_strerror (res), res);
237 }
238
239 #else
240
241 /* Vanilla MinGW is missing some defs, load them from MinGW-w64. */
242 #if defined __MINGW32__ && !defined __MINGW64_VERSION_MAJOR && (_WIN32_WINNT >= 0x0600)
243
244 /* Fixme: Opaque structs */
245 typedef PVOID RTL_CONDITION_VARIABLE;
246 typedef PVOID RTL_SRWLOCK;
247
248 #ifndef _RTL_RUN_ONCE_DEF
249 #define _RTL_RUN_ONCE_DEF 1
250 typedef PVOID RTL_RUN_ONCE, *PRTL_RUN_ONCE;
251 typedef DWORD (WINAPI *PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID *);
252 #define RTL_RUN_ONCE_INIT 0
253 #define RTL_RUN_ONCE_CHECK_ONLY 1UL
254 #define RTL_RUN_ONCE_ASYNC 2UL
255 #define RTL_RUN_ONCE_INIT_FAILED 4UL
256 #define RTL_RUN_ONCE_CTX_RESERVED_BITS 2
257 #endif /* _RTL_RUN_ONCE_DEF */
258 #define RTL_SRWLOCK_INIT 0
259 #define RTL_CONDITION_VARIABLE_INIT 0
260 #define RTL_CONDITION_VARIABLE_LOCKMODE_SHARED 1
261
262 #define CONDITION_VARIABLE_INIT RTL_CONDITION_VARIABLE_INIT
263 #define CONDITION_VARIABLE_LOCKMODE_SHARED RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
264 #define SRWLOCK_INIT RTL_SRWLOCK_INIT
265
266 /*Condition Variables http://msdn.microsoft.com/en-us/library/ms682052%28VS.85%29.aspx*/
267 typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE;
268 typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
269
270 WINBASEAPI VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
271 WINBASEAPI WINBOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds);
272 WINBASEAPI WINBOOL WINAPI SleepConditionVariableSRW(PCONDITION_VARIABLE ConditionVariable, PSRWLOCK SRWLock, DWORD dwMilliseconds, ULONG Flags);
273 WINBASEAPI VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable);
274 WINBASEAPI VOID WINAPI WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
275
276 /*Slim Reader/Writer (SRW) Locks http://msdn.microsoft.com/en-us/library/aa904937%28VS.85%29.aspx*/
277 WINBASEAPI VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock);
278 WINBASEAPI VOID WINAPI AcquireSRWLockShared(PSRWLOCK SRWLock);
279 WINBASEAPI VOID WINAPI InitializeSRWLock(PSRWLOCK SRWLock);
280 WINBASEAPI VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
281 WINBASEAPI VOID WINAPI ReleaseSRWLockShared(PSRWLOCK SRWLock);
282
283 WINBASEAPI BOOLEAN TryAcquireSRWLockExclusive(PSRWLOCK SRWLock);
284 WINBASEAPI BOOLEAN TryAcquireSRWLockShared(PSRWLOCK SRWLock);
285
286 /*One-Time Initialization http://msdn.microsoft.com/en-us/library/aa363808(VS.85).aspx*/
287 #define INIT_ONCE_ASYNC 0x00000002UL
288 #define INIT_ONCE_INIT_FAILED 0x00000004UL
289
290 typedef PRTL_RUN_ONCE PINIT_ONCE;
291 typedef PRTL_RUN_ONCE LPINIT_ONCE;
292 typedef WINBOOL CALLBACK (*PINIT_ONCE_FN) (PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context);
293
294 WINBASEAPI WINBOOL WINAPI InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID *lpContext);
295 WINBASEAPI WINBOOL WINAPI InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext);
296 WINBASEAPI WINBOOL WINAPI InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID *Context);
297
298 /* https://msdn.microsoft.com/en-us/library/windows/desktop/ms683477(v=vs.85).aspx */
299 WINBASEAPI BOOL WINAPI InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
300
301 #define CRITICAL_SECTION_NO_DEBUG_INFO 0x01000000
302
303 #endif /* defined __MINGW32__ && !defined __MINGW64_VERSION_MAJOR && (_WIN32_WINNT >= 0x0600) */
304
305 typedef CRITICAL_SECTION mono_mutex_t;
306 typedef CONDITION_VARIABLE mono_cond_t;
307
308 static inline void
309 mono_os_mutex_init (mono_mutex_t *mutex)
310 {
311         BOOL res;
312
313         res = InitializeCriticalSectionEx (mutex, 0, CRITICAL_SECTION_NO_DEBUG_INFO);
314         if (G_UNLIKELY (res == 0))
315                 g_error ("%s: InitializeCriticalSectionEx failed with error %d", __func__, GetLastError ());
316 }
317
318 static inline void
319 mono_os_mutex_init_recursive (mono_mutex_t *mutex)
320 {
321         BOOL res;
322
323         res = InitializeCriticalSectionEx (mutex, 0, CRITICAL_SECTION_NO_DEBUG_INFO);
324         if (G_UNLIKELY (res == 0))
325                 g_error ("%s: InitializeCriticalSectionEx failed with error %d", __func__, GetLastError ());
326 }
327
328 static inline void
329 mono_os_mutex_destroy (mono_mutex_t *mutex)
330 {
331         DeleteCriticalSection (mutex);
332 }
333
334 static inline void
335 mono_os_mutex_lock (mono_mutex_t *mutex)
336 {
337         EnterCriticalSection (mutex);
338 }
339
340 static inline int
341 mono_os_mutex_trylock (mono_mutex_t *mutex)
342 {
343         return TryEnterCriticalSection (mutex) == 0 ? -1 : 0;
344 }
345
346 static inline void
347 mono_os_mutex_unlock (mono_mutex_t *mutex)
348 {
349         LeaveCriticalSection (mutex);
350 }
351
352 static inline void
353 mono_os_cond_init (mono_cond_t *cond)
354 {
355         InitializeConditionVariable (cond);
356 }
357
358 static inline void
359 mono_os_cond_destroy (mono_cond_t *cond)
360 {
361         /* Beauty of win32 API: do not destroy it */
362 }
363
364 static inline void
365 mono_os_cond_wait (mono_cond_t *cond, mono_mutex_t *mutex)
366 {
367         BOOL res;
368
369         res = SleepConditionVariableCS (cond, mutex, INFINITE);
370         if (G_UNLIKELY (res == 0))
371                 g_error ("%s: SleepConditionVariableCS failed with error %d", __func__, GetLastError ());
372 }
373
374 static inline int
375 mono_os_cond_timedwait (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout_ms)
376 {
377         BOOL res;
378
379         res = SleepConditionVariableCS (cond, mutex, timeout_ms);
380         if (G_UNLIKELY (res == 0 && GetLastError () != ERROR_TIMEOUT))
381                 g_error ("%s: SleepConditionVariableCS failed with error %d", __func__, GetLastError ());
382
383         return res == 0 ? -1 : 0;
384 }
385
386 static inline void
387 mono_os_cond_signal (mono_cond_t *cond)
388 {
389         WakeConditionVariable (cond);
390 }
391
392 static inline void
393 mono_os_cond_broadcast (mono_cond_t *cond)
394 {
395         WakeAllConditionVariable (cond);
396 }
397
398 #endif
399
400 G_END_DECLS
401
402 #endif /* __MONO_OS_MUTEX_H__ */