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