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_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
70 MonoW32HandleMutex *mutex_handle;
74 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
75 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
79 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",
80 __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");
82 if (mutex_handle->recursion != 0) {
83 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
84 mutex_handle->recursion++;
86 mutex_handle->tid = pthread_self ();
87 mutex_handle->recursion = 1;
89 thread_own_mutex (mono_thread_internal_current (), handle);
92 if (mutex_handle->abandoned) {
93 mutex_handle->abandoned = FALSE;
97 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
103 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
105 MonoW32HandleMutex *mutex_handle;
107 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
108 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
112 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
113 __func__, mono_w32handle_get_typename (type), handle);
115 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
116 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
117 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self ());
120 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
121 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
126 static void mutex_signal(gpointer handle)
128 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
131 static gboolean mutex_own (gpointer handle, gboolean *abandoned)
133 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
136 static gboolean mutex_is_owned (gpointer handle)
139 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
142 static void namedmutex_signal (gpointer handle)
144 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
147 /* NB, always called with the shared handle lock held */
148 static gboolean namedmutex_own (gpointer handle, gboolean *abandoned)
150 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
153 static gboolean namedmutex_is_owned (gpointer handle)
155 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
158 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
160 /* If the mutex is not currently owned, do nothing and let the
161 * usual wait carry on. If it is owned, check that the owner
162 * is still alive; if it isn't we override the previous owner
163 * and assume that process exited abnormally and failed to
166 MonoW32HandleMutex *mutex_handle;
168 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
169 g_warning ("%s: error looking up %s handle %p",
170 __func__, mono_w32handle_get_typename (type), handle);
174 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
175 __func__, mono_w32handle_get_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
178 /* The shared state is not locked when prewait methods are called */
179 static void mutex_prewait (gpointer handle)
181 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
184 /* The shared state is not locked when prewait methods are called */
185 static void namedmutex_prewait (gpointer handle)
187 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
190 static void mutex_details (gpointer data)
192 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
194 #ifdef PTHREAD_POINTER_ID
195 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
197 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
201 static void namedmutex_details (gpointer data)
203 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
205 #ifdef PTHREAD_POINTER_ID
206 g_print ("own: %5p, count: %5u, name: \"%s\"",
207 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
209 g_print ("own: %5ld, count: %5u, name: \"%s\"",
210 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
214 static const gchar* mutex_typename (void)
219 static gsize mutex_typesize (void)
221 return sizeof (MonoW32HandleMutex);
224 static const gchar* namedmutex_typename (void)
229 static gsize namedmutex_typesize (void)
231 return sizeof (MonoW32HandleNamedMutex);
235 mono_w32mutex_init (void)
237 static MonoW32HandleOps mutex_ops = {
239 mutex_signal, /* signal */
241 mutex_is_owned, /* is_owned */
242 NULL, /* special_wait */
243 mutex_prewait, /* prewait */
244 mutex_details, /* details */
245 mutex_typename, /* typename */
246 mutex_typesize, /* typesize */
249 static MonoW32HandleOps namedmutex_ops = {
251 namedmutex_signal, /* signal */
252 namedmutex_own, /* own */
253 namedmutex_is_owned, /* is_owned */
254 NULL, /* special_wait */
255 namedmutex_prewait, /* prewait */
256 namedmutex_details, /* details */
257 namedmutex_typename, /* typename */
258 namedmutex_typesize, /* typesize */
261 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
262 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
264 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
265 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
266 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
267 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
270 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
275 mutex_handle->tid = 0;
276 mutex_handle->recursion = 0;
277 mutex_handle->abandoned = FALSE;
279 handle = mono_w32handle_new (type, mutex_handle);
280 if (handle == INVALID_HANDLE_VALUE) {
281 g_warning ("%s: error creating %s handle",
282 __func__, mono_w32handle_get_typename (type));
283 mono_w32error_set_last (ERROR_GEN_FAILURE);
287 mono_w32handle_lock_handle (handle);
290 mutex_handle_own (handle, type, &abandoned);
292 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
294 mono_w32handle_unlock_handle (handle);
296 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
297 __func__, mono_w32handle_get_typename (type), handle);
302 static gpointer mutex_create (gboolean owned)
304 MonoW32HandleMutex mutex_handle;
305 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
306 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_MUTEX));
307 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
310 static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name)
314 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
315 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
317 /* w32 seems to guarantee that opening named objects can't race each other */
318 mono_w32handle_namespace_lock ();
320 glong utf8_len = strlen (utf8_name);
322 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
323 if (handle == INVALID_HANDLE_VALUE) {
324 /* The name has already been used for a different object. */
326 mono_w32error_set_last (ERROR_INVALID_HANDLE);
328 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
329 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
331 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
333 /* A new named mutex */
334 MonoW32HandleNamedMutex namedmutex_handle;
336 size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
337 memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len);
338 namedmutex_handle.sharedns.name [len] = '\0';
340 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
343 mono_w32handle_namespace_unlock ();
349 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error)
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);
361 if (MONO_HANDLE_IS_NULL (name)) {
362 mutex = mutex_create (owned);
364 gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
365 return_val_if_nok (error, NULL);
367 mutex = namedmutex_create (owned, utf8_name);
369 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
378 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
380 MonoW32HandleType type;
381 MonoW32HandleMutex *mutex_handle;
385 if (handle == NULL) {
386 mono_w32error_set_last (ERROR_INVALID_HANDLE);
390 switch (type = mono_w32handle_get_type (handle)) {
391 case MONO_W32HANDLE_MUTEX:
392 case MONO_W32HANDLE_NAMEDMUTEX:
395 mono_w32error_set_last (ERROR_INVALID_HANDLE);
399 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
400 g_warning ("%s: error looking up %s handle %p",
401 __func__, mono_w32handle_get_typename (type), handle);
405 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
406 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
408 mono_w32handle_lock_handle (handle);
410 tid = pthread_self ();
412 if (mutex_handle->abandoned) {
413 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
415 } else if (!pthread_equal (mutex_handle->tid, tid)) {
418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
419 __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
423 /* OK, we own this mutex */
424 mutex_handle->recursion--;
426 if (mutex_handle->recursion == 0) {
427 thread_disown_mutex (mono_thread_internal_current (), handle);
429 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
430 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
432 mutex_handle->tid = 0;
433 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
437 mono_w32handle_unlock_handle (handle);
443 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error)
446 gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
447 return_val_if_nok (error, NULL);
448 gpointer handle = mono_w32mutex_open (utf8_name, rights, err);
454 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error)
458 *error = ERROR_SUCCESS;
460 /* w32 seems to guarantee that opening named objects can't race each other */
461 mono_w32handle_namespace_lock ();
463 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
464 __func__, utf8_name);
466 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
467 if (handle == INVALID_HANDLE_VALUE) {
468 /* The name has already been used for a different object. */
469 *error = ERROR_INVALID_HANDLE;
471 } else if (!handle) {
472 /* This name doesn't exist */
473 *error = ERROR_FILE_NOT_FOUND;
477 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
481 mono_w32handle_namespace_unlock ();
487 mono_w32mutex_abandon (void)
489 MonoInternalThread *internal;
491 g_assert (mono_thread_internal_current_is_attached ());
493 internal = mono_thread_internal_current ();
496 if (!internal->owned_mutexes)
499 while (internal->owned_mutexes->len) {
500 MonoW32HandleType type;
501 MonoW32HandleMutex *mutex_handle;
502 MonoNativeThreadId tid;
505 handle = g_ptr_array_index (internal->owned_mutexes, 0);
507 switch (type = mono_w32handle_get_type (handle)) {
508 case MONO_W32HANDLE_MUTEX:
509 case MONO_W32HANDLE_NAMEDMUTEX:
512 g_assert_not_reached ();
515 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
516 g_error ("%s: error looking up %s handle %p",
517 __func__, mono_w32handle_get_typename (type), handle);
520 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
521 __func__, mono_w32handle_get_typename (type), handle);
523 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
525 if (!pthread_equal (mutex_handle->tid, tid))
526 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
527 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
529 mono_w32handle_lock_handle (handle);
531 mutex_handle->recursion = 0;
532 mutex_handle->tid = 0;
533 mutex_handle->abandoned = TRUE;
535 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
537 thread_disown_mutex (internal, handle);
539 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
540 __func__, mono_w32handle_get_typename (type), handle);
542 mono_w32handle_unlock_handle (handle);
545 g_ptr_array_free (internal->owned_mutexes, TRUE);
546 internal->owned_mutexes = NULL;
549 MonoW32HandleNamespace*
550 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
552 return &mutex->sharedns;