2004-01-29 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  * (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         
178         mono_once (&mutex_ops_once, mutex_ops_init);
179
180         if(name!=NULL) {
181                 utf8_name=g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
182                 /* w32 seems to guarantee that opening named mutexes can't
183                  * race each other
184                  */
185                 mono_mutex_lock (&named_mutex_mutex);
186         } else {
187                 utf8_name=NULL;
188         }
189         
190 #ifdef DEBUG
191         g_message (G_GNUC_PRETTY_FUNCTION ": Creating mutex (name [%s])",
192                    utf8_name==NULL?"<unnamed>":utf8_name);
193 #endif
194         
195         if(name!=NULL) {
196                 handle=_wapi_search_handle_namespace (
197                         WAPI_HANDLE_MUTEX, utf8_name,
198                         (gpointer *)&mutex_handle, NULL);
199                 if(handle==_WAPI_HANDLE_INVALID) {
200                         /* The name has already been used for a different
201                          * object.
202                          */
203                         g_free (utf8_name);
204                         mono_mutex_unlock (&named_mutex_mutex);
205                         SetLastError (ERROR_INVALID_HANDLE);
206                         return(NULL);
207                 } else if (handle!=NULL) {
208                         g_free (utf8_name);
209                         mono_mutex_unlock (&named_mutex_mutex);
210                         _wapi_handle_ref (handle);
211                         return(handle);
212                 }
213                 /* Otherwise fall through to create the mutex. */
214         }
215         
216         handle=_wapi_handle_new (WAPI_HANDLE_MUTEX);
217         if(handle==_WAPI_HANDLE_INVALID) {
218                 g_warning (G_GNUC_PRETTY_FUNCTION
219                            ": error creating mutex handle");
220                 if(utf8_name!=NULL) {
221                         g_free (utf8_name);
222                 }
223                 if(name!=NULL) {
224                         mono_mutex_unlock (&named_mutex_mutex);
225                 }
226                 
227                 return(NULL);
228         }
229
230         _wapi_handle_lock_handle (handle);
231         
232         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
233                                 (gpointer *)&mutex_handle, NULL);
234         if(ok==FALSE) {
235                 g_warning (G_GNUC_PRETTY_FUNCTION
236                            ": error looking up mutex handle %p", handle);
237                 if(utf8_name!=NULL) {
238                         g_free (utf8_name);
239                 }
240                 
241                 if(name!=NULL) {
242                         mono_mutex_unlock (&named_mutex_mutex);
243                 }
244                 
245                 _wapi_handle_unlock_handle (handle);
246                 return(NULL);
247         }
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         _wapi_handle_unlock_handle (handle);
266
267         if(utf8_name!=NULL) {
268                 g_free (utf8_name);
269         }
270         
271         if(name!=NULL) {
272                 mono_mutex_unlock (&named_mutex_mutex);
273         }
274                 
275         return(handle);
276 }
277
278 /**
279  * ReleaseMutex:
280  * @handle: The mutex handle.
281  *
282  * Releases ownership if the mutex handle @handle.
283  *
284  * Return value: %TRUE on success, %FALSE otherwise.  This function
285  * fails if the calling thread does not own the mutex @handle.
286  */
287 gboolean ReleaseMutex(gpointer handle)
288 {
289         struct _WapiHandle_mutex *mutex_handle;
290         gboolean ok;
291         pthread_t tid=pthread_self();
292         pid_t pid=getpid ();
293         
294         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
295                                 (gpointer *)&mutex_handle, NULL);
296         if(ok==FALSE) {
297                 g_warning (G_GNUC_PRETTY_FUNCTION
298                            ": error looking up mutex handle %p", handle);
299                 return(FALSE);
300         }
301
302         _wapi_handle_lock_handle (handle);
303         
304 #ifdef DEBUG
305         g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex handle %p",
306                   handle);
307 #endif
308
309         if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) {
310 #ifdef DEBUG
311                 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);
312 #endif
313
314                 _wapi_handle_unlock_handle (handle);
315                 return(FALSE);
316         }
317
318         /* OK, we own this mutex */
319         mutex_handle->recursion--;
320         
321         if(mutex_handle->recursion==0) {
322 #ifdef DEBUG
323                 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p",
324                           handle);
325 #endif
326
327                 mutex_handle->pid=0;
328                 mutex_handle->tid=0;
329                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
330         }
331
332         _wapi_handle_unlock_handle (handle);
333         
334         return(TRUE);
335 }