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