e314274e991676e45e8b718d501a4b6a843579b2
[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
32 #include "mono-mutex.h"
33
34
35 #ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
36 int
37 pthread_mutex_timedlock (pthread_mutex_t *mutex, const 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 = 10000;     /* 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
64 #ifdef USE_MONO_MUTEX
65
66 int
67 mono_mutexattr_init (mono_mutexattr_t *attr)
68 {
69         memset (attr, 0, sizeof (mono_mutexattr_t));
70         return 0;
71 }
72
73 int
74 mono_mutexattr_settype (mono_mutexattr_t *attr, int type)
75 {
76         attr->type = type;
77         return 0;
78 }
79
80 int
81 mono_mutexattr_gettype (mono_mutexattr_t *attr, int *type)
82 {
83         *type = attr->type;
84         return 0;
85 }
86
87 int
88 mono_mutexattr_setpshared (mono_mutexattr_t *attr, int pshared)
89 {
90         attr->shared = pshared;
91         return 0;
92 }
93
94 int
95 mono_mutexattr_getpshared (mono_mutexattr_t *attr, int *pshared)
96 {
97         *pshared = attr->shared;
98         return 0;
99 }
100
101 int
102 mono_mutexattr_setprotocol (mono_mutexattr_t *attr, int protocol)
103 {
104         attr->protocol = protocol;
105         return 0;
106 }
107
108 int
109 mono_mutexattr_getprotocol (mono_mutexattr_t *attr, int *protocol)
110 {
111         *protocol = attr->protocol;
112         return 0;
113 }
114
115 int
116 mono_mutexattr_setprioceiling (mono_mutexattr_t *attr, int prioceiling)
117 {
118         attr->priority = prioceiling;
119         return 0;
120 }
121
122 int
123 mono_mutexattr_getprioceiling (mono_mutexattr_t *attr, int *prioceiling)
124 {
125         *prioceiling = attr->priority;
126         return 0;
127 }
128
129 int
130 mono_mutexattr_destroy (mono_mutexattr_t *attr)
131 {
132         return 0;
133 }
134
135
136 int
137 mono_mutex_init (mono_mutex_t *mutex, const mono_mutexattr_t *attr)
138 {
139         mutex->waiters = 0;
140         mutex->depth = 0;
141         mutex->owner = MONO_THREAD_NONE;
142         
143         if (!attr || attr->type == MONO_MUTEX_NORMAL) {
144                 mutex->type = MONO_MUTEX_NORMAL;
145                 pthread_mutex_init (&mutex->mutex, NULL);
146         } else {
147                 mutex->type = MONO_MUTEX_RECURSIVE;
148                 pthread_mutex_init (&mutex->mutex, NULL);
149                 pthread_cond_init (&mutex->cond, NULL);
150         }
151         
152         return 0;
153 }
154
155 int
156 mono_mutex_lock (mono_mutex_t *mutex)
157 {
158         pthread_t id;
159         
160         switch (mutex->type) {
161         case MONO_MUTEX_NORMAL:
162                 return pthread_mutex_lock (&mutex->mutex);
163         case MONO_MUTEX_RECURSIVE:
164                 id = pthread_self ();
165                 if (pthread_mutex_lock (&mutex->mutex) != 0)
166                         return EINVAL;
167                 
168                 while (1) {
169                         if (mutex->owner == MONO_THREAD_NONE) {
170                                 mutex->owner = id;
171                                 mutex->depth = 1;
172                                 break;
173                         } else if (mutex->owner == id) {
174                                 mutex->depth++;
175                                 break;
176                         } else {
177                                 mutex->waiters++;
178                                 if (pthread_cond_wait (&mutex->cond, &mutex->mutex) == -1)
179                                         return EINVAL;
180                                 mutex->waiters--;
181                         }
182                 }
183                 
184                 return pthread_mutex_unlock (&mutex->mutex);
185         }
186         
187         return EINVAL;
188 }
189
190 int
191 mono_mutex_trylock (mono_mutex_t *mutex)
192 {
193         pthread_t id;
194         
195         switch (mutex->type) {
196         case MONO_MUTEX_NORMAL:
197                 return pthread_mutex_trylock (&mutex->mutex);
198         case MONO_MUTEX_RECURSIVE:
199                 id = pthread_self ();
200                 
201                 if (pthread_mutex_lock (&mutex->mutex) != 0)
202                         return EINVAL;
203                 
204                 if (mutex->owner != MONO_THREAD_NONE && mutex->owner != id) {
205                         pthread_mutex_unlock (&mutex->mutex);
206                         return EBUSY;
207                 }
208                 
209                 while (1) {
210                         if (mutex->owner == MONO_THREAD_NONE) {
211                                 mutex->owner = id;
212                                 mutex->depth = 1;
213                                 break;
214                         } else {
215                                 mutex->depth++;
216                                 break;
217                         }
218                 }
219                 
220                 return pthread_mutex_unlock (&mutex->mutex);
221         }
222         
223         return EINVAL;
224 }
225
226 int
227 mono_mutex_timedlock (mono_mutex_t *mutex, const struct timespec *timeout)
228 {
229         pthread_t id;
230         
231         switch (mutex->type) {
232         case MONO_MUTEX_NORMAL:
233                 return pthread_mutex_timedlock (&mutex->mutex, timeout);
234         case MONO_MUTEX_RECURSIVE:
235                 id = pthread_self ();
236                 
237                 if (pthread_mutex_timedlock (&mutex->mutex, timeout) != 0)
238                         return ETIMEDOUT;
239                 
240                 while (1) {
241                         if (mutex->owner == MONO_THREAD_NONE) {
242                                 mutex->owner = id;
243                                 mutex->depth = 1;
244                                 break;
245                         } else if (mutex->owner == id) {
246                                 mutex->depth++;
247                                 break;
248                         } else {
249                                 mutex->waiters++;
250                                 if (pthread_cond_timedwait (&mutex->cond, &mutex->mutex, timeout) != 0)
251                                         return ETIMEDOUT;
252                                 mutex->waiters--;
253                         }
254                 }
255                 
256                 return pthread_mutex_unlock (&mutex->mutex);
257         }
258         
259         return EINVAL;
260 }
261
262 int
263 mono_mutex_unlock (mono_mutex_t *mutex)
264 {
265         switch (mutex->type) {
266         case MONO_MUTEX_NORMAL:
267                 return pthread_mutex_unlock (&mutex->mutex);
268         case MONO_MUTEX_RECURSIVE:
269                 if (pthread_mutex_lock (&mutex->mutex) != 0)
270                         return EINVAL;
271                 
272                 g_assert (mutex->owner == pthread_self ());
273                 
274                 mutex->depth--;
275                 if (mutex->depth == 0) {
276                         mutex->owner = MONO_THREAD_NONE;
277                         if (mutex->waiters > 0)
278                                 pthread_cond_signal (&mutex->cond);
279                 }
280                 
281                 return pthread_mutex_unlock (&mutex->mutex);
282         }
283         
284         return EINVAL;
285 }
286
287 int
288 mono_mutex_destroy (mono_mutex_t *mutex)
289 {
290         int ret = 0;
291         
292         switch (mutex->type) {
293         case MONO_MUTEX_NORMAL:
294                 ret = pthread_mutex_destroy (&mutex->mutex);
295                 break;
296         case MONO_MUTEX_RECURSIVE:
297                 if ((ret = pthread_mutex_destroy (&mutex->mutex)) == 0) {
298                         pthread_cond_destroy (&mutex->cond);
299                 }
300         }
301         
302         return ret;
303 }
304
305
306 int
307 mono_cond_wait (pthread_cond_t *cond, mono_mutex_t *mutex)
308 {
309         return pthread_cond_wait (cond, &mutex->mutex);
310 }
311
312 int
313 mono_cond_timedwait (pthread_cond_t *cond, mono_mutex_t *mutex, const struct timespec *timeout)
314 {
315         return pthread_cond_wait (cond, &mutex->mutex, timeout);
316 }
317
318 #endif /* USE_MONO_MUTEX */