Merge pull request #1691 from esdrubal/exitevent
[mono.git] / mono / utils / mono-mutex.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * mono-mutex.h: Portability wrappers around POSIX Mutexes
4  *
5  * Authors: Jeffrey Stedfast <fejj@ximian.com>
6  *
7  * Copyright 2002 Ximian, Inc. (www.ximian.com)
8  */
9
10
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <assert.h>
19 #include <mono/utils/mono-memory-model.h>
20
21 #ifndef HOST_WIN32
22 #include <sys/time.h>
23 #endif
24
25 #include "mono-mutex.h"
26
27 #ifndef HOST_WIN32
28
29 #if defined(__APPLE__)
30 #define _DARWIN_C_SOURCE
31 #include <pthread_spis.h>
32 #include <dlfcn.h>
33 #endif
34
35 #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
36 /* Android does not implement pthread_mutex_timedlock(), but does provide an
37  * unusual declaration: http://code.google.com/p/android/issues/detail?id=7807
38  */
39 #ifdef PLATFORM_ANDROID
40 #define CONST_NEEDED
41 #else
42 #define CONST_NEEDED const
43 #endif
44
45 int pthread_mutex_timedlock (pthread_mutex_t *mutex,
46                             CONST_NEEDED struct timespec *timeout);
47 int
48 pthread_mutex_timedlock (pthread_mutex_t *mutex, CONST_NEEDED struct timespec *timeout)
49 {
50         struct timeval timenow;
51         struct timespec sleepytime;
52         int retcode;
53         
54         /* This is just to avoid a completely busy wait */
55         sleepytime.tv_sec = 0;
56         sleepytime.tv_nsec = 10000000;  /* 10ms */
57         
58         while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
59                 gettimeofday (&timenow, NULL);
60                 
61                 if (timenow.tv_sec >= timeout->tv_sec &&
62                     (timenow.tv_usec * 1000) >= timeout->tv_nsec) {
63                         return ETIMEDOUT;
64                 }
65                 
66                 nanosleep (&sleepytime, NULL);
67         }
68         
69         return retcode;
70 }
71 #endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */
72
73
74 int
75 mono_once (mono_once_t *once, void (*once_init) (void))
76 {
77         int thr_ret;
78         
79         if (!once->complete) {
80                 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
81                                       (void *)&once->mutex);
82                 thr_ret = pthread_mutex_lock (&once->mutex);
83                 g_assert (thr_ret == 0);
84                 
85                 if (!once->complete) {
86                         once_init ();
87                         once->complete = TRUE;
88                 }
89                 thr_ret = pthread_mutex_unlock (&once->mutex);
90                 g_assert (thr_ret == 0);
91                 
92                 pthread_cleanup_pop (0);
93         }
94         
95         return 0;
96 }
97
98 #endif
99
100 /*
101 Returns a recursive mutex that is safe under suspension.
102
103 A suspension safe mutex means one that can handle this scenario:
104
105 mutex M
106
107 thread 1:
108 1)lock M
109 2)suspend thread 2
110 3)unlock M
111 4)lock M
112
113 thread 2:
114 5)lock M
115
116 Say (1) happens before (5) and (5) happens before (2).
117 This means that thread 2 was suspended by the kernel because
118 it's waiting on mutext M.
119
120 Thread 1 then proceed to suspend thread 2 and unlock/lock the
121 mutex.
122
123 If the kernel implements mutexes with FIFO wait lists, this means
124 that thread 1 will be blocked waiting for thread 2 acquire the lock.
125 Since thread 2 is suspended, we have a deadlock.
126
127 A suspend safe mutex is an unfair lock but will schedule any runable
128 thread that is waiting for a the lock.
129
130 This problem was witnessed on OSX in mono/tests/thread-exit.cs.
131
132 */
133 int
134 mono_mutex_init_suspend_safe (mono_mutex_t *mutex)
135 {
136 #if defined(__APPLE__)
137         int res;
138         pthread_mutexattr_t attr;
139         static gboolean inited;
140         static int (*setpolicy_np) (pthread_mutexattr_t *, int);
141
142         if (!inited) {
143                 setpolicy_np = (int (*) (pthread_mutexattr_t *, int)) dlsym (RTLD_NEXT, "pthread_mutexattr_setpolicy_np");
144                 mono_atomic_store_release (&inited, TRUE);
145         }
146
147         pthread_mutexattr_init (&attr);
148         pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
149         if (setpolicy_np)
150                 setpolicy_np (&attr, _PTHREAD_MUTEX_POLICY_FIRSTFIT);
151         res = pthread_mutex_init (mutex, &attr);
152         pthread_mutexattr_destroy (&attr);
153
154         return res;
155 #else
156         return mono_mutex_init (mutex);
157 #endif
158 }