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 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
);
54 thread_disown_mutex (MonoInternalThread
*internal
, gpointer handle
)
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
);
64 mono_w32handle_unref (handle
);
68 mutex_handle_signal (gpointer handle
, MonoW32HandleType type
, MonoW32HandleMutex
*mutex_handle
)
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
);
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
);
100 mutex_handle_own (gpointer handle
, MonoW32HandleType type
, gboolean
*abandoned
)
102 MonoW32HandleMutex
*mutex_handle
;
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
);
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
++;
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
;
129 mono_w32handle_set_signal_state (handle
, FALSE
, FALSE
);
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
);
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 ());
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
);
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
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
);
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
);
228 g_print ("own: %5ld, count: %5u", mut
->tid
, mut
->recursion
);
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
);
240 g_print ("own: %5ld, count: %5u, name: \"%s\"",
241 namedmut
->m
.tid
, namedmut
->m
.recursion
, namedmut
->sharedns
.name
);
245 static const gchar
* mutex_typename (void)
250 static gsize
mutex_typesize (void)
252 return sizeof (MonoW32HandleMutex
);
255 static const gchar
* namedmutex_typename (void)
260 static gsize
namedmutex_typesize (void)
262 return sizeof (MonoW32HandleNamedMutex
);
266 mono_w32mutex_init (void)
268 static MonoW32HandleOps mutex_ops
= {
270 mutex_signal
, /* signal */
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
= {
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
)
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
);
318 mono_w32handle_lock_handle (handle
);
321 mutex_handle_own (handle
, type
, &abandoned
);
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
);
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
)
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. */
357 mono_w32error_set_last (ERROR_INVALID_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 */
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 ();
380 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned
, MonoStringHandle name
, MonoBoolean
*created
, MonoError
*error
)
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
);
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
)
409 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle
)
411 MonoW32HandleType type
;
412 MonoW32HandleMutex
*mutex_handle
;
416 if (handle
== NULL
) {
417 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
421 switch (type
= mono_w32handle_get_type (handle
)) {
422 case MONO_W32HANDLE_MUTEX
:
423 case MONO_W32HANDLE_NAMEDMUTEX
:
426 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
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
);
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
446 } else if (!pthread_equal (mutex_handle
->tid
, tid
)) {
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
);
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
);
474 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name
, gint32 rights G_GNUC_UNUSED
, gint32
*err
, MonoError
*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
);
485 mono_w32mutex_open (const gchar
* utf8_name
, gint32 right G_GNUC_UNUSED
, gint32
*error
)
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
;
502 } else if (!handle
) {
503 /* This name doesn't exist */
504 *error
= ERROR_FILE_NOT_FOUND
;
508 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: returning named mutex handle %p",
512 mono_w32handle_namespace_unlock ();
518 mono_w32mutex_abandon (void)
520 MonoInternalThread
*internal
;
522 g_assert (mono_thread_internal_current_is_attached ());
524 internal
= mono_thread_internal_current ();
527 if (!internal
->owned_mutexes
)
530 while (internal
->owned_mutexes
->len
) {
531 MonoW32HandleType type
;
532 MonoW32HandleMutex
*mutex_handle
;
533 MonoNativeThreadId tid
;
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
:
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
;