New tests.
[mono.git] / mono / io-layer / 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 <sys/time.h>
20
21 #include "mono-mutex.h"
22
23
24 #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
25 /* Android does not implement pthread_mutex_timedlock(), but does provide an
26  * unusual declaration: http://code.google.com/p/android/issues/detail?id=7807
27  */
28 #ifdef PLATFORM_ANDROID
29 #define CONST_NEEDED
30 #else
31 #define CONST_NEEDED const
32 #endif
33
34 int pthread_mutex_timedlock (pthread_mutex_t *mutex,
35                             CONST_NEEDED struct timespec *timeout);
36 int
37 pthread_mutex_timedlock (pthread_mutex_t *mutex, CONST_NEEDED struct timespec *timeout)
38 {
39         struct timeval timenow;
40         struct timespec sleepytime;
41         int retcode;
42         
43         /* This is just to avoid a completely busy wait */
44         sleepytime.tv_sec = 0;
45         sleepytime.tv_nsec = 10000000;  /* 10ms */
46         
47         while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
48                 gettimeofday (&timenow, NULL);
49                 
50                 if (timenow.tv_sec >= timeout->tv_sec &&
51                     (timenow.tv_usec * 1000) >= timeout->tv_nsec) {
52                         return ETIMEDOUT;
53                 }
54                 
55                 nanosleep (&sleepytime, NULL);
56         }
57         
58         return retcode;
59 }
60 #endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */
61
62
63 int
64 mono_once (mono_once_t *once, void (*once_init) (void))
65 {
66         int thr_ret;
67         
68         if (!once->complete) {
69                 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
70                                       (void *)&once->mutex);
71                 thr_ret = pthread_mutex_lock (&once->mutex);
72                 g_assert (thr_ret == 0);
73                 
74                 if (!once->complete) {
75                         once_init ();
76                         once->complete = TRUE;
77                 }
78                 thr_ret = pthread_mutex_unlock (&once->mutex);
79                 g_assert (thr_ret == 0);
80                 
81                 pthread_cleanup_pop (0);
82         }
83         
84         return 0;
85 }
86
87
88 #ifdef USE_MONO_MUTEX
89
90 int
91 mono_mutexattr_init (mono_mutexattr_t *attr)
92 {
93         memset (attr, 0, sizeof (mono_mutexattr_t));
94         return 0;
95 }
96
97 int
98 mono_mutexattr_settype (mono_mutexattr_t *attr, int type)
99 {
100         attr->type = type;
101         return 0;
102 }
103
104 int
105 mono_mutexattr_gettype (mono_mutexattr_t *attr, int *type)
106 {
107         *type = attr->type;
108         return 0;
109 }
110
111 int
112 mono_mutexattr_setpshared (mono_mutexattr_t *attr, int pshared)
113 {
114         attr->shared = pshared;
115         return 0;
116 }
117
118 int
119 mono_mutexattr_getpshared (mono_mutexattr_t *attr, int *pshared)
120 {
121         *pshared = attr->shared;
122         return 0;
123 }
124
125 int
126 mono_mutexattr_setprotocol (mono_mutexattr_t *attr, int protocol)
127 {
128         attr->protocol = protocol;
129         return 0;
130 }
131
132 int
133 mono_mutexattr_getprotocol (mono_mutexattr_t *attr, int *protocol)
134 {
135         *protocol = attr->protocol;
136         return 0;
137 }
138
139 int
140 mono_mutexattr_setprioceiling (mono_mutexattr_t *attr, int prioceiling)
141 {
142         attr->priority = prioceiling;
143         return 0;
144 }
145
146 int
147 mono_mutexattr_getprioceiling (mono_mutexattr_t *attr, int *prioceiling)
148 {
149         *prioceiling = attr->priority;
150         return 0;
151 }
152
153 int
154 mono_mutexattr_destroy (mono_mutexattr_t *attr)
155 {
156         return 0;
157 }
158
159
160 int
161 mono_mutex_init (mono_mutex_t *mutex, const mono_mutexattr_t *attr)
162 {
163         int ret;
164         int thr_ret;
165         
166         mutex->waiters = 0;
167         mutex->depth = 0;
168         mutex->owner = MONO_THREAD_NONE;
169         
170         if (!attr || attr->type == MONO_MUTEX_NORMAL) {
171                 mutex->type = MONO_MUTEX_NORMAL;
172                 ret = pthread_mutex_init (&mutex->mutex, NULL);
173         } else {
174                 mutex->type = MONO_MUTEX_RECURSIVE;
175                 ret = pthread_mutex_init (&mutex->mutex, NULL);
176                 thr_ret = pthread_cond_init (&mutex->cond, NULL);
177                 g_assert (thr_ret == 0);
178         }
179         
180         return(ret);
181 }
182
183 int
184 mono_mutex_lock (mono_mutex_t *mutex)
185 {
186         pthread_t id;
187         
188         switch (mutex->type) {
189         case MONO_MUTEX_NORMAL:
190                 return pthread_mutex_lock (&mutex->mutex);
191         case MONO_MUTEX_RECURSIVE:
192                 id = pthread_self ();
193                 if (pthread_mutex_lock (&mutex->mutex) != 0)
194                         return EINVAL;
195                 
196                 while (1) {
197                         if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) {
198                                 mutex->owner = id;
199                                 mutex->depth = 1;
200                                 break;
201                         } else if (pthread_equal (mutex->owner, id)) {
202                                 mutex->depth++;
203                                 break;
204                         } else {
205                                 mutex->waiters++;
206                                 if (pthread_cond_wait (&mutex->cond, &mutex->mutex) != 0)
207                                         return EINVAL;
208                                 mutex->waiters--;
209                         }
210                 }
211                 
212                 return pthread_mutex_unlock (&mutex->mutex);
213         }
214         
215         return EINVAL;
216 }
217
218 int
219 mono_mutex_trylock (mono_mutex_t *mutex)
220 {
221         pthread_t id;
222         
223         switch (mutex->type) {
224         case MONO_MUTEX_NORMAL:
225                 return pthread_mutex_trylock (&mutex->mutex);
226         case MONO_MUTEX_RECURSIVE:
227                 id = pthread_self ();
228                 
229                 if (pthread_mutex_lock (&mutex->mutex) != 0)
230                         return EINVAL;
231                 
232                 if (!pthread_equal (mutex->owner, MONO_THREAD_NONE) &&
233                     !pthread_equal (mutex->owner, id)) {
234                         pthread_mutex_unlock (&mutex->mutex);
235                         return EBUSY;
236                 }
237                 
238                 while (1) {
239                         if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) {
240                                 mutex->owner = id;
241                                 mutex->depth = 1;
242                                 break;
243                         } else {
244                                 mutex->depth++;
245                                 break;
246                         }
247                 }
248                 
249                 return pthread_mutex_unlock (&mutex->mutex);
250         }
251         
252         return EINVAL;
253 }
254
255 int
256 mono_mutex_timedlock (mono_mutex_t *mutex, const struct timespec *timeout)
257 {
258         pthread_t id;
259         
260         switch (mutex->type) {
261         case MONO_MUTEX_NORMAL:
262                 return pthread_mutex_timedlock (&mutex->mutex, timeout);
263         case MONO_MUTEX_RECURSIVE:
264                 id = pthread_self ();
265                 
266                 if (pthread_mutex_timedlock (&mutex->mutex, timeout) != 0)
267                         return ETIMEDOUT;
268                 
269                 while (1) {
270                         if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) {
271                                 mutex->owner = id;
272                                 mutex->depth = 1;
273                                 break;
274                         } else if (pthread_equal (mutex->owner, id)) {
275                                 mutex->depth++;
276                                 break;
277                         } else {
278                                 mutex->waiters++;
279                                 if (pthread_cond_timedwait (&mutex->cond, &mutex->mutex, timeout) != 0)
280                                         return ETIMEDOUT;
281                                 mutex->waiters--;
282                         }
283                 }
284                 
285                 return pthread_mutex_unlock (&mutex->mutex);
286         }
287         
288         return EINVAL;
289 }
290
291 int
292 mono_mutex_unlock (mono_mutex_t *mutex)
293 {
294         int thr_ret;
295         
296         switch (mutex->type) {
297         case MONO_MUTEX_NORMAL:
298                 return pthread_mutex_unlock (&mutex->mutex);
299         case MONO_MUTEX_RECURSIVE:
300                 if (pthread_mutex_lock (&mutex->mutex) != 0)
301                         return EINVAL;
302                 
303                 if (pthread_equal (mutex->owner, pthread_self())) {
304                         /* Not owned by this thread */
305                         pthread_mutex_unlock (&mutex->mutex);
306                         return EPERM;
307                 }
308                 
309                 mutex->depth--;
310                 if (mutex->depth == 0) {
311                         mutex->owner = MONO_THREAD_NONE;
312                         if (mutex->waiters > 0) {
313                                 thr_ret = pthread_cond_signal (&mutex->cond);
314                                 g_assert (thr_ret == 0);
315                         }
316                 }
317                 
318                 return pthread_mutex_unlock (&mutex->mutex);
319         }
320         
321         return EINVAL;
322 }
323
324 int
325 mono_mutex_destroy (mono_mutex_t *mutex)
326 {
327         int ret = 0;
328         int thr_ret;
329         
330         switch (mutex->type) {
331         case MONO_MUTEX_NORMAL:
332                 ret = pthread_mutex_destroy (&mutex->mutex);
333                 break;
334         case MONO_MUTEX_RECURSIVE:
335                 if ((ret = pthread_mutex_destroy (&mutex->mutex)) == 0) {
336                         thr_ret = pthread_cond_destroy (&mutex->cond);
337                         g_assert (thr_ret == 0);
338                 }
339         }
340         
341         return ret;
342 }
343
344
345 int
346 mono_cond_wait (pthread_cond_t *cond, mono_mutex_t *mutex)
347 {
348         return pthread_cond_wait (cond, &mutex->mutex);
349 }
350
351 int
352 mono_cond_timedwait (pthread_cond_t *cond, mono_mutex_t *mutex, const struct timespec *timeout)
353 {
354         return pthread_cond_timedwait (cond, &mutex->mutex, timeout);
355 }
356
357 #endif /* USE_MONO_MUTEX */