2002-04-30 Dick Porter <dick@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 #include <unistd.h>
6
7 #include <mono/io-layer/wapi.h>
8 #include <mono/io-layer/wapi-private.h>
9 #include <mono/io-layer/wait-private.h>
10 #include <mono/io-layer/misc-private.h>
11 #include <mono/io-layer/handles-private.h>
12 #include <mono/io-layer/mono-mutex.h>
13 #include <mono/io-layer/mutex-private.h>
14
15 #undef DEBUG
16
17 static void mutex_close(gpointer handle);
18 static void mutex_signal(gpointer handle);
19 static void mutex_own (gpointer handle);
20 static gboolean mutex_is_owned (gpointer handle);
21
22 static struct _WapiHandleOps mutex_ops = {
23         mutex_close,            /* close */
24         NULL,                   /* getfiletype */
25         NULL,                   /* readfile */
26         NULL,                   /* writefile */
27         NULL,                   /* flushfile */
28         NULL,                   /* seek */
29         NULL,                   /* setendoffile */
30         NULL,                   /* getfilesize */
31         NULL,                   /* getfiletime */
32         NULL,                   /* setfiletime */
33         mutex_signal,           /* signal */
34         mutex_own,              /* own */
35         mutex_is_owned,         /* is_owned */
36 };
37
38 static pthread_once_t mutex_ops_once=PTHREAD_ONCE_INIT;
39
40 static void mutex_ops_init (void)
41 {
42         _wapi_handle_register_ops (WAPI_HANDLE_MUTEX, &mutex_ops);
43         _wapi_handle_register_capabilities (WAPI_HANDLE_MUTEX,
44                                             WAPI_HANDLE_CAP_WAIT |
45                                             WAPI_HANDLE_CAP_SIGNAL |
46                                             WAPI_HANDLE_CAP_OWN);
47 }
48
49 static void mutex_close(gpointer handle)
50 {
51         struct _WapiHandle_mutex *mutex_handle;
52         gboolean ok;
53         
54         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
55                                 (gpointer *)&mutex_handle, NULL);
56         if(ok==FALSE) {
57                 g_warning (G_GNUC_PRETTY_FUNCTION
58                            ": error looking up mutex handle %p", handle);
59                 return;
60         }
61         
62 #ifdef DEBUG
63         g_message(G_GNUC_PRETTY_FUNCTION ": closing mutex handle %p", handle);
64 #endif
65
66         if(mutex_handle->name!=0) {
67                 _wapi_handle_scratch_delete (mutex_handle->name);
68                 mutex_handle->name=0;
69         }
70 }
71
72 static void mutex_signal(gpointer handle)
73 {
74         ReleaseMutex(handle);
75 }
76
77 static void mutex_own (gpointer handle)
78 {
79         struct _WapiHandle_mutex *mutex_handle;
80         gboolean ok;
81         
82         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
83                                 (gpointer *)&mutex_handle, NULL);
84         if(ok==FALSE) {
85                 g_warning (G_GNUC_PRETTY_FUNCTION
86                            ": error looking up mutex handle %p", handle);
87                 return;
88         }
89         
90 #ifdef DEBUG
91         g_message(G_GNUC_PRETTY_FUNCTION ": owning mutex handle %p", handle);
92 #endif
93
94         _wapi_handle_set_signal_state (handle, FALSE, FALSE);
95         
96         mutex_handle->pid=getpid ();
97         mutex_handle->tid=pthread_self ();
98         mutex_handle->recursion++;
99
100 #ifdef DEBUG
101         g_message (G_GNUC_PRETTY_FUNCTION
102                    ": mutex handle %p locked %d times by %d:%ld", handle,
103                    mutex_handle->recursion, mutex_handle->pid,
104                    mutex_handle->tid);
105 #endif
106 }
107
108 static gboolean mutex_is_owned (gpointer handle)
109 {
110         struct _WapiHandle_mutex *mutex_handle;
111         gboolean ok;
112         
113         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
114                                 (gpointer *)&mutex_handle, NULL);
115         if(ok==FALSE) {
116                 g_warning (G_GNUC_PRETTY_FUNCTION
117                            ": error looking up mutex handle %p", handle);
118                 return(FALSE);
119         }
120         
121 #ifdef DEBUG
122         g_message(G_GNUC_PRETTY_FUNCTION
123                   ": testing ownership mutex handle %p", handle);
124 #endif
125
126         if(mutex_handle->recursion>0 &&
127            mutex_handle->pid==getpid () &&
128            mutex_handle->tid==pthread_self ()) {
129 #ifdef DEBUG
130                 g_message (G_GNUC_PRETTY_FUNCTION
131                            ": mutex handle %p owned by %d:%ld", handle,
132                            getpid (), pthread_self ());
133 #endif
134
135                 return(TRUE);
136         } else {
137 #ifdef DEBUG
138                 g_message (G_GNUC_PRETTY_FUNCTION
139                            ": mutex handle %p not owned by %d:%ld", handle,
140                            getpid (), pthread_self ());
141 #endif
142
143                 return(FALSE);
144         }
145 }
146
147 /**
148  * CreateMutex:
149  * @security: Ignored for now.
150  * @owned: If %TRUE, the mutex is created with the calling thread
151  * already owning the mutex.
152  * @name:Pointer to a string specifying the name of this mutex, or
153  * %NULL.
154  *
155  * Creates a new mutex handle.  A mutex is signalled when no thread
156  * owns it.  A thread acquires ownership of the mutex by waiting for
157  * it with WaitForSingleObject() or WaitForMultipleObjects().  A
158  * thread relinquishes ownership with ReleaseMutex().
159  *
160  * A thread that owns a mutex can specify the same mutex in repeated
161  * wait function calls without blocking.  The thread must call
162  * ReleaseMutex() an equal number of times to release the mutex.
163  *
164  * Return value: A new handle, or %NULL on error.
165  */
166 gpointer CreateMutex(WapiSecurityAttributes *security G_GNUC_UNUSED, gboolean owned,
167                         const guchar *name)
168 {
169         struct _WapiHandle_mutex *mutex_handle;
170         gpointer handle;
171         gboolean ok;
172         
173         pthread_once (&mutex_ops_once, mutex_ops_init);
174         
175         handle=_wapi_handle_new (WAPI_HANDLE_MUTEX);
176         if(handle==_WAPI_HANDLE_INVALID) {
177                 g_warning (G_GNUC_PRETTY_FUNCTION
178                            ": error creating mutex handle");
179                 return(NULL);
180         }
181
182         _wapi_handle_lock_handle (handle);
183         
184         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
185                                 (gpointer *)&mutex_handle, NULL);
186         if(ok==FALSE) {
187                 g_warning (G_GNUC_PRETTY_FUNCTION
188                            ": error looking up mutex handle %p", handle);
189                 _wapi_handle_unlock_handle (handle);
190                 return(NULL);
191         }
192
193         if(name!=NULL) {
194                 mutex_handle->name=_wapi_handle_scratch_store (name,
195                                                                strlen (name));
196         }
197         
198         if(owned==TRUE) {
199                 mutex_own (handle);
200         } else {
201                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
202         }
203         
204 #ifdef DEBUG
205         g_message (G_GNUC_PRETTY_FUNCTION ": returning mutex handle %p",
206                    handle);
207 #endif
208
209         _wapi_handle_unlock_handle (handle);
210
211         return(handle);
212 }
213
214 /**
215  * ReleaseMutex:
216  * @handle: The mutex handle.
217  *
218  * Releases ownership if the mutex handle @handle.
219  *
220  * Return value: %TRUE on success, %FALSE otherwise.  This function
221  * fails if the calling thread does not own the mutex @handle.
222  */
223 gboolean ReleaseMutex(gpointer handle)
224 {
225         struct _WapiHandle_mutex *mutex_handle;
226         gboolean ok;
227         pthread_t tid=pthread_self();
228         pid_t pid=getpid ();
229         
230         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_MUTEX,
231                                 (gpointer *)&mutex_handle, NULL);
232         if(ok==FALSE) {
233                 g_warning (G_GNUC_PRETTY_FUNCTION
234                            ": error looking up mutex handle %p", handle);
235                 return(FALSE);
236         }
237
238         _wapi_handle_lock_handle (handle);
239         
240 #ifdef DEBUG
241         g_message(G_GNUC_PRETTY_FUNCTION ": Releasing mutex handle %p",
242                   handle);
243 #endif
244
245         if(mutex_handle->tid!=tid || mutex_handle->pid!=pid) {
246 #ifdef DEBUG
247                 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);
248 #endif
249
250                 _wapi_handle_unlock_handle (handle);
251                 return(FALSE);
252         }
253
254         /* OK, we own this mutex */
255         mutex_handle->recursion--;
256         
257         if(mutex_handle->recursion==0) {
258 #ifdef DEBUG
259                 g_message(G_GNUC_PRETTY_FUNCTION ": Unlocking mutex handle %p",
260                           handle);
261 #endif
262
263                 mutex_handle->pid=0;
264                 mutex_handle->tid=0;
265                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
266         }
267
268         _wapi_handle_unlock_handle (handle);
269         
270         return(TRUE);
271 }