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