[w32handle] Fix deadlock on SignalAndWait (#4973)
[mono-project.git] / mono / metadata / w32mutex-unix.c
blob189b808ba22436aace20b71b1449db5ca1838f88
1 /**
2 * \file
3 * Runtime support for managed Mutex on Unix
5 * Author:
6 * Ludovic Henry (luhenry@microsoft.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
11 #include "w32mutex.h"
13 #include <pthread.h>
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"
22 #define MAX_PATH 260
24 typedef struct {
25 MonoNativeThreadId tid;
26 guint32 recursion;
27 gboolean abandoned;
28 } MonoW32HandleMutex;
30 struct MonoW32HandleNamedMutex {
31 MonoW32HandleMutex m;
32 MonoW32HandleNamespace sharedns;
35 gpointer
36 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error);
38 static void
39 thread_own_mutex (MonoInternalThread *internal, gpointer handle)
41 mono_w32handle_ref (handle);
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));
47 if (!internal->owned_mutexes)
48 internal->owned_mutexes = g_ptr_array_new ();
50 g_ptr_array_add (internal->owned_mutexes, handle);
53 static void
54 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
56 gboolean removed;
58 g_assert (mono_thread_internal_is_current (internal));
60 g_assert (internal->owned_mutexes);
61 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
62 g_assert (removed);
64 mono_w32handle_unref (handle);
67 static void
68 mutex_handle_signal (gpointer handle, MonoW32HandleType type, MonoW32HandleMutex *mutex_handle)
70 pthread_t tid;
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);
75 tid = pthread_self ();
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--;
87 if (mutex_handle->recursion == 0) {
88 thread_disown_mutex (mono_thread_internal_current (), handle);
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);
93 mutex_handle->tid = 0;
94 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
99 static gboolean
100 mutex_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
102 MonoW32HandleMutex *mutex_handle;
104 *abandoned = FALSE;
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;
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");
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;
121 thread_own_mutex (mono_thread_internal_current (), handle);
124 if (mutex_handle->abandoned) {
125 mutex_handle->abandoned = FALSE;
126 *abandoned = TRUE;
129 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
131 return TRUE;
134 static gboolean
135 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
137 MonoW32HandleMutex *mutex_handle;
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;
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);
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;
158 static void mutex_signal(gpointer handle, gpointer handle_specific)
160 mutex_handle_signal (handle, MONO_W32HANDLE_MUTEX, (MonoW32HandleMutex*) handle_specific);
163 static gboolean mutex_own (gpointer handle, gboolean *abandoned)
165 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
168 static gboolean mutex_is_owned (gpointer handle)
170 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
173 static void namedmutex_signal (gpointer handle, gpointer handle_specific)
175 mutex_handle_signal (handle, MONO_W32HANDLE_NAMEDMUTEX, (MonoW32HandleMutex*) handle_specific);
178 /* NB, always called with the shared handle lock held */
179 static gboolean namedmutex_own (gpointer handle, gboolean *abandoned)
181 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
184 static gboolean namedmutex_is_owned (gpointer handle)
186 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
189 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
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.
197 MonoW32HandleMutex *mutex_handle;
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;
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");
209 /* The shared state is not locked when prewait methods are called */
210 static void mutex_prewait (gpointer handle)
212 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
215 /* The shared state is not locked when prewait methods are called */
216 static void namedmutex_prewait (gpointer handle)
218 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
221 static void mutex_details (gpointer data)
223 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
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
232 static void namedmutex_details (gpointer data)
234 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
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
245 static const gchar* mutex_typename (void)
247 return "Mutex";
250 static gsize mutex_typesize (void)
252 return sizeof (MonoW32HandleMutex);
255 static const gchar* namedmutex_typename (void)
257 return "N.Mutex";
260 static gsize namedmutex_typesize (void)
262 return sizeof (MonoW32HandleNamedMutex);
265 void
266 mono_w32mutex_init (void)
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 */
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 */
292 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
293 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
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));
301 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
303 gpointer handle;
304 gboolean abandoned;
306 mutex_handle->tid = 0;
307 mutex_handle->recursion = 0;
308 mutex_handle->abandoned = FALSE;
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;
318 mono_w32handle_lock_handle (handle);
320 if (owned)
321 mutex_handle_own (handle, type, &abandoned);
322 else
323 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
325 mono_w32handle_unlock_handle (handle);
327 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
328 __func__, mono_w32handle_get_typename (type), handle);
330 return handle;
333 static gpointer mutex_create (gboolean owned)
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);
341 static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name)
343 gpointer handle;
345 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
346 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
348 /* w32 seems to guarantee that opening named objects can't race each other */
349 mono_w32handle_namespace_lock ();
351 glong utf8_len = strlen (utf8_name);
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);
362 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
363 } else {
364 /* A new named mutex */
365 MonoW32HandleNamedMutex namedmutex_handle;
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';
371 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
374 mono_w32handle_namespace_unlock ();
376 return handle;
379 gpointer
380 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error)
382 gpointer mutex;
384 error_init (error);
385 *created = TRUE;
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);
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);
398 mutex = namedmutex_create (owned, utf8_name);
400 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
401 *created = FALSE;
402 g_free (utf8_name);
405 return mutex;
408 MonoBoolean
409 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
411 MonoW32HandleType type;
412 MonoW32HandleMutex *mutex_handle;
413 pthread_t tid;
414 gboolean ret;
416 if (handle == NULL) {
417 mono_w32error_set_last (ERROR_INVALID_HANDLE);
418 return FALSE;
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;
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;
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);
439 mono_w32handle_lock_handle (handle);
441 tid = pthread_self ();
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;
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;
454 /* OK, we own this mutex */
455 mutex_handle->recursion--;
457 if (mutex_handle->recursion == 0) {
458 thread_disown_mutex (mono_thread_internal_current (), handle);
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);
463 mutex_handle->tid = 0;
464 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
468 mono_w32handle_unlock_handle (handle);
470 return ret;
473 gpointer
474 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error)
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;
484 gpointer
485 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error)
487 gpointer handle;
489 *error = ERROR_SUCCESS;
491 /* w32 seems to guarantee that opening named objects can't race each other */
492 mono_w32handle_namespace_lock ();
494 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
495 __func__, utf8_name);
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;
508 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
509 __func__, handle);
511 cleanup:
512 mono_w32handle_namespace_unlock ();
514 return handle;
517 void
518 mono_w32mutex_abandon (void)
520 MonoInternalThread *internal;
522 g_assert (mono_thread_internal_current_is_attached ());
524 internal = mono_thread_internal_current ();
525 g_assert (internal);
527 if (!internal->owned_mutexes)
528 return;
530 while (internal->owned_mutexes->len) {
531 MonoW32HandleType type;
532 MonoW32HandleMutex *mutex_handle;
533 MonoNativeThreadId tid;
534 gpointer handle;
536 handle = g_ptr_array_index (internal->owned_mutexes, 0);
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 ();
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);
551 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
552 __func__, mono_w32handle_get_typename (type), handle);
554 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
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);
560 mono_w32handle_lock_handle (handle);
562 mutex_handle->recursion = 0;
563 mutex_handle->tid = 0;
564 mutex_handle->abandoned = TRUE;
566 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
568 thread_disown_mutex (internal, handle);
570 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
571 __func__, mono_w32handle_get_typename (type), handle);
573 mono_w32handle_unlock_handle (handle);
576 g_ptr_array_free (internal->owned_mutexes, TRUE);
577 internal->owned_mutexes = NULL;
580 MonoW32HandleNamespace*
581 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
583 return &mutex->sharedns;