2004-11-04 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / io-layer / mutexes.c
1 /*
2  * mutexes.c:  Mutex handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include <mono/io-layer/wapi.h>
17 #include <mono/io-layer/wapi-private.h>
18 #include <mono/io-layer/misc-private.h>
19 #include <mono/io-layer/handles-private.h>
20 #include <mono/io-layer/mono-mutex.h>
21 #include <mono/io-layer/mutex-private.h>
22
23 #undef DEBUG
24
25 /* This is used to serialise mutex creation when names are given
26  */
27 static mono_mutex_t named_mutex_mutex;
28
29 static void mutex_close_shared (gpointer handle);
30 static void mutex_signal(gpointer handle);
31 static void mutex_own (gpointer handle);
32 static gboolean mutex_is_owned (gpointer handle);
33
34 struct _WapiHandleOps _wapi_mutex_ops = {
35         mutex_close_shared,     /* close_shared */
36         NULL,                   /* close_private */
37         mutex_signal,           /* signal */
38         mutex_own,              /* own */
39         mutex_is_owned,         /* is_owned */
40 };
41
42 static mono_once_t mutex_ops_once=MONO_ONCE_INIT;
43
44 static void mutex_ops_init (void)
45 {
46         int thr_ret;
47 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
48         pthread_mutexattr_t mutex_shared_attr;
49
50         thr_ret = mono_mutexattr_init (&mutex_shared_attr);
51         g_assert (thr_ret == 0);
52
53         thr_ret = mono_mutexattr_setpshared (&mutex_shared_attr,
54                                              PTHREAD_PROCESS_SHARED);
55         g_assert (thr_ret == 0);
56
57         thr_ret = mono_mutex_init (&named_mutex_mutex, &mutex_shared_attr);
58         g_assert (thr_ret == 0);
59 #else
60         thr_ret = mono_mutex_init (&named_mutex_mutex, NULL);
61 #endif
62
63         _wapi_handle_register_capabilities (WAPI_HANDLE_MUTEX,
64                                             WAPI_HANDLE_CAP_WAIT |
65                                             WAPI_HANDLE_CAP_SIGNAL |
66                                             WAPI_HANDLE_CAP_OWN);
67 }
68
69 static void mutex_close_shared (gpointer handle)
70 {
71         struct _WapiHandle_mutex *mutex_handle;
72         gboolean ok;
73         
74         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
75                                 (gpointer *)&mutex_handle, NULL);
76         if(ok==FALSE) {
77                 g_warning (G_GNUC_PRETTY_FUNCTION
78                            ": error looking up mutex handle %p", handle);
79                 return;
80         }
81         
82 #ifdef DEBUG
83         g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p", handle);
84 #endif
85
86         if(mutex_handle->sharedns.name!=0) {
87                 _wapi_handle_scratch_delete (mutex_handle->sharedns.name);
88                 mutex_handle->sharedns.name=0;
89         }
90 }
91
92 static void mutex_signal(gpointer handle)
93 {
94         ReleaseMutex(handle);
95 }
96
97 static void mutex_own (gpointer handle)
98 {
99         struct _WapiHandle_mutex *mutex_handle;
100         gboolean ok;
101         
102         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
103                                 (gpointer *)&mutex_handle, NULL);
104         if(ok==FALSE) {
105                 g_warning (G_GNUC_PRETTY_FUNCTION
106                            ": error looking up mutex handle %p", handle);
107                 return;
108         }
109         
110 #ifdef DEBUG
111         g_message(G_GNUC_PRETTY_FUNCTION ": owning mutex handle %p", handle);
112 #endif
113
114         _wapi_handle_set_signal_state (handle, FALSE, FALSE);
115         
116         mutex_handle->pid=getpid ();
117         mutex_handle->tid=pthread_self ();
118         mutex_handle->recursion++;
119
120 #ifdef DEBUG
121         g_message (G_GNUC_PRETTY_FUNCTION
122                    ": mutex handle %p locked %d times by %d:%ld", handle,
123                    mutex_handle->recursion, mutex_handle->pid,
124                    mutex_handle->tid);
125 #endif
126 }
127
128 static gboolean mutex_is_owned (gpointer handle)
129 {
130         struct _WapiHandle_mutex *mutex_handle;
131         gboolean ok;
132         
133         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
134                                 (gpointer *)&mutex_handle, NULL);
135         if(ok==FALSE) {
136                 g_warning (G_GNUC_PRETTY_FUNCTION
137                            ": error looking up mutex handle %p", handle);
138                 return(FALSE);
139         }
140         
141 #ifdef DEBUG
142         g_message(G_GNUC_PRETTY_FUNCTION
143                   ": testing ownership mutex handle %p", handle);
144 #endif
145
146         if(mutex_handle->recursion>0 &&
147            mutex_handle->pid==getpid () &&
148            mutex_handle->tid==pthread_self ()) {
149 #ifdef DEBUG
150                 g_message (G_GNUC_PRETTY_FUNCTION
151                            ": mutex handle %p owned by %d:%ld", handle,
152                            getpid (), pthread_self ());
153 #endif
154
155                 return(TRUE);
156         } else {
157 #ifdef DEBUG
158                 g_message (G_GNUC_PRETTY_FUNCTION
159                            ": mutex handle %p not owned by %d:%ld, but locked %d times by %d:%ld", handle, getpid (), pthread_self (), mutex_handle->recursion, mutex_handle->pid, mutex_handle->tid);
160 #endif
161
162                 return(FALSE);
163         }
164 }
165
166 struct mutex_check_data
167 {
168         pid_t pid;
169         pthread_t tid;
170 };
171
172 static gboolean mutex_check (gpointer handle, gpointer user_data)
173 {
174         struct _WapiHandle_mutex *mutex_handle;
175         gboolean ok;
176         struct mutex_check_data *data = (struct mutex_check_data *)user_data;
177         int thr_ret;
178         
179         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
180                                   (gpointer *)&mutex_handle, NULL);
181         if (ok == FALSE) {
182                 g_warning (G_GNUC_PRETTY_FUNCTION
183                            ": error looking up mutex handle %p", handle);
184                 return(FALSE);
185         }
186
187         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
188                               handle);
189         thr_ret = _wapi_handle_lock_handle (handle);
190         g_assert (thr_ret == 0);
191         
192         if (mutex_handle->pid == data->pid &&
193             mutex_handle->tid == data->tid) {
194 #ifdef DEBUG
195                 g_message (G_GNUC_PRETTY_FUNCTION
196                            ": Mutex handle %p abandoned!", handle);
197 #endif
198
199                 mutex_handle->recursion = 0;
200                 mutex_handle->pid = 0;
201                 mutex_handle->tid = 0;
202                 
203                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
204         }
205
206         thr_ret = _wapi_handle_unlock_handle (handle);
207         g_assert (thr_ret == 0);
208         pthread_cleanup_pop (0);
209         
210         /* Return false to keep searching */
211         return(FALSE);
212 }
213
214 /* When a thread exits, any mutexes it still holds need to be signalled */
215 void _wapi_mutex_check_abandoned (pid_t pid, pthread_t tid)
216 {
217         struct mutex_check_data data;
218
219         data.pid = pid;
220         data.tid = tid;
221         
222         _wapi_search_handle (WAPI_HANDLE_MUTEX, mutex_check, &data, NULL,
223                              NULL);
224 }
225
226 /**
227  * CreateMutex:
228  * @security: Ignored for now.
229  * @owned: If %TRUE, the mutex is created with the calling thread
230  * already owning the mutex.
231  * @name:Pointer to a string specifying the name of this mutex, or
232  * %NULL.
233  *
234  * Creates a new mutex handle.  A mutex is signalled when no thread
235  * owns it.  A thread acquires ownership of the mutex by waiting for
236  * it with WaitForSingleObject() or WaitForMultipleObjects().  A
237  * thread relinquishes ownership with ReleaseMutex().
238  *
239  * A thread that owns a mutex can specify the same mutex in repeated
240  * wait function calls without blocking.  The thread must call
241  * ReleaseMutex() an equal number of times to release the mutex.
242  *
243  * Return value: A new handle, or %NULL on error.
244  */
245 gpointer CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned,
246                         const gunichar2 *name)
247 {
248         struct _WapiHandle_mutex *mutex_handle;
249         gpointer handle;
250         gboolean ok;
251         gchar *utf8_name;
252         int thr_ret;
253         gpointer ret = NULL;
254         
255         mono_once (&mutex_ops_once, mutex_ops_init);
256
257         /* w32 seems to guarantee that opening named mutexes can't
258          * race each other
259          */
260         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
261                               (void *)&named_mutex_mutex);
262         thr_ret = mono_mutex_lock (&named_mutex_mutex);
263         g_assert (thr_ret == 0);
264
265         /* Need to blow away any old errors here, because code tests
266          * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
267          * was freshly created
268          */
269         SetLastError (ERROR_SUCCESS);
270         
271         if(name!=NULL) {
272                 utf8_name=g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
273         } else {
274                 utf8_name=NULL;
275         }
276         
277 #ifdef DEBUG
278         g_message (G_GNUC_PRETTY_FUNCTION ": Creating mutex (name [%s])",
279                    utf8_name==NULL?"<unnamed>":utf8_name);
280 #endif
281         
282         if(name!=NULL) {
283                 handle=_wapi_search_handle_namespace (
284                         WAPI_HANDLE_MUTEX, utf8_name,
285                         (gpointer *)&mutex_handle, NULL);
286                 if(handle==_WAPI_HANDLE_INVALID) {
287                         /* The name has already been used for a different
288                          * object.
289                          */
290                         g_free (utf8_name);
291                         SetLastError (ERROR_INVALID_HANDLE);
292                         goto cleanup;
293                 } else if (handle!=NULL) {
294                         g_free (utf8_name);
295                         _wapi_handle_ref (handle);
296                         ret = handle;
297
298                         /* Not an error, but this is how the caller is
299                          * informed that the mutex wasn't freshly
300                          * created
301                          */
302                         SetLastError (ERROR_ALREADY_EXISTS);
303                         goto cleanup;
304                 }
305                 /* Otherwise fall through to create the mutex. */
306         }
307         
308         handle=_wapi_handle_new (WAPI_HANDLE_MUTEX);
309         if(handle==_WAPI_HANDLE_INVALID) {
310                 g_warning (G_GNUC_PRETTY_FUNCTION
311                            ": error creating mutex handle");
312                 if(utf8_name!=NULL) {
313                         g_free (utf8_name);
314                 }
315                 goto cleanup;
316         }
317
318         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
319                               handle);
320         thr_ret = _wapi_handle_lock_handle (handle);
321         g_assert (thr_ret == 0);
322         
323         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
324                                 (gpointer *)&mutex_handle, NULL);
325         if(ok==FALSE) {
326                 g_warning (G_GNUC_PRETTY_FUNCTION
327                            ": error looking up mutex handle %p", handle);
328                 if(utf8_name!=NULL) {
329                         g_free (utf8_name);
330                 }
331                 
332                 goto handle_cleanup;
333         }
334         ret = handle;
335         
336         if(utf8_name!=NULL) {
337                 mutex_handle->sharedns.name=_wapi_handle_scratch_store (
338                         utf8_name, strlen (utf8_name));
339         }
340         
341         if(owned==TRUE) {
342                 mutex_own (handle);
343         } else {
344                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
345         }
346         
347 #ifdef DEBUG
348         g_message (G_GNUC_PRETTY_FUNCTION ": returning mutex handle %p",
349                    handle);
350 #endif
351
352         if(utf8_name!=NULL) {
353                 g_free (utf8_name);
354         }
355
356 handle_cleanup:
357         thr_ret = _wapi_handle_unlock_handle (handle);
358         g_assert (thr_ret == 0);
359         pthread_cleanup_pop (0);
360         
361 cleanup:
362         thr_ret = mono_mutex_unlock (&named_mutex_mutex);
363         g_assert (thr_ret == 0);
364         pthread_cleanup_pop (0);
365         
366         return(ret);
367 }
368
369 /**
370  * ReleaseMutex:
371  * @handle: The mutex handle.
372  *
373  * Releases ownership if the mutex handle @handle.
374  *
375  * Return value: %TRUE on success, %FALSE otherwise.  This function
376  * fails if the calling thread does not own the mutex @handle.
377  */
378 gboolean ReleaseMutex(gpointer handle)
379 {
380         struct _WapiHandle_mutex *mutex_handle;
381         gboolean ok;
382         pthread_t tid=pthread_self();
383         pid_t pid=getpid ();
384         int thr_ret;
385         gboolean ret = FALSE;
386         
387         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
388                                 (gpointer *)&mutex_handle, NULL);
389         if(ok==FALSE) {
390                 g_warning (G_GNUC_PRETTY_FUNCTION
391                            ": error looking up mutex handle %p", handle);
392                 return(FALSE);
393         }
394
395         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
396                               handle);
397         thr_ret = _wapi_handle_lock_handle (handle);
398         g_assert (thr_ret == 0);
399         
400 #ifdef DEBUG
401         g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex handle %p",
402                   handle);
403 #endif
404
405         if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) {
406 #ifdef DEBUG
407                 g_message(G_GNUC_PRETTY_FUNCTION ": We don't own mutex handle %p (owned by %d:%ld, me %d:%ld)", handle, mutex_handle->pid, mutex_handle->tid, pid, tid);
408 #endif
409
410                 goto cleanup;
411         }
412         ret = TRUE;
413         
414         /* OK, we own this mutex */
415         mutex_handle->recursion--;
416         
417         if(mutex_handle->recursion==0) {
418 #ifdef DEBUG
419                 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p",
420                           handle);
421 #endif
422
423                 mutex_handle->pid=0;
424                 mutex_handle->tid=0;
425                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
426         }
427
428 cleanup:
429         thr_ret = _wapi_handle_unlock_handle (handle);
430         g_assert (thr_ret == 0);
431         pthread_cleanup_pop (0);
432         
433         return(ret);
434 }