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 thread_own_mutex (MonoInternalThread *internal, gpointer handle)
38 mono_w32handle_ref (handle);
40 /* if we are not on the current thread, there is a
41 * race condition when allocating internal->owned_mutexes */
42 g_assert (mono_thread_internal_is_current (internal));
44 if (!internal->owned_mutexes)
45 internal->owned_mutexes = g_ptr_array_new ();
47 g_ptr_array_add (internal->owned_mutexes, handle);
51 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
55 g_assert (mono_thread_internal_is_current (internal));
57 g_assert (internal->owned_mutexes);
58 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
61 mono_w32handle_unref (handle);
65 mutex_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
67 MonoW32HandleMutex *mutex_handle;
71 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
72 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
76 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",
77 __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");
79 if (mutex_handle->recursion != 0) {
80 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
81 mutex_handle->recursion++;
83 mutex_handle->tid = pthread_self ();
84 mutex_handle->recursion = 1;
86 thread_own_mutex (mono_thread_internal_current (), handle);
89 if (mutex_handle->abandoned) {
90 mutex_handle->abandoned = FALSE;
94 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
100 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
102 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: testing ownership %s handle %p",
110 __func__, mono_w32handle_get_typename (type), handle);
112 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
113 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
114 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self ());
117 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
118 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
123 static void mutex_signal(gpointer handle)
125 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
128 static gboolean mutex_own (gpointer handle, gboolean *abandoned)
130 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
133 static gboolean mutex_is_owned (gpointer handle)
136 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
139 static void namedmutex_signal (gpointer handle)
141 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
144 /* NB, always called with the shared handle lock held */
145 static gboolean namedmutex_own (gpointer handle, gboolean *abandoned)
147 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
150 static gboolean namedmutex_is_owned (gpointer handle)
152 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
155 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
157 /* If the mutex is not currently owned, do nothing and let the
158 * usual wait carry on. If it is owned, check that the owner
159 * is still alive; if it isn't we override the previous owner
160 * and assume that process exited abnormally and failed to
163 MonoW32HandleMutex *mutex_handle;
165 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
166 g_warning ("%s: error looking up %s handle %p",
167 __func__, mono_w32handle_get_typename (type), handle);
171 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
172 __func__, mono_w32handle_get_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
175 /* The shared state is not locked when prewait methods are called */
176 static void mutex_prewait (gpointer handle)
178 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
181 /* The shared state is not locked when prewait methods are called */
182 static void namedmutex_prewait (gpointer handle)
184 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
187 static void mutex_details (gpointer data)
189 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
191 #ifdef PTHREAD_POINTER_ID
192 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
194 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
198 static void namedmutex_details (gpointer data)
200 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
202 #ifdef PTHREAD_POINTER_ID
203 g_print ("own: %5p, count: %5u, name: \"%s\"",
204 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
206 g_print ("own: %5ld, count: %5u, name: \"%s\"",
207 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
211 static const gchar* mutex_typename (void)
216 static gsize mutex_typesize (void)
218 return sizeof (MonoW32HandleMutex);
221 static const gchar* namedmutex_typename (void)
226 static gsize namedmutex_typesize (void)
228 return sizeof (MonoW32HandleNamedMutex);
232 mono_w32mutex_init (void)
234 static MonoW32HandleOps mutex_ops = {
236 mutex_signal, /* signal */
238 mutex_is_owned, /* is_owned */
239 NULL, /* special_wait */
240 mutex_prewait, /* prewait */
241 mutex_details, /* details */
242 mutex_typename, /* typename */
243 mutex_typesize, /* typesize */
246 static MonoW32HandleOps namedmutex_ops = {
248 namedmutex_signal, /* signal */
249 namedmutex_own, /* own */
250 namedmutex_is_owned, /* is_owned */
251 NULL, /* special_wait */
252 namedmutex_prewait, /* prewait */
253 namedmutex_details, /* details */
254 namedmutex_typename, /* typename */
255 namedmutex_typesize, /* typesize */
258 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
259 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
261 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
262 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
263 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
264 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
267 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
272 mutex_handle->tid = 0;
273 mutex_handle->recursion = 0;
274 mutex_handle->abandoned = FALSE;
276 handle = mono_w32handle_new (type, mutex_handle);
277 if (handle == INVALID_HANDLE_VALUE) {
278 g_warning ("%s: error creating %s handle",
279 __func__, mono_w32handle_get_typename (type));
280 mono_w32error_set_last (ERROR_GEN_FAILURE);
284 mono_w32handle_lock_handle (handle);
287 mutex_handle_own (handle, type, &abandoned);
289 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
291 mono_w32handle_unlock_handle (handle);
293 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
294 __func__, mono_w32handle_get_typename (type), handle);
299 static gpointer mutex_create (gboolean owned)
301 MonoW32HandleMutex mutex_handle;
302 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
303 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_MUTEX));
304 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
307 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
312 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
313 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
315 /* w32 seems to guarantee that opening named objects can't race each other */
316 mono_w32handle_namespace_lock ();
319 utf8_name = g_utf16_to_utf8 (name, -1, NULL, &utf8_len, NULL);
321 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
322 if (handle == INVALID_HANDLE_VALUE) {
323 /* The name has already been used for a different object. */
325 mono_w32error_set_last (ERROR_INVALID_HANDLE);
327 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
328 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
330 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
332 /* A new named mutex */
333 MonoW32HandleNamedMutex namedmutex_handle;
335 size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
336 memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len);
337 namedmutex_handle.sharedns.name [len] = '\0';
339 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
344 mono_w32handle_namespace_unlock ();
350 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
356 /* Need to blow away any old errors here, because code tests
357 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
358 * was freshly created */
359 mono_w32error_set_last (ERROR_SUCCESS);
362 mutex = mutex_create (owned);
364 mutex = namedmutex_create (owned, mono_string_chars (name));
366 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
374 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
376 MonoW32HandleType type;
377 MonoW32HandleMutex *mutex_handle;
381 if (handle == NULL) {
382 mono_w32error_set_last (ERROR_INVALID_HANDLE);
386 switch (type = mono_w32handle_get_type (handle)) {
387 case MONO_W32HANDLE_MUTEX:
388 case MONO_W32HANDLE_NAMEDMUTEX:
391 mono_w32error_set_last (ERROR_INVALID_HANDLE);
395 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
396 g_warning ("%s: error looking up %s handle %p",
397 __func__, mono_w32handle_get_typename (type), handle);
401 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
402 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
404 mono_w32handle_lock_handle (handle);
406 tid = pthread_self ();
408 if (mutex_handle->abandoned) {
409 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
411 } else if (!pthread_equal (mutex_handle->tid, tid)) {
414 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
415 __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
419 /* OK, we own this mutex */
420 mutex_handle->recursion--;
422 if (mutex_handle->recursion == 0) {
423 thread_disown_mutex (mono_thread_internal_current (), handle);
425 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
426 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
428 mutex_handle->tid = 0;
429 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
433 mono_w32handle_unlock_handle (handle);
439 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
444 *error = ERROR_SUCCESS;
446 /* w32 seems to guarantee that opening named objects can't race each other */
447 mono_w32handle_namespace_lock ();
449 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
451 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
452 __func__, utf8_name);
454 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
455 if (handle == INVALID_HANDLE_VALUE) {
456 /* The name has already been used for a different object. */
457 *error = ERROR_INVALID_HANDLE;
459 } else if (!handle) {
460 /* This name doesn't exist */
461 *error = ERROR_FILE_NOT_FOUND;
465 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
471 mono_w32handle_namespace_unlock ();
477 mono_w32mutex_abandon (void)
479 MonoInternalThread *internal;
481 g_assert (mono_thread_internal_current_is_attached ());
483 internal = mono_thread_internal_current ();
486 if (!internal->owned_mutexes)
489 while (internal->owned_mutexes->len) {
490 MonoW32HandleType type;
491 MonoW32HandleMutex *mutex_handle;
492 MonoNativeThreadId tid;
495 handle = g_ptr_array_index (internal->owned_mutexes, 0);
497 switch (type = mono_w32handle_get_type (handle)) {
498 case MONO_W32HANDLE_MUTEX:
499 case MONO_W32HANDLE_NAMEDMUTEX:
502 g_assert_not_reached ();
505 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
506 g_error ("%s: error looking up %s handle %p",
507 __func__, mono_w32handle_get_typename (type), handle);
510 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
511 __func__, mono_w32handle_get_typename (type), handle);
513 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
515 if (!pthread_equal (mutex_handle->tid, tid))
516 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
517 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
519 mono_w32handle_lock_handle (handle);
521 mutex_handle->recursion = 0;
522 mutex_handle->tid = 0;
523 mutex_handle->abandoned = TRUE;
525 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
527 thread_disown_mutex (internal, handle);
529 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
530 __func__, mono_w32handle_get_typename (type), handle);
532 mono_w32handle_unlock_handle (handle);
535 g_ptr_array_free (internal->owned_mutexes, TRUE);
536 internal->owned_mutexes = NULL;
539 MonoW32HandleNamespace*
540 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
542 return &mutex->sharedns;