This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mono / io-layer / mono-mutex.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2002 Ximain, Inc. (www.ximian.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <sys/time.h>
33
34 #include "mono-mutex.h"
35
36
37 #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
38 int pthread_mutex_timedlock (pthread_mutex_t *mutex,
39                             const struct timespec *timeout);
40
41 int
42 pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *timeout)
43 {
44         struct timeval timenow;
45         struct timespec sleepytime;
46         int retcode;
47         
48         /* This is just to avoid a completely busy wait */
49         sleepytime.tv_sec = 0;
50         sleepytime.tv_nsec = 10000000;  /* 10ms */
51         
52         while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
53                 gettimeofday (&timenow, NULL);
54                 
55                 if (timenow.tv_sec >= timeout->tv_sec &&
56                     (timenow.tv_usec * 1000) >= timeout->tv_nsec) {
57                         return ETIMEDOUT;
58                 }
59                 
60                 nanosleep (&sleepytime, NULL);
61         }
62         
63         return retcode;
64 }
65 #endif /* HAVE_PTHREAD_MUTEX_TIMEDLOCK */
66
67
68 int
69 mono_once (mono_once_t *once, void (*once_init) (void))
70 {
71         int thr_ret;
72         
73         if (!once->complete) {
74                 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
75                                       (void *)&once->mutex);
76                 thr_ret = pthread_mutex_lock (&once->mutex);
77                 g_assert (thr_ret == 0);
78                 
79                 if (!once->complete) {
80                         once_init ();
81                         once->complete = TRUE;
82                 }
83                 thr_ret = pthread_mutex_unlock (&once->mutex);
84                 g_assert (thr_ret == 0);
85                 
86                 pthread_cleanup_pop (0);
87         }
88         
89         return 0;
90 }
91
92
93 #ifdef USE_MONO_MUTEX
94
95 int
96 mono_mutexattr_init (mono_mutexattr_t *attr)
97 {
98         memset (attr, 0, sizeof (mono_mutexattr_t));
99         return 0;
100 }
101
102 int
103 mono_mutexattr_settype (mono_mutexattr_t *attr, int type)
104 {
105         attr->type = type;
106         return 0;
107 }
108
109 int
110 mono_mutexattr_gettype (mono_mutexattr_t *attr, int *type)
111 {
112         *type = attr->type;
113         return 0;
114 }
115
116 int
117 mono_mutexattr_setpshared (mono_mutexattr_t *attr, int pshared)
118 {
119         attr->shared = pshared;
120         return 0;
121 }
122
123 int
124 mono_mutexattr_getpshared (mono_mutexattr_t *attr, int *pshared)
125 {
126         *pshared = attr->shared;
127         return 0;
128 }
129
130 int
131 mono_mutexattr_setprotocol (mono_mutexattr_t *attr, int protocol)
132 {
133         attr->protocol = protocol;
134         return 0;
135 }
136
137 int
138 mono_mutexattr_getprotocol (mono_mutexattr_t *attr, int *protocol)
139 {
140         *protocol = attr->protocol;
141         return 0;
142 }
143
144 int
145 mono_mutexattr_setprioceiling (mono_mutexattr_t *attr, int prioceiling)
146 {
147         attr->priority = prioceiling;
148         return 0;
149 }
150
151 int
152 mono_mutexattr_getprioceiling (mono_mutexattr_t *attr, int *prioceiling)
153 {
154         *prioceiling = attr->priority;
155         return 0;
156 }
157
158 int
159 mono_mutexattr_destroy (mono_mutexattr_t *attr)
160 {
161         return 0;
162 }
163
164
165 int
166 mono_mutex_init (mono_mutex_t *mutex, const mono_mutexattr_t *attr)
167 {
168         int ret;
169         int thr_ret;
170         
171         mutex->waiters = 0;
172         mutex->depth = 0;
173         mutex->owner = MONO_THREAD_NONE;
174         
175         if (!attr || attr->type == MONO_MUTEX_NORMAL) {
176                 mutex->type = MONO_MUTEX_NORMAL;
177                 ret = pthread_mutex_init (&mutex->mutex, NULL);
178         } else {
179                 mutex->type = MONO_MUTEX_RECURSIVE;
180                 ret = pthread_mutex_init (&mutex->mutex, NULL);
181                 thr_ret = pthread_cond_init (&mutex->cond, NULL);
182                 g_assert (thr_ret == 0);
183         }
184         
185         return(ret);
186 }
187
188 int
189 mono_mutex_lock (mono_mutex_t *mutex)
190 {
191         pthread_t id;
192         
193         switch (mutex->type) {
194         case MONO_MUTEX_NORMAL:
195                 return pthread_mutex_lock (&mutex->mutex);
196         case MONO_MUTEX_RECURSIVE:
197                 id = pthread_self ();
198                 if (pthread_mutex_lock (&mutex->mutex) != 0)
199                         return EINVAL;
200                 
201                 while (1) {
202                         if (mutex->owner == MONO_THREAD_NONE) {
203                                 mutex->owner = id;
204                                 mutex->depth = 1;
205                                 break;
206                         } else if (mutex->owner == id) {
207                                 mutex->depth++;
208                                 break;
209                         } else {
210                                 mutex->waiters++;
211                                 if (pthread_cond_wait (&mutex->cond, &mutex->mutex) != 0)
212                                         return EINVAL;
213                                 mutex->waiters--;
214                         }
215                 }
216                 
217                 return pthread_mutex_unlock (&mutex->mutex);
218         }
219         
220         return EINVAL;
221 }
222
223 int
224 mono_mutex_trylock (mono_mutex_t *mutex)
225 {
226         pthread_t id;
227         
228         switch (mutex->type) {
229         case MONO_MUTEX_NORMAL:
230                 return pthread_mutex_trylock (&mutex->mutex);
231         case MONO_MUTEX_RECURSIVE:
232                 id = pthread_self ();
233                 
234                 if (pthread_mutex_lock (&mutex->mutex) != 0)
235                         return EINVAL;
236                 
237                 if (mutex->owner != MONO_THREAD_NONE && mutex->owner != id) {
238                         pthread_mutex_unlock (&mutex->mutex);
239                         return EBUSY;
240                 }
241                 
242                 while (1) {
243                         if (mutex->owner == MONO_THREAD_NONE) {
244                                 mutex->owner = id;
245                                 mutex->depth = 1;
246                                 break;
247                         } else {
248                                 mutex->depth++;
249                                 break;
250                         }
251                 }
252                 
253                 return pthread_mutex_unlock (&mutex->mutex);
254         }
255         
256         return EINVAL;
257 }
258
259 int
260 mono_mutex_timedlock (mono_mutex_t *mutex, const struct timespec *timeout)
261 {
262         pthread_t id;
263         
264         switch (mutex->type) {
265         case MONO_MUTEX_NORMAL:
266                 return pthread_mutex_timedlock (&mutex->mutex, timeout);
267         case MONO_MUTEX_RECURSIVE:
268                 id = pthread_self ();
269                 
270                 if (pthread_mutex_timedlock (&mutex->mutex, timeout) != 0)
271                         return ETIMEDOUT;
272                 
273                 while (1) {
274                         if (mutex->owner == MONO_THREAD_NONE) {
275                                 mutex->owner = id;
276                                 mutex->depth = 1;
277                                 break;
278                         } else if (mutex->owner == id) {
279                                 mutex->depth++;
280                                 break;
281                         } else {
282                                 mutex->waiters++;
283                                 if (pthread_cond_timedwait (&mutex->cond, &mutex->mutex, timeout) != 0)
284                                         return ETIMEDOUT;
285                                 mutex->waiters--;
286                         }
287                 }
288                 
289                 return pthread_mutex_unlock (&mutex->mutex);
290         }
291         
292         return EINVAL;
293 }
294
295 int
296 mono_mutex_unlock (mono_mutex_t *mutex)
297 {
298         int thr_ret;
299         
300         switch (mutex->type) {
301         case MONO_MUTEX_NORMAL:
302                 return pthread_mutex_unlock (&mutex->mutex);
303         case MONO_MUTEX_RECURSIVE:
304                 if (pthread_mutex_lock (&mutex->mutex) != 0)
305                         return EINVAL;
306                 
307                 if (mutex->owner != pthread_self()) {
308                         /* Not owned by this thread */
309                         pthread_mutex_unlock (&mutex->mutex);
310                         return EPERM;
311                 }
312                 
313                 mutex->depth--;
314                 if (mutex->depth == 0) {
315                         mutex->owner = MONO_THREAD_NONE;
316                         if (mutex->waiters > 0) {
317                                 thr_ret = pthread_cond_signal (&mutex->cond);
318                                 g_assert (thr_ret == 0);
319                         }
320                 }
321                 
322                 return pthread_mutex_unlock (&mutex->mutex);
323         }
324         
325         return EINVAL;
326 }
327
328 int
329 mono_mutex_destroy (mono_mutex_t *mutex)
330 {
331         int ret = 0;
332         int thr_ret;
333         
334         switch (mutex->type) {
335         case MONO_MUTEX_NORMAL:
336                 ret = pthread_mutex_destroy (&mutex->mutex);
337                 break;
338         case MONO_MUTEX_RECURSIVE:
339                 if ((ret = pthread_mutex_destroy (&mutex->mutex)) == 0) {
340                         thr_ret = pthread_cond_destroy (&mutex->cond);
341                         g_assert (thr_ret == 0);
342                 }
343         }
344         
345         return ret;
346 }
347
348
349 int
350 mono_cond_wait (pthread_cond_t *cond, mono_mutex_t *mutex)
351 {
352         return pthread_cond_wait (cond, &mutex->mutex);
353 }
354
355 int
356 mono_cond_timedwait (pthread_cond_t *cond, mono_mutex_t *mutex, const struct timespec *timeout)
357 {
358         return pthread_cond_timedwait (cond, &mutex->mutex, timeout);
359 }
360
361 #endif /* USE_MONO_MUTEX */