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