2002-03-19 Dan Lewis <dihlewis@yahoo.co.uk>
[mono.git] / mono / io-layer / mutexes.c
1 #include <config.h>
2 #include <glib.h>
3 #include <pthread.h>
4 #include <string.h>
5
6 #include "mono/io-layer/wapi.h"
7 #include "wapi-private.h"
8 #include "wait-private.h"
9 #include "misc-private.h"
10 #include "handles-private.h"
11
12 #include "mono-mutex.h"
13
14 #undef DEBUG
15
16 struct _WapiHandle_mutex
17 {
18         WapiHandle handle;
19         mono_mutex_t mutex;
20         pthread_t tid;
21         guint32 recursion;
22 };
23
24 static void mutex_close(WapiHandle *handle);
25 static gboolean mutex_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms);
26 static guint32 mutex_wait_multiple(gpointer data);
27 static void mutex_signal(WapiHandle *handle);
28
29 static struct _WapiHandleOps mutex_ops = {
30         mutex_close,            /* close */
31         NULL,                   /* getfiletype */
32         NULL,                   /* readfile */
33         NULL,                   /* writefile */
34         NULL,                   /* flushfile */
35         NULL,                   /* seek */
36         NULL,                   /* setendoffile */
37         NULL,                   /* getfilesize */
38         NULL,                   /* getfiletime */
39         NULL,                   /* setfiletime */
40         mutex_wait,             /* wait */
41         mutex_wait_multiple,    /* wait_multiple */
42         mutex_signal,           /* signal */
43 };
44
45 static void mutex_close(WapiHandle *handle)
46 {
47         struct _WapiHandle_mutex *mutex_handle=(struct _WapiHandle_mutex *)handle;
48         
49 #ifdef DEBUG
50         g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p",
51                   mutex_handle);
52 #endif
53
54         mono_mutex_destroy(&mutex_handle->mutex);
55 }
56
57 static gboolean mutex_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms)
58 {
59         struct _WapiHandle_mutex *mutex_handle=(struct _WapiHandle_mutex *)handle;
60         pthread_t tid=pthread_self();
61         int ret;
62         
63 #ifdef DEBUG
64         g_message(G_GNUC_PRETTY_FUNCTION ": waiting for mutex handle %p",
65                   mutex_handle);
66 #endif
67
68         /* Signal this handle now.  It really doesn't matter if some
69          * other thread grabs the mutex before we can
70          */
71         if(signal!=NULL) {
72                 signal->ops->signal(signal);
73         }
74
75         if(mutex_handle->tid==tid) {
76                 /* We already own this mutex, so just increase the count and
77                  * return TRUE
78                  */
79
80                 mutex_handle->recursion++;
81         
82 #ifdef DEBUG
83                 g_message(G_GNUC_PRETTY_FUNCTION
84                           ": Already own mutex handle %p (recursion %d)",
85                           mutex_handle, mutex_handle->recursion);
86 #endif
87
88                 return(TRUE);
89         }
90         
91         if(ms==INFINITE) {
92                 ret=mono_mutex_lock(&mutex_handle->mutex);
93         } else {
94                 struct timespec timeout;
95                 
96                 _wapi_calc_timeout(&timeout, ms);
97                 
98                 ret=mono_mutex_timedlock(&mutex_handle->mutex, &timeout);
99         }
100
101         if(ret==0) {
102                 /* Mutex locked */
103         
104 #ifdef DEBUG
105                 g_message(G_GNUC_PRETTY_FUNCTION ": Locking mutex handle %p",
106                           mutex_handle);
107 #endif
108
109                 mutex_handle->tid=tid;
110                 mutex_handle->recursion=1;
111
112                 return(TRUE);
113         } else {
114                 /* ret might be ETIMEDOUT for timeout, or other for error */
115         
116 #ifdef DEBUG
117                 g_message(G_GNUC_PRETTY_FUNCTION
118                           ": Failed to lock mutex handle %p: %s", mutex_handle,
119                           strerror(ret));
120 #endif
121                 return(FALSE);
122         }
123 }
124
125 static guint32 mutex_wait_multiple(gpointer data G_GNUC_UNUSED)
126 {
127         WaitQueueItem *item=(WaitQueueItem *)data;
128         GPtrArray *needed;
129         int ret;
130         guint32 numhandles;
131         struct timespec timeout;
132         pthread_t tid=pthread_self();
133         guint32 i, iterations;
134         
135         numhandles=item->handles[WAPI_HANDLE_MUTEX]->len;
136         
137 #ifdef DEBUG
138         g_message(G_GNUC_PRETTY_FUNCTION
139                   ": waiting on %d mutex handles for %d ms", numhandles,
140                   item->timeout);
141 #endif
142
143         /*
144          * See which ones we need to lock
145          */
146         needed=g_ptr_array_new();
147         for(i=0; i<numhandles; i++) {
148                 struct _WapiHandle_mutex *mutex_handle;
149                 
150                 mutex_handle=g_ptr_array_index(
151                         item->handles[WAPI_HANDLE_MUTEX], i);
152                 
153                 if(mutex_handle->tid!=tid) {
154                         /* We don't have this one, so add it to the list */
155                         g_ptr_array_add(needed, mutex_handle);
156                 }
157         }
158
159 #ifdef DEBUG
160         g_message(G_GNUC_PRETTY_FUNCTION ": need to lock %d mutex handles",
161                   needed->len);
162 #endif
163         
164         iterations=0;
165         do {
166                 iterations++;
167                 
168                 /* If the timeout isnt INFINITE but greater than 1s,
169                  * split the timeout into 1s chunks
170                  */
171                 if((item->timeout!=INFINITE) &&
172                    (item->timeout < (iterations*1000))) {
173                         _wapi_calc_timeout(
174                                 &timeout, item->timeout-((iterations-1)*1000));
175                 } else {
176                         _wapi_calc_timeout(&timeout, 1000);
177                 }
178         
179                 /* Try and lock as many mutexes as we can until we run
180                  * out of time, but to avoid deadlocks back off if we
181                  * fail to lock one
182                  */
183                 for(i=0; i<needed->len; i++) {
184                         struct _WapiHandle_mutex *mutex_handle;
185
186                         mutex_handle=g_ptr_array_index(needed, i);
187                 
188 #ifdef DEBUG
189                         g_message(G_GNUC_PRETTY_FUNCTION
190                                   ": Locking %d mutex %p (owner %ld, me %ld)",
191                                   i, mutex_handle, mutex_handle->tid, tid);
192 #endif
193
194                         ret=mono_mutex_timedlock(&mutex_handle->mutex,
195                                                  &timeout);
196
197 #ifdef DEBUG
198                         g_message(G_GNUC_PRETTY_FUNCTION ": timedlock ret %s",
199                                   strerror(ret));
200 #endif
201
202                         if(ret!=0) {
203                                 /* ETIMEDOUT is the most likely, but
204                                  * fail on other error too
205                                  */
206
207 #ifdef DEBUG
208                                 g_message(G_GNUC_PRETTY_FUNCTION
209                                           ": Lock %d mutex failed: %s", i,
210                                           strerror(ret));
211 #endif
212
213                                 while(i--) {
214 #ifdef DEBUG
215                                         g_message(G_GNUC_PRETTY_FUNCTION
216                                                   ": Releasing %d mutex", i);
217 #endif
218                                         mutex_handle=g_ptr_array_index(needed,
219                                                                        i);
220                                         mono_mutex_unlock(&mutex_handle->mutex);
221                                 }
222
223                                 break;
224                         }
225
226                         /* OK, got that one. Don't record it as ours
227                          * though until we get them all
228                          */
229                 }
230
231                 if(i==needed->len) {
232                         /* We've locked all the mutexes.  Update the
233                          * ones we already had, and record that the
234                          * new ones belong to us
235                          */
236                         for(i=0; i<numhandles; i++) {
237                                 struct _WapiHandle_mutex *mutex_handle;
238                                 guint32 idx;
239
240                                 mutex_handle=g_ptr_array_index(
241                                         item->handles[WAPI_HANDLE_MUTEX], i);
242                 
243                                 idx=g_array_index(
244                                         item->waitindex[WAPI_HANDLE_MUTEX],
245                                         guint32, i);
246                                 _wapi_handle_set_lowest(item, idx);
247                                 
248 #ifdef DEBUG
249                                 g_message(G_GNUC_PRETTY_FUNCTION
250                                           ": Updating mutex %p", mutex_handle);
251 #endif
252                                 
253                                 if(mutex_handle->tid==tid) {
254                                         /* We already own this mutex,
255                                          * so just increase the count
256                                          */
257                                         mutex_handle->recursion++;
258                                 } else {
259                                         mutex_handle->tid=tid;
260                                         mutex_handle->recursion=1;
261                                 }
262                         }
263                 
264                         g_ptr_array_free(needed, FALSE);
265
266                         item->waited[WAPI_HANDLE_MUTEX]=TRUE;
267                         item->waitcount[WAPI_HANDLE_MUTEX]=numhandles;
268                         
269                         return(numhandles);
270                 }
271         } while((item->timeout==INFINITE) ||
272                 (item->timeout > (iterations * 1000)));
273
274         /* Didn't get all the locks, and timeout isn't INFINITE */
275                 
276         g_ptr_array_free(needed, FALSE);
277
278         item->waited[WAPI_HANDLE_MUTEX]=TRUE;
279         item->waitcount[WAPI_HANDLE_MUTEX]=0;
280         
281         return(0);
282 }
283
284 static void mutex_signal(WapiHandle *handle)
285 {
286         ReleaseMutex(handle);
287 }
288
289 /**
290  * CreateMutex:
291  * @security: Ignored for now.
292  * @owned: If %TRUE, the mutex is created with the calling thread
293  * already owning the mutex.
294  * @name:Pointer to a string specifying the name of this mutex, or
295  * %NULL.  Currently ignored.
296  *
297  * Creates a new mutex handle.  A mutex is signalled when no thread
298  * owns it.  A thread acquires ownership of the mutex by waiting for
299  * it with WaitForSingleObject() or WaitForMultipleObjects().  A
300  * thread relinquishes ownership with ReleaseMutex().
301  *
302  * A thread that owns a mutex can specify the same mutex in repeated
303  * wait function calls without blocking.  The thread must call
304  * ReleaseMutex() an equal number of times to release the mutex.
305  *
306  * Return value: A new handle, or %NULL on error.
307  */
308 WapiHandle *CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned G_GNUC_UNUSED,
309                         const guchar *name G_GNUC_UNUSED)
310 {
311         struct _WapiHandle_mutex *mutex_handle;
312         WapiHandle *handle;
313         
314         mutex_handle=(struct _WapiHandle_mutex *)g_new0(struct _WapiHandle_mutex, 1);
315         handle=(WapiHandle *)mutex_handle;
316         _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_MUTEX, mutex_ops);
317
318         mono_mutex_init(&mutex_handle->mutex, NULL);
319         if(owned==TRUE) {
320                 pthread_t tid=pthread_self();
321                 
322                 mono_mutex_lock(&mutex_handle->mutex);
323                 
324                 mutex_handle->tid=tid;
325                 mutex_handle->recursion=1;
326         }
327         
328         return(handle);
329 }
330
331 /**
332  * ReleaseMutex:
333  * @handle: The mutex handle.
334  *
335  * Releases ownership if the mutex handle @handle.
336  *
337  * Return value: %TRUE on success, %FALSE otherwise.  This function
338  * fails if the calling thread does not own the mutex @handle.
339  */
340 gboolean ReleaseMutex(WapiHandle *handle)
341 {
342         struct _WapiHandle_mutex *mutex_handle=(struct _WapiHandle_mutex *)handle;
343         pthread_t tid=pthread_self();
344         
345 #ifdef DEBUG
346         g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex handle %p",
347                   mutex_handle);
348 #endif
349
350         if(mutex_handle->tid!=tid) {
351 #ifdef DEBUG
352                 g_message(G_GNUC_PRETTY_FUNCTION ": We don't own mutex handle %p (owned by %ld, me %ld)", mutex_handle, mutex_handle->tid, tid);
353 #endif
354
355                 return(FALSE);
356         }
357
358         /* OK, we own this mutex */
359         mutex_handle->recursion--;
360         
361         if(mutex_handle->recursion==0) {
362 #ifdef DEBUG
363                 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p",
364                           mutex_handle);
365 #endif
366
367                 mutex_handle->tid=0;
368                 mono_mutex_unlock(&mutex_handle->mutex);
369         }
370         
371         return(TRUE);
372 }