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"
21 #include "icall-decl.h"
26 MonoNativeThreadId tid
;
31 struct MonoW32HandleNamedMutex
{
33 MonoW32HandleNamespace sharedns
;
37 mono_w32mutex_open (const char* utf8_name
, gint32 rights G_GNUC_UNUSED
, gint32
*win32error
);
40 thread_own_mutex (MonoInternalThread
*internal
, gpointer handle
, MonoW32Handle
*handle_data
)
42 // Thread and InternalThread are pinned/mature.
43 // Take advantage of that and do not use handles here.
45 /* if we are not on the current thread, there is a
46 * race condition when allocating internal->owned_mutexes */
47 g_assert (mono_thread_internal_is_current (internal
));
49 if (!internal
->owned_mutexes
)
50 internal
->owned_mutexes
= g_ptr_array_new ();
52 g_ptr_array_add (internal
->owned_mutexes
, mono_w32handle_duplicate (handle_data
));
56 thread_disown_mutex (MonoInternalThread
*internal
, gpointer handle
)
58 // Thread and InternalThread are pinned/mature.
59 // Take advantage of that and do not use handles here.
62 g_assert (mono_thread_internal_is_current (internal
));
64 g_assert (internal
->owned_mutexes
);
65 removed
= g_ptr_array_remove (internal
->owned_mutexes
, handle
);
68 mono_w32handle_close (handle
);
72 mutex_handle_signal (MonoW32Handle
*handle_data
)
74 MonoW32HandleMutex
*mutex_handle
;
77 mutex_handle
= (MonoW32HandleMutex
*) handle_data
->specific
;
79 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: signalling %s handle %p, tid: %p recursion: %d",
80 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, (gpointer
) mutex_handle
->tid
, mutex_handle
->recursion
);
82 tid
= pthread_self ();
84 if (mutex_handle
->abandoned
) {
85 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: %s handle %p is abandoned",
86 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
);
87 } else if (!pthread_equal (mutex_handle
->tid
, tid
)) {
88 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
89 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, (long)mutex_handle
->tid
, (long)tid
);
90 return MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER
;
92 /* OK, we own this mutex */
93 mutex_handle
->recursion
--;
95 if (mutex_handle
->recursion
== 0) {
96 thread_disown_mutex (mono_thread_internal_current (), handle_data
);
98 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: unlocking %s handle %p, tid: %p recusion : %d",
99 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, (gpointer
) mutex_handle
->tid
, mutex_handle
->recursion
);
101 mutex_handle
->tid
= 0;
102 mono_w32handle_set_signal_state (handle_data
, TRUE
, FALSE
);
105 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
109 mutex_handle_own (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
111 MonoW32HandleMutex
*mutex_handle
;
115 mutex_handle
= (MonoW32HandleMutex
*) handle_data
->specific
;
117 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: owning %s handle %p, before: [tid: %p, recursion: %d], after: [tid: %p, recursion: %d], abandoned: %s",
118 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, (gpointer
) mutex_handle
->tid
, mutex_handle
->recursion
, (gpointer
) pthread_self (), mutex_handle
->recursion
+ 1, mutex_handle
->abandoned
? "true" : "false");
120 if (mutex_handle
->recursion
!= 0) {
121 g_assert (pthread_equal (pthread_self (), mutex_handle
->tid
));
122 mutex_handle
->recursion
++;
124 mutex_handle
->tid
= pthread_self ();
125 mutex_handle
->recursion
= 1;
127 thread_own_mutex (mono_thread_internal_current (), handle_data
, handle_data
);
130 if (mutex_handle
->abandoned
) {
131 mutex_handle
->abandoned
= FALSE
;
135 mono_w32handle_set_signal_state (handle_data
, FALSE
, FALSE
);
140 mutex_handle_is_owned (MonoW32Handle
*handle_data
)
142 MonoW32HandleMutex
*mutex_handle
;
144 mutex_handle
= (MonoW32HandleMutex
*) handle_data
->specific
;
146 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: testing ownership %s handle %p",
147 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
);
149 if (mutex_handle
->recursion
> 0 && pthread_equal (mutex_handle
->tid
, pthread_self ())) {
150 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: %s handle %p owned by %p",
151 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, (gpointer
) pthread_self ());
154 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
155 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, (gpointer
) pthread_self (), (gpointer
) mutex_handle
->tid
, mutex_handle
->recursion
);
160 static void mutex_handle_prewait (MonoW32Handle
*handle_data
)
162 /* If the mutex is not currently owned, do nothing and let the
163 * usual wait carry on. If it is owned, check that the owner
164 * is still alive; if it isn't we override the previous owner
165 * and assume that process exited abnormally and failed to
168 MonoW32HandleMutex
*mutex_handle
;
170 mutex_handle
= (MonoW32HandleMutex
*) handle_data
->specific
;
172 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: pre-waiting %s handle %p, owned? %s",
173 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, mutex_handle
->recursion
!= 0 ? "true" : "false");
176 static void mutex_details (MonoW32Handle
*handle_data
)
178 MonoW32HandleMutex
*mut
= (MonoW32HandleMutex
*)handle_data
->specific
;
180 #ifdef PTHREAD_POINTER_ID
181 g_print ("own: %5p, count: %5u", mut
->tid
, mut
->recursion
);
183 g_print ("own: %5ld, count: %5u", mut
->tid
, mut
->recursion
);
187 static void namedmutex_details (MonoW32Handle
*handle_data
)
189 MonoW32HandleNamedMutex
*namedmut
= (MonoW32HandleNamedMutex
*)handle_data
->specific
;
191 #ifdef PTHREAD_POINTER_ID
192 g_print ("own: %5p, count: %5u, name: \"%s\"",
193 namedmut
->m
.tid
, namedmut
->m
.recursion
, namedmut
->sharedns
.name
);
195 g_print ("own: %5ld, count: %5u, name: \"%s\"",
196 namedmut
->m
.tid
, namedmut
->m
.recursion
, namedmut
->sharedns
.name
);
200 static const gchar
* mutex_typename (void)
205 static gsize
mutex_typesize (void)
207 return sizeof (MonoW32HandleMutex
);
210 static const gchar
* namedmutex_typename (void)
215 static gsize
namedmutex_typesize (void)
217 return sizeof (MonoW32HandleNamedMutex
);
221 mono_w32mutex_init (void)
223 static const MonoW32HandleOps mutex_ops
= {
225 mutex_handle_signal
, /* signal */
226 mutex_handle_own
, /* own */
227 mutex_handle_is_owned
, /* is_owned */
228 NULL
, /* special_wait */
229 mutex_handle_prewait
, /* prewait */
230 mutex_details
, /* details */
231 mutex_typename
, /* typename */
232 mutex_typesize
, /* typesize */
235 static const MonoW32HandleOps namedmutex_ops
= {
237 mutex_handle_signal
, /* signal */
238 mutex_handle_own
, /* own */
239 mutex_handle_is_owned
, /* is_owned */
240 NULL
, /* special_wait */
241 mutex_handle_prewait
, /* prewait */
242 namedmutex_details
, /* details */
243 namedmutex_typename
, /* typename */
244 namedmutex_typesize
, /* typesize */
247 mono_w32handle_register_ops (MONO_W32TYPE_MUTEX
, &mutex_ops
);
248 mono_w32handle_register_ops (MONO_W32TYPE_NAMEDMUTEX
, &namedmutex_ops
);
250 mono_w32handle_register_capabilities (MONO_W32TYPE_MUTEX
,
251 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
| MONO_W32HANDLE_CAP_OWN
));
252 mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDMUTEX
,
253 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
| MONO_W32HANDLE_CAP_OWN
));
256 static gpointer
mutex_handle_create (MonoW32HandleMutex
*mutex_handle
, MonoW32Type type
, gboolean owned
)
258 MonoW32Handle
*handle_data
;
262 mutex_handle
->tid
= 0;
263 mutex_handle
->recursion
= 0;
264 mutex_handle
->abandoned
= FALSE
;
266 handle
= mono_w32handle_new (type
, mutex_handle
);
267 if (handle
== INVALID_HANDLE_VALUE
) {
268 g_warning ("%s: error creating %s handle",
269 __func__
, mono_w32handle_get_typename (type
));
270 mono_w32error_set_last (ERROR_GEN_FAILURE
);
274 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
275 g_error ("%s: unkown handle %p", __func__
, handle
);
277 if (handle_data
->type
!= type
)
278 g_error ("%s: unknown mutex handle %p", __func__
, handle
);
280 mono_w32handle_lock (handle_data
);
283 mutex_handle_own (handle_data
, &abandoned
);
285 mono_w32handle_set_signal_state (handle_data
, TRUE
, FALSE
);
287 mono_w32handle_unlock (handle_data
);
289 /* Balance mono_w32handle_lookup_and_ref */
290 mono_w32handle_unref (handle_data
);
292 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: created %s handle %p",
293 __func__
, mono_w32handle_get_typename (type
), handle
);
298 static gpointer
mutex_create (gboolean owned
)
300 MonoW32HandleMutex mutex_handle
;
301 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: creating %s handle",
302 __func__
, mono_w32handle_get_typename (MONO_W32TYPE_MUTEX
));
303 return mutex_handle_create (&mutex_handle
, MONO_W32TYPE_MUTEX
, owned
);
307 namedmutex_create (gboolean owned
, const char *utf8_name
, gsize utf8_len
)
309 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: creating %s handle",
310 __func__
, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDMUTEX
));
312 // Opening named objects does not race.
313 mono_w32handle_namespace_lock ();
315 gpointer handle
= mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX
, utf8_name
);
317 if (handle
== INVALID_HANDLE_VALUE
) {
318 /* The name has already been used for a different object. */
320 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
322 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
323 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
325 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
327 /* A new named mutex */
328 MonoW32HandleNamedMutex namedmutex_handle
;
330 // FIXME Silent truncation.
332 size_t len
= utf8_len
< MAX_PATH
? utf8_len
: MAX_PATH
;
333 memcpy (&namedmutex_handle
.sharedns
.name
[0], utf8_name
, len
);
334 namedmutex_handle
.sharedns
.name
[len
] = '\0';
336 handle
= mutex_handle_create ((MonoW32HandleMutex
*) &namedmutex_handle
, MONO_W32TYPE_NAMEDMUTEX
, owned
);
339 mono_w32handle_namespace_unlock ();
345 ves_icall_System_Threading_Mutex_CreateMutex_icall (MonoBoolean owned
, const gunichar2
*name
,
346 gint32 name_length
, MonoBoolean
*created
, MonoError
*error
)
352 /* Need to blow away any old errors here, because code tests
353 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
354 * was freshly created */
355 mono_w32error_set_last (ERROR_SUCCESS
);
358 mutex
= mutex_create (owned
);
360 gsize utf8_name_length
= 0;
361 char *utf8_name
= mono_utf16_to_utf8len (name
, name_length
, &utf8_name_length
, error
);
362 return_val_if_nok (error
, NULL
);
364 mutex
= namedmutex_create (owned
, utf8_name
, utf8_name_length
);
366 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS
)
375 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle
)
377 MonoW32Handle
*handle_data
;
378 MonoW32HandleMutex
*mutex_handle
;
382 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
383 g_warning ("%s: unkown handle %p", __func__
, handle
);
384 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
388 if (handle_data
->type
!= MONO_W32TYPE_MUTEX
&& handle_data
->type
!= MONO_W32TYPE_NAMEDMUTEX
) {
389 g_warning ("%s: unknown mutex handle %p", __func__
, handle
);
390 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
391 mono_w32handle_unref (handle_data
);
395 mutex_handle
= (MonoW32HandleMutex
*) handle_data
->specific
;
397 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: releasing %s handle %p, tid: %p recursion: %d",
398 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, (gpointer
) mutex_handle
->tid
, mutex_handle
->recursion
);
400 mono_w32handle_lock (handle_data
);
402 tid
= pthread_self ();
404 if (mutex_handle
->abandoned
) {
405 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
407 } else if (!pthread_equal (mutex_handle
->tid
, tid
)) {
410 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
411 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, (long)mutex_handle
->tid
, (long)tid
);
415 /* OK, we own this mutex */
416 mutex_handle
->recursion
--;
418 if (mutex_handle
->recursion
== 0) {
419 thread_disown_mutex (mono_thread_internal_current (), handle
);
421 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: unlocking %s handle %p, tid: %p recusion : %d",
422 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, (gpointer
) mutex_handle
->tid
, mutex_handle
->recursion
);
424 mutex_handle
->tid
= 0;
425 mono_w32handle_set_signal_state (handle_data
, TRUE
, FALSE
);
429 mono_w32handle_unlock (handle_data
);
430 mono_w32handle_unref (handle_data
);
436 ves_icall_System_Threading_Mutex_OpenMutex_icall (const gunichar2
*name
, gint32 name_length
, gint32 rights G_GNUC_UNUSED
, gint32
*win32error
, MonoError
*error
)
438 *win32error
= ERROR_SUCCESS
;
439 char *utf8_name
= mono_utf16_to_utf8 (name
, name_length
, error
);
440 return_val_if_nok (error
, NULL
);
441 gpointer handle
= mono_w32mutex_open (utf8_name
, rights
, win32error
);
447 mono_w32mutex_open (const char* utf8_name
, gint32 rights G_GNUC_UNUSED
, gint32
*win32error
)
449 *win32error
= ERROR_SUCCESS
;
451 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: Opening named mutex [%s]",
452 __func__
, utf8_name
);
454 // Opening named objects does not race.
455 mono_w32handle_namespace_lock ();
457 gpointer handle
= mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX
, utf8_name
);
459 mono_w32handle_namespace_unlock ();
461 if (handle
== INVALID_HANDLE_VALUE
) {
462 /* The name has already been used for a different object. */
463 *win32error
= ERROR_INVALID_HANDLE
;
465 } else if (!handle
) {
466 /* This name doesn't exist */
467 *win32error
= ERROR_FILE_NOT_FOUND
;
471 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: returning named mutex handle %p",
478 mono_w32mutex_abandon (MonoInternalThread
*internal
)
480 // Thread and InternalThread are pinned/mature.
481 // Take advantage of that and do not use handles here.
482 g_assert (mono_thread_internal_is_current (internal
));
484 if (!internal
->owned_mutexes
)
487 while (internal
->owned_mutexes
->len
) {
488 MonoW32Handle
*handle_data
;
489 MonoW32HandleMutex
*mutex_handle
;
490 MonoNativeThreadId tid
;
493 handle
= g_ptr_array_index (internal
->owned_mutexes
, 0);
495 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
496 g_error ("%s: unkown handle %p", __func__
, handle
);
498 if (handle_data
->type
!= MONO_W32TYPE_MUTEX
&& handle_data
->type
!= MONO_W32TYPE_NAMEDMUTEX
)
499 g_error ("%s: unkown mutex handle %p", __func__
, handle
);
501 mutex_handle
= (MonoW32HandleMutex
*) handle_data
->specific
;
503 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: abandoning %s handle %p",
504 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
);
506 tid
= MONO_UINT_TO_NATIVE_THREAD_ID (internal
->tid
);
508 if (!pthread_equal (mutex_handle
->tid
, tid
))
509 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
510 __func__
, handle
, (gpointer
) mutex_handle
->tid
, (gpointer
) tid
);
512 mono_w32handle_lock (handle_data
);
514 mutex_handle
->recursion
= 0;
515 mutex_handle
->tid
= 0;
516 mutex_handle
->abandoned
= TRUE
;
518 mono_w32handle_set_signal_state (handle_data
, TRUE
, FALSE
);
520 thread_disown_mutex (internal
, handle
);
522 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_MUTEX
, "%s: abandoned %s handle %p",
523 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
);
525 mono_w32handle_unlock (handle_data
);
526 mono_w32handle_unref (handle_data
);
529 g_ptr_array_free (internal
->owned_mutexes
, TRUE
);
530 internal
->owned_mutexes
= NULL
;
533 MonoW32HandleNamespace
*
534 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex
*mutex
)
536 return &mutex
->sharedns
;