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