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