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