3 * Runtime support for managed Mutex on Unix
6 * Ludovic Henry (luhenry@microsoft.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
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"
25 MonoNativeThreadId tid;
30 struct MonoW32HandleNamedMutex {
32 MonoW32HandleNamespace sharedns;
36 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error);
39 thread_own_mutex (MonoInternalThread *internal, gpointer handle)
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));
45 if (!internal->owned_mutexes)
46 internal->owned_mutexes = g_ptr_array_new ();
48 g_ptr_array_add (internal->owned_mutexes, mono_w32handle_duplicate (handle));
52 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
56 g_assert (mono_thread_internal_is_current (internal));
58 g_assert (internal->owned_mutexes);
59 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
62 mono_w32handle_close (handle);
66 mutex_handle_signal (gpointer handle, MonoW32HandleType type, MonoW32HandleMutex *mutex_handle)
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);
73 tid = pthread_self ();
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);
82 /* OK, we own this mutex */
83 mutex_handle->recursion--;
85 if (mutex_handle->recursion == 0) {
86 thread_disown_mutex (mono_thread_internal_current (), handle);
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);
91 mutex_handle->tid = 0;
92 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
98 mutex_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
100 MonoW32HandleMutex *mutex_handle;
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);
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");
112 if (mutex_handle->recursion != 0) {
113 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
114 mutex_handle->recursion++;
116 mutex_handle->tid = pthread_self ();
117 mutex_handle->recursion = 1;
119 thread_own_mutex (mono_thread_internal_current (), handle);
122 if (mutex_handle->abandoned) {
123 mutex_handle->abandoned = FALSE;
127 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
133 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
135 MonoW32HandleMutex *mutex_handle;
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);
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);
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 ());
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);
156 static void mutex_signal(gpointer handle, gpointer handle_specific)
158 mutex_handle_signal (handle, MONO_W32HANDLE_MUTEX, (MonoW32HandleMutex*) handle_specific);
161 static gboolean mutex_own (gpointer handle, gboolean *abandoned)
163 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
166 static gboolean mutex_is_owned (gpointer handle)
168 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
171 static void namedmutex_signal (gpointer handle, gpointer handle_specific)
173 mutex_handle_signal (handle, MONO_W32HANDLE_NAMEDMUTEX, (MonoW32HandleMutex*) handle_specific);
176 /* NB, always called with the shared handle lock held */
177 static gboolean namedmutex_own (gpointer handle, gboolean *abandoned)
179 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
182 static gboolean namedmutex_is_owned (gpointer handle)
184 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
187 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
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
195 MonoW32HandleMutex *mutex_handle;
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);
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");
207 /* The shared state is not locked when prewait methods are called */
208 static void mutex_prewait (gpointer handle)
210 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
213 /* The shared state is not locked when prewait methods are called */
214 static void namedmutex_prewait (gpointer handle)
216 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
219 static void mutex_details (gpointer data)
221 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
223 #ifdef PTHREAD_POINTER_ID
224 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
226 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
230 static void namedmutex_details (gpointer data)
232 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
234 #ifdef PTHREAD_POINTER_ID
235 g_print ("own: %5p, count: %5u, name: \"%s\"",
236 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
238 g_print ("own: %5ld, count: %5u, name: \"%s\"",
239 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
243 static const gchar* mutex_typename (void)
248 static gsize mutex_typesize (void)
250 return sizeof (MonoW32HandleMutex);
253 static const gchar* namedmutex_typename (void)
258 static gsize namedmutex_typesize (void)
260 return sizeof (MonoW32HandleNamedMutex);
264 mono_w32mutex_init (void)
266 static MonoW32HandleOps mutex_ops = {
268 mutex_signal, /* signal */
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 */
278 static MonoW32HandleOps namedmutex_ops = {
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 */
290 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
291 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
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));
299 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
304 mutex_handle->tid = 0;
305 mutex_handle->recursion = 0;
306 mutex_handle->abandoned = FALSE;
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);
316 mono_w32handle_lock_handle (handle);
319 mutex_handle_own (handle, type, &abandoned);
321 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
323 mono_w32handle_unlock_handle (handle);
325 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
326 __func__, mono_w32handle_get_typename (type), handle);
331 static gpointer mutex_create (gboolean owned)
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);
339 static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name)
343 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
344 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
346 /* w32 seems to guarantee that opening named objects can't race each other */
347 mono_w32handle_namespace_lock ();
349 glong utf8_len = strlen (utf8_name);
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. */
355 mono_w32error_set_last (ERROR_INVALID_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);
360 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
362 /* A new named mutex */
363 MonoW32HandleNamedMutex namedmutex_handle;
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';
369 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
372 mono_w32handle_namespace_unlock ();
378 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error)
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);
390 if (MONO_HANDLE_IS_NULL (name)) {
391 mutex = mutex_create (owned);
393 gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
394 return_val_if_nok (error, NULL);
396 mutex = namedmutex_create (owned, utf8_name);
398 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
407 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
409 MonoW32HandleType type;
410 MonoW32HandleMutex *mutex_handle;
414 if (handle == NULL) {
415 mono_w32error_set_last (ERROR_INVALID_HANDLE);
419 switch (type = mono_w32handle_get_type (handle)) {
420 case MONO_W32HANDLE_MUTEX:
421 case MONO_W32HANDLE_NAMEDMUTEX:
424 mono_w32error_set_last (ERROR_INVALID_HANDLE);
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);
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);
437 mono_w32handle_lock_handle (handle);
439 tid = pthread_self ();
441 if (mutex_handle->abandoned) {
442 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
444 } else if (!pthread_equal (mutex_handle->tid, tid)) {
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);
452 /* OK, we own this mutex */
453 mutex_handle->recursion--;
455 if (mutex_handle->recursion == 0) {
456 thread_disown_mutex (mono_thread_internal_current (), handle);
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);
461 mutex_handle->tid = 0;
462 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
466 mono_w32handle_unlock_handle (handle);
472 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *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);
483 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error)
487 *error = ERROR_SUCCESS;
489 /* w32 seems to guarantee that opening named objects can't race each other */
490 mono_w32handle_namespace_lock ();
492 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
493 __func__, utf8_name);
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;
500 } else if (!handle) {
501 /* This name doesn't exist */
502 *error = ERROR_FILE_NOT_FOUND;
506 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
510 mono_w32handle_namespace_unlock ();
516 mono_w32mutex_abandon (void)
518 MonoInternalThread *internal;
520 g_assert (mono_thread_internal_current_is_attached ());
522 internal = mono_thread_internal_current ();
525 if (!internal->owned_mutexes)
528 while (internal->owned_mutexes->len) {
529 MonoW32HandleType type;
530 MonoW32HandleMutex *mutex_handle;
531 MonoNativeThreadId tid;
534 handle = g_ptr_array_index (internal->owned_mutexes, 0);
536 switch (type = mono_w32handle_get_type (handle)) {
537 case MONO_W32HANDLE_MUTEX:
538 case MONO_W32HANDLE_NAMEDMUTEX:
541 g_assert_not_reached ();
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);
549 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
550 __func__, mono_w32handle_get_typename (type), handle);
552 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
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);
558 mono_w32handle_lock_handle (handle);
560 mutex_handle->recursion = 0;
561 mutex_handle->tid = 0;
562 mutex_handle->abandoned = TRUE;
564 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
566 thread_disown_mutex (internal, handle);
568 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
569 __func__, mono_w32handle_get_typename (type), handle);
571 mono_w32handle_unlock_handle (handle);
574 g_ptr_array_free (internal->owned_mutexes, TRUE);
575 internal->owned_mutexes = NULL;
578 MonoW32HandleNamespace*
579 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
581 return &mutex->sharedns;