d7a3c03dbd48c9b67aefe00deff653925eaaf192
[mono.git] / mono / metadata / w32mutex-unix.c
1 /*
2  * w32mutex-unix.c: Runtime support for managed Mutex on Unix
3  *
4  * Author:
5  *      Ludovic Henry (luhenry@microsoft.com)
6  *
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "w32mutex.h"
11 #include "w32mutex-utils.h"
12
13 #include <pthread.h>
14
15 #include "w32handle-namespace.h"
16 #include "mono/io-layer/io-layer.h"
17 #include "mono/utils/mono-logger-internals.h"
18 #include "mono/utils/mono-threads.h"
19 #include "mono/utils/w32handle.h"
20
21 typedef struct {
22         MonoNativeThreadId tid;
23         guint32 recursion;
24 } MonoW32HandleMutex;
25
26 struct MonoW32HandleNamedMutex {
27         MonoW32HandleMutex m;
28         MonoW32HandleNamespace sharedns;
29 };
30
31 static gboolean
32 mutex_handle_own (gpointer handle, MonoW32HandleType type)
33 {
34         MonoW32HandleMutex *mutex_handle;
35
36         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
37                 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
38                 return FALSE;
39         }
40
41         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p, tid %p, recursion %u",
42                 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
43
44         mono_thread_info_own_mutex (mono_thread_info_current (), handle);
45
46         mutex_handle->tid = pthread_self ();
47         mutex_handle->recursion++;
48
49         mono_w32handle_set_signal_state (handle, FALSE, FALSE);
50
51         return TRUE;
52 }
53
54 static gboolean
55 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
56 {
57         MonoW32HandleMutex *mutex_handle;
58
59         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
60                 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
61                 return FALSE;
62         }
63
64         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
65                 __func__, mono_w32handle_ops_typename (type), handle);
66
67         if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
68                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
69                         __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self ());
70                 return TRUE;
71         } else {
72                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, but locked %d times by %p",
73                         __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self (), mutex_handle->recursion, (gpointer) mutex_handle->tid);
74                 return FALSE;
75         }
76 }
77
78 static void mutex_signal(gpointer handle)
79 {
80         ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
81 }
82
83 static gboolean mutex_own (gpointer handle)
84 {
85         return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX);
86 }
87
88 static gboolean mutex_is_owned (gpointer handle)
89 {
90         
91         return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
92 }
93
94 static void namedmutex_signal (gpointer handle)
95 {
96         ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
97 }
98
99 /* NB, always called with the shared handle lock held */
100 static gboolean namedmutex_own (gpointer handle)
101 {
102         return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX);
103 }
104
105 static gboolean namedmutex_is_owned (gpointer handle)
106 {
107         return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
108 }
109
110 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
111 {
112         /* If the mutex is not currently owned, do nothing and let the
113          * usual wait carry on.  If it is owned, check that the owner
114          * is still alive; if it isn't we override the previous owner
115          * and assume that process exited abnormally and failed to
116          * clean up.
117          */
118         MonoW32HandleMutex *mutex_handle;
119
120         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
121                 g_warning ("%s: error looking up %s handle %p",
122                         __func__, mono_w32handle_ops_typename (type), handle);
123                 return;
124         }
125
126         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
127                 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
128 }
129
130 /* The shared state is not locked when prewait methods are called */
131 static void mutex_prewait (gpointer handle)
132 {
133         mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
134 }
135
136 /* The shared state is not locked when prewait methods are called */
137 static void namedmutex_prewait (gpointer handle)
138 {
139         mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
140 }
141
142 static void mutex_details (gpointer data)
143 {
144         MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
145         
146 #ifdef PTHREAD_POINTER_ID
147         g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
148 #else
149         g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
150 #endif
151 }
152
153 static void namedmutex_details (gpointer data)
154 {
155         MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
156         
157 #ifdef PTHREAD_POINTER_ID
158         g_print ("own: %5p, count: %5u, name: \"%s\"",
159                 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
160 #else
161         g_print ("own: %5ld, count: %5u, name: \"%s\"",
162                 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
163 #endif
164 }
165
166 static const gchar* mutex_typename (void)
167 {
168         return "Mutex";
169 }
170
171 static gsize mutex_typesize (void)
172 {
173         return sizeof (MonoW32HandleMutex);
174 }
175
176 static const gchar* namedmutex_typename (void)
177 {
178         return "N.Mutex";
179 }
180
181 static gsize namedmutex_typesize (void)
182 {
183         return sizeof (MonoW32HandleNamedMutex);
184 }
185
186 void
187 mono_w32mutex_init (void)
188 {
189         static MonoW32HandleOps mutex_ops = {
190                 NULL,                   /* close */
191                 mutex_signal,           /* signal */
192                 mutex_own,              /* own */
193                 mutex_is_owned,         /* is_owned */
194                 NULL,                   /* special_wait */
195                 mutex_prewait,                  /* prewait */
196                 mutex_details,  /* details */
197                 mutex_typename, /* typename */
198                 mutex_typesize, /* typesize */
199         };
200
201         static MonoW32HandleOps namedmutex_ops = {
202                 NULL,                   /* close */
203                 namedmutex_signal,      /* signal */
204                 namedmutex_own,         /* own */
205                 namedmutex_is_owned,    /* is_owned */
206                 NULL,                   /* special_wait */
207                 namedmutex_prewait,     /* prewait */
208                 namedmutex_details,     /* details */
209                 namedmutex_typename,    /* typename */
210                 namedmutex_typesize,    /* typesize */
211         };
212
213         mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX,      &mutex_ops);
214         mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
215
216         mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
217                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
218         mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
219                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
220 }
221
222 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
223 {
224         gpointer handle;
225         int thr_ret;
226
227         mutex_handle->tid = 0;
228         mutex_handle->recursion = 0;
229
230         handle = mono_w32handle_new (type, mutex_handle);
231         if (handle == INVALID_HANDLE_VALUE) {
232                 g_warning ("%s: error creating %s handle",
233                         __func__, mono_w32handle_ops_typename (type));
234                 SetLastError (ERROR_GEN_FAILURE);
235                 return NULL;
236         }
237
238         thr_ret = mono_w32handle_lock_handle (handle);
239         g_assert (thr_ret == 0);
240
241         if (owned)
242                 mutex_handle_own (handle, type);
243         else
244                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
245
246         thr_ret = mono_w32handle_unlock_handle (handle);
247         g_assert (thr_ret == 0);
248
249         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
250                 __func__, mono_w32handle_ops_typename (type), handle);
251
252         return handle;
253 }
254
255 static gpointer mutex_create (gboolean owned)
256 {
257         MonoW32HandleMutex mutex_handle;
258         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
259                 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX));
260         return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
261 }
262
263 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
264 {
265         gpointer handle;
266         gchar *utf8_name;
267
268         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
269                 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX));
270
271         /* w32 seems to guarantee that opening named objects can't race each other */
272         mono_w32handle_namespace_lock ();
273
274         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
275
276         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
277         if (handle == INVALID_HANDLE_VALUE) {
278                 /* The name has already been used for a different object. */
279                 handle = NULL;
280                 SetLastError (ERROR_INVALID_HANDLE);
281         } else if (handle) {
282                 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
283                 SetLastError (ERROR_ALREADY_EXISTS);
284
285                 /* this is used as creating a new handle */
286                 mono_w32handle_ref (handle);
287         } else {
288                 /* A new named mutex */
289                 MonoW32HandleNamedMutex namedmutex_handle;
290
291                 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
292                 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
293
294                 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
295         }
296
297         g_free (utf8_name);
298
299         mono_w32handle_namespace_unlock ();
300
301         return handle;
302 }
303
304 gpointer
305 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
306 {
307         gpointer mutex;
308
309         *created = TRUE;
310
311         /* Need to blow away any old errors here, because code tests
312          * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
313          * was freshly created */
314         SetLastError (ERROR_SUCCESS);
315
316         if (!name) {
317                 mutex = mutex_create (owned);
318         } else {
319                 mutex = namedmutex_create (owned, mono_string_chars (name));
320
321                 if (GetLastError () == ERROR_ALREADY_EXISTS)
322                         *created = FALSE;
323         }
324
325         return mutex;
326 }
327
328 MonoBoolean
329 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
330 {
331         MonoW32HandleType type;
332         MonoW32HandleMutex *mutex_handle;
333         pthread_t tid;
334         int thr_ret;
335         gboolean ret;
336
337         if (handle == NULL) {
338                 SetLastError (ERROR_INVALID_HANDLE);
339                 return FALSE;
340         }
341
342         switch (type = mono_w32handle_get_type (handle)) {
343         case MONO_W32HANDLE_MUTEX:
344         case MONO_W32HANDLE_NAMEDMUTEX:
345                 break;
346         default:
347                 SetLastError (ERROR_INVALID_HANDLE);
348                 return FALSE;
349         }
350
351         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
352                 g_warning ("%s: error looking up %s handle %p",
353                         __func__, mono_w32handle_ops_typename (type), handle);
354                 return FALSE;
355         }
356
357         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
358                 __func__, mono_w32handle_ops_typename (type), handle);
359
360         thr_ret = mono_w32handle_lock_handle (handle);
361         g_assert (thr_ret == 0);
362
363         tid = pthread_self ();
364
365         if (!pthread_equal (mutex_handle->tid, tid)) {
366                 ret = FALSE;
367
368                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
369                         __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid);
370         } else {
371                 ret = TRUE;
372
373                 /* OK, we own this mutex */
374                 mutex_handle->recursion--;
375
376                 if (mutex_handle->recursion == 0) {
377                         mono_thread_info_disown_mutex (mono_thread_info_current (), handle);
378
379                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p",
380                                 __func__, mono_w32handle_ops_typename (type), handle);
381
382                         mutex_handle->tid = 0;
383                         mono_w32handle_set_signal_state (handle, TRUE, FALSE);
384                 }
385         }
386
387         thr_ret = mono_w32handle_unlock_handle (handle);
388         g_assert (thr_ret == 0);
389
390         return ret;
391 }
392
393 gpointer
394 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
395 {
396         gpointer handle;
397         gchar *utf8_name;
398
399         *error = ERROR_SUCCESS;
400
401         /* w32 seems to guarantee that opening named objects can't race each other */
402         mono_w32handle_namespace_lock ();
403
404         utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
405
406         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
407                 __func__, utf8_name);
408
409         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
410         if (handle == INVALID_HANDLE_VALUE) {
411                 /* The name has already been used for a different object. */
412                 *error = ERROR_INVALID_HANDLE;
413                 goto cleanup;
414         } else if (!handle) {
415                 /* This name doesn't exist */
416                 *error = ERROR_FILE_NOT_FOUND;
417                 goto cleanup;
418         }
419
420         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
421                 __func__, handle);
422
423 cleanup:
424         g_free (utf8_name);
425
426         mono_w32handle_namespace_unlock ();
427
428         return handle;
429 }
430
431 void
432 mono_w32mutex_abandon (gpointer handle, MonoNativeThreadId tid)
433 {
434         MonoW32HandleType type;
435         MonoW32HandleMutex *mutex_handle;
436         int thr_ret;
437
438         switch (type = mono_w32handle_get_type (handle)) {
439         case MONO_W32HANDLE_MUTEX:
440         case MONO_W32HANDLE_NAMEDMUTEX:
441                 break;
442         default:
443                 g_assert_not_reached ();
444         }
445
446         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
447                 g_warning ("%s: error looking up %s handle %p",
448                         __func__, mono_w32handle_ops_typename (type), handle);
449                 return;
450         }
451
452         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandon %s handle %p",
453                 __func__, mono_w32handle_ops_typename (type), handle);
454
455         thr_ret = mono_w32handle_lock_handle (handle);
456         g_assert (thr_ret == 0);
457
458         if (pthread_equal (mutex_handle->tid, tid)) {
459                 mutex_handle->recursion = 0;
460                 mutex_handle->tid = 0;
461
462                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
463
464                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
465                         __func__, mono_w32handle_ops_typename (type), handle);
466         }
467
468         thr_ret = mono_w32handle_unlock_handle (handle);
469         g_assert (thr_ret == 0);
470 }
471
472 MonoW32HandleNamespace*
473 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
474 {
475         return &mutex->sharedns;
476 }