2004-06-08 Martin Baulig <martin@ximian.com>
[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  * (FIXME: make it process-shared)
27  */
28 static mono_mutex_t named_mutex_mutex = MONO_MUTEX_INITIALIZER;
29
30 static void mutex_close_shared (gpointer handle);
31 static void mutex_signal(gpointer handle);
32 static void mutex_own (gpointer handle);
33 static gboolean mutex_is_owned (gpointer handle);
34
35 struct _WapiHandleOps _wapi_mutex_ops = {
36         mutex_close_shared,     /* close_shared */
37         NULL,                   /* close_private */
38         mutex_signal,           /* signal */
39         mutex_own,              /* own */
40         mutex_is_owned,         /* is_owned */
41 };
42
43 static mono_once_t mutex_ops_once=MONO_ONCE_INIT;
44
45 static void mutex_ops_init (void)
46 {
47         _wapi_handle_register_capabilities (WAPI_HANDLE_MUTEX,
48                                             WAPI_HANDLE_CAP_WAIT |
49                                             WAPI_HANDLE_CAP_SIGNAL |
50                                             WAPI_HANDLE_CAP_OWN);
51 }
52
53 static void mutex_close_shared (gpointer handle)
54 {
55         struct _WapiHandle_mutex *mutex_handle;
56         gboolean ok;
57         
58         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
59                                 (gpointer *)&mutex_handle, NULL);
60         if(ok==FALSE) {
61                 g_warning (G_GNUC_PRETTY_FUNCTION
62                            ": error looking up mutex handle %p", handle);
63                 return;
64         }
65         
66 #ifdef DEBUG
67         g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p", handle);
68 #endif
69
70         if(mutex_handle->sharedns.name!=0) {
71                 _wapi_handle_scratch_delete (mutex_handle->sharedns.name);
72                 mutex_handle->sharedns.name=0;
73         }
74 }
75
76 static void mutex_signal(gpointer handle)
77 {
78         ReleaseMutex(handle);
79 }
80
81 static void mutex_own (gpointer handle)
82 {
83         struct _WapiHandle_mutex *mutex_handle;
84         gboolean ok;
85         
86         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
87                                 (gpointer *)&mutex_handle, NULL);
88         if(ok==FALSE) {
89                 g_warning (G_GNUC_PRETTY_FUNCTION
90                            ": error looking up mutex handle %p", handle);
91                 return;
92         }
93         
94 #ifdef DEBUG
95         g_message(G_GNUC_PRETTY_FUNCTION ": owning mutex handle %p", handle);
96 #endif
97
98         _wapi_handle_set_signal_state (handle, FALSE, FALSE);
99         
100         mutex_handle->pid=getpid ();
101         mutex_handle->tid=pthread_self ();
102         mutex_handle->recursion++;
103
104 #ifdef DEBUG
105         g_message (G_GNUC_PRETTY_FUNCTION
106                    ": mutex handle %p locked %d times by %d:%ld", handle,
107                    mutex_handle->recursion, mutex_handle->pid,
108                    mutex_handle->tid);
109 #endif
110 }
111
112 static gboolean mutex_is_owned (gpointer handle)
113 {
114         struct _WapiHandle_mutex *mutex_handle;
115         gboolean ok;
116         
117         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
118                                 (gpointer *)&mutex_handle, NULL);
119         if(ok==FALSE) {
120                 g_warning (G_GNUC_PRETTY_FUNCTION
121                            ": error looking up mutex handle %p", handle);
122                 return(FALSE);
123         }
124         
125 #ifdef DEBUG
126         g_message(G_GNUC_PRETTY_FUNCTION
127                   ": testing ownership mutex handle %p", handle);
128 #endif
129
130         if(mutex_handle->recursion>0 &&
131            mutex_handle->pid==getpid () &&
132            mutex_handle->tid==pthread_self ()) {
133 #ifdef DEBUG
134                 g_message (G_GNUC_PRETTY_FUNCTION
135                            ": mutex handle %p owned by %d:%ld", handle,
136                            getpid (), pthread_self ());
137 #endif
138
139                 return(TRUE);
140         } else {
141 #ifdef DEBUG
142                 g_message (G_GNUC_PRETTY_FUNCTION
143                            ": mutex handle %p not owned by %d:%ld", handle,
144                            getpid (), pthread_self ());
145 #endif
146
147                 return(FALSE);
148         }
149 }
150
151 /**
152  * CreateMutex:
153  * @security: Ignored for now.
154  * @owned: If %TRUE, the mutex is created with the calling thread
155  * already owning the mutex.
156  * @name:Pointer to a string specifying the name of this mutex, or
157  * %NULL.
158  *
159  * Creates a new mutex handle.  A mutex is signalled when no thread
160  * owns it.  A thread acquires ownership of the mutex by waiting for
161  * it with WaitForSingleObject() or WaitForMultipleObjects().  A
162  * thread relinquishes ownership with ReleaseMutex().
163  *
164  * A thread that owns a mutex can specify the same mutex in repeated
165  * wait function calls without blocking.  The thread must call
166  * ReleaseMutex() an equal number of times to release the mutex.
167  *
168  * Return value: A new handle, or %NULL on error.
169  */
170 gpointer CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned,
171                         const gunichar2 *name)
172 {
173         struct _WapiHandle_mutex *mutex_handle;
174         gpointer handle;
175         gboolean ok;
176         gchar *utf8_name;
177         int thr_ret;
178         gpointer ret = NULL;
179         
180         mono_once (&mutex_ops_once, mutex_ops_init);
181
182         /* w32 seems to guarantee that opening named mutexes can't
183          * race each other
184          */
185         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
186                               (void *)&named_mutex_mutex);
187         thr_ret = mono_mutex_lock (&named_mutex_mutex);
188         g_assert (thr_ret == 0);
189
190         if(name!=NULL) {
191                 utf8_name=g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
192         } else {
193                 utf8_name=NULL;
194         }
195         
196 #ifdef DEBUG
197         g_message (G_GNUC_PRETTY_FUNCTION ": Creating mutex (name [%s])",
198                    utf8_name==NULL?"<unnamed>":utf8_name);
199 #endif
200         
201         if(name!=NULL) {
202                 handle=_wapi_search_handle_namespace (
203                         WAPI_HANDLE_MUTEX, utf8_name,
204                         (gpointer *)&mutex_handle, NULL);
205                 if(handle==_WAPI_HANDLE_INVALID) {
206                         /* The name has already been used for a different
207                          * object.
208                          */
209                         g_free (utf8_name);
210                         SetLastError (ERROR_INVALID_HANDLE);
211                         goto cleanup;
212                 } else if (handle!=NULL) {
213                         g_free (utf8_name);
214                         _wapi_handle_ref (handle);
215                         ret = handle;
216                         goto cleanup;
217                 }
218                 /* Otherwise fall through to create the mutex. */
219         }
220         
221         handle=_wapi_handle_new (WAPI_HANDLE_MUTEX);
222         if(handle==_WAPI_HANDLE_INVALID) {
223                 g_warning (G_GNUC_PRETTY_FUNCTION
224                            ": error creating mutex handle");
225                 if(utf8_name!=NULL) {
226                         g_free (utf8_name);
227                 }
228                 goto cleanup;
229         }
230
231         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
232                               handle);
233         thr_ret = _wapi_handle_lock_handle (handle);
234         g_assert (thr_ret == 0);
235         
236         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
237                                 (gpointer *)&mutex_handle, NULL);
238         if(ok==FALSE) {
239                 g_warning (G_GNUC_PRETTY_FUNCTION
240                            ": error looking up mutex handle %p", handle);
241                 if(utf8_name!=NULL) {
242                         g_free (utf8_name);
243                 }
244                 
245                 goto handle_cleanup;
246         }
247         ret = handle;
248         
249         if(utf8_name!=NULL) {
250                 mutex_handle->sharedns.name=_wapi_handle_scratch_store (
251                         utf8_name, strlen (utf8_name));
252         }
253         
254         if(owned==TRUE) {
255                 mutex_own (handle);
256         } else {
257                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
258         }
259         
260 #ifdef DEBUG
261         g_message (G_GNUC_PRETTY_FUNCTION ": returning mutex handle %p",
262                    handle);
263 #endif
264
265         if(utf8_name!=NULL) {
266                 g_free (utf8_name);
267         }
268
269 handle_cleanup:
270         thr_ret = _wapi_handle_unlock_handle (handle);
271         g_assert (thr_ret == 0);
272         pthread_cleanup_pop (0);
273         
274 cleanup:
275         thr_ret = mono_mutex_unlock (&named_mutex_mutex);
276         g_assert (thr_ret == 0);
277         pthread_cleanup_pop (0);
278         
279         return(ret);
280 }
281
282 /**
283  * ReleaseMutex:
284  * @handle: The mutex handle.
285  *
286  * Releases ownership if the mutex handle @handle.
287  *
288  * Return value: %TRUE on success, %FALSE otherwise.  This function
289  * fails if the calling thread does not own the mutex @handle.
290  */
291 gboolean ReleaseMutex(gpointer handle)
292 {
293         struct _WapiHandle_mutex *mutex_handle;
294         gboolean ok;
295         pthread_t tid=pthread_self();
296         pid_t pid=getpid ();
297         int thr_ret;
298         gboolean ret = FALSE;
299         
300         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
301                                 (gpointer *)&mutex_handle, NULL);
302         if(ok==FALSE) {
303                 g_warning (G_GNUC_PRETTY_FUNCTION
304                            ": error looking up mutex handle %p", handle);
305                 return(FALSE);
306         }
307
308         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
309                               handle);
310         thr_ret = _wapi_handle_lock_handle (handle);
311         g_assert (thr_ret == 0);
312         
313 #ifdef DEBUG
314         g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex handle %p",
315                   handle);
316 #endif
317
318         if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) {
319 #ifdef DEBUG
320                 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);
321 #endif
322
323                 goto cleanup;
324         }
325         ret = TRUE;
326         
327         /* OK, we own this mutex */
328         mutex_handle->recursion--;
329         
330         if(mutex_handle->recursion==0) {
331 #ifdef DEBUG
332                 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p",
333                           handle);
334 #endif
335
336                 mutex_handle->pid=0;
337                 mutex_handle->tid=0;
338                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
339         }
340
341 cleanup:
342         thr_ret = _wapi_handle_unlock_handle (handle);
343         g_assert (thr_ret == 0);
344         pthread_cleanup_pop (0);
345         
346         return(ret);
347 }