merge -r 60814:60815
[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 (pthread_equal (mutex->owner, MONO_THREAD_NONE)) {
203                                 mutex->owner = id;
204                                 mutex->depth = 1;
205                                 break;
206                         } else if (pthread_equal (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 (!pthread_equal (mutex->owner, MONO_THREAD_NONE) &&
238                     !pthread_equal (mutex->owner, id)) {
239                         pthread_mutex_unlock (&mutex->mutex);
240                         return EBUSY;
241                 }
242                 
243                 while (1) {
244                         if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) {
245                                 mutex->owner = id;
246                                 mutex->depth = 1;
247                                 break;
248                         } else {
249                                 mutex->depth++;
250                                 break;
251                         }
252                 }
253                 
254                 return pthread_mutex_unlock (&mutex->mutex);
255         }
256         
257         return EINVAL;
258 }
259
260 int
261 mono_mutex_timedlock (mono_mutex_t *mutex, const struct timespec *timeout)
262 {
263         pthread_t id;
264         
265         switch (mutex->type) {
266         case MONO_MUTEX_NORMAL:
267                 return pthread_mutex_timedlock (&mutex->mutex, timeout);
268         case MONO_MUTEX_RECURSIVE:
269                 id = pthread_self ();
270                 
271                 if (pthread_mutex_timedlock (&mutex->mutex, timeout) != 0)
272                         return ETIMEDOUT;
273                 
274                 while (1) {
275                         if (pthread_equal (mutex->owner, MONO_THREAD_NONE)) {
276                                 mutex->owner = id;
277                                 mutex->depth = 1;
278                                 break;
279                         } else if (pthread_equal (mutex->owner, id)) {
280                                 mutex->depth++;
281                                 break;
282                         } else {
283                                 mutex->waiters++;
284                                 if (pthread_cond_timedwait (&mutex->cond, &mutex->mutex, timeout) != 0)
285                                         return ETIMEDOUT;
286                                 mutex->waiters--;
287                         }
288                 }
289                 
290                 return pthread_mutex_unlock (&mutex->mutex);
291         }
292         
293         return EINVAL;
294 }
295
296 int
297 mono_mutex_unlock (mono_mutex_t *mutex)
298 {
299         int thr_ret;
300         
301         switch (mutex->type) {
302         case MONO_MUTEX_NORMAL:
303                 return pthread_mutex_unlock (&mutex->mutex);
304         case MONO_MUTEX_RECURSIVE:
305                 if (pthread_mutex_lock (&mutex->mutex) != 0)
306                         return EINVAL;
307                 
308                 if (pthread_equal (mutex->owner, pthread_self())) {
309                         /* Not owned by this thread */
310                         pthread_mutex_unlock (&mutex->mutex);
311                         return EPERM;
312                 }
313                 
314                 mutex->depth--;
315                 if (mutex->depth == 0) {
316                         mutex->owner = MONO_THREAD_NONE;
317                         if (mutex->waiters > 0) {
318                                 thr_ret = pthread_cond_signal (&mutex->cond);
319                                 g_assert (thr_ret == 0);
320                         }
321                 }
322                 
323                 return pthread_mutex_unlock (&mutex->mutex);
324         }
325         
326         return EINVAL;
327 }
328
329 int
330 mono_mutex_destroy (mono_mutex_t *mutex)
331 {
332         int ret = 0;
333         int thr_ret;
334         
335         switch (mutex->type) {
336         case MONO_MUTEX_NORMAL:
337                 ret = pthread_mutex_destroy (&mutex->mutex);
338                 break;
339         case MONO_MUTEX_RECURSIVE:
340                 if ((ret = pthread_mutex_destroy (&mutex->mutex)) == 0) {
341                         thr_ret = pthread_cond_destroy (&mutex->cond);
342                         g_assert (thr_ret == 0);
343                 }
344         }
345         
346         return ret;
347 }
348
349
350 int
351 mono_cond_wait (pthread_cond_t *cond, mono_mutex_t *mutex)
352 {
353         return pthread_cond_wait (cond, &mutex->mutex);
354 }
355
356 int
357 mono_cond_timedwait (pthread_cond_t *cond, mono_mutex_t *mutex, const struct timespec *timeout)
358 {
359         return pthread_cond_timedwait (cond, &mutex->mutex, timeout);
360 }
361
362 #endif /* USE_MONO_MUTEX */