2 * w32mutex-unix.c: Runtime support for managed Mutex on Unix
5 * Ludovic Henry (luhenry@microsoft.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #include "w32handle-namespace.h"
16 #include "mono/metadata/object-internals.h"
17 #include "mono/utils/mono-logger-internals.h"
18 #include "mono/utils/mono-threads.h"
19 #include "mono/metadata/w32handle.h"
24 MonoNativeThreadId tid;
29 struct MonoW32HandleNamedMutex {
31 MonoW32HandleNamespace sharedns;
35 thread_own_mutex (MonoInternalThread *internal, gpointer handle)
37 mono_w32handle_ref (handle);
39 /* if we are not on the current thread, there is a
40 * race condition when allocating internal->owned_mutexes */
41 g_assert (mono_thread_internal_is_current (internal));
43 if (!internal->owned_mutexes)
44 internal->owned_mutexes = g_ptr_array_new ();
46 g_ptr_array_add (internal->owned_mutexes, handle);
50 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
54 g_assert (mono_thread_internal_is_current (internal));
56 g_assert (internal->owned_mutexes);
57 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
60 mono_w32handle_unref (handle);
64 mutex_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
66 MonoW32HandleMutex *mutex_handle;
70 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
71 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
75 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",
76 __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");
78 if (mutex_handle->recursion != 0) {
79 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
80 mutex_handle->recursion++;
82 mutex_handle->tid = pthread_self ();
83 mutex_handle->recursion = 1;
85 thread_own_mutex (mono_thread_internal_current (), handle);
88 if (mutex_handle->abandoned) {
89 mutex_handle->abandoned = FALSE;
93 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
99 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
101 MonoW32HandleMutex *mutex_handle;
103 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
104 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
108 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
109 __func__, mono_w32handle_get_typename (type), handle);
111 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
112 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
113 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self ());
116 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
117 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
122 static void mutex_signal(gpointer handle)
124 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
127 static gboolean mutex_own (gpointer handle, gboolean *abandoned)
129 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
132 static gboolean mutex_is_owned (gpointer handle)
135 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
138 static void namedmutex_signal (gpointer handle)
140 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
143 /* NB, always called with the shared handle lock held */
144 static gboolean namedmutex_own (gpointer handle, gboolean *abandoned)
146 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
149 static gboolean namedmutex_is_owned (gpointer handle)
151 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
154 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
156 /* If the mutex is not currently owned, do nothing and let the
157 * usual wait carry on. If it is owned, check that the owner
158 * is still alive; if it isn't we override the previous owner
159 * and assume that process exited abnormally and failed to
162 MonoW32HandleMutex *mutex_handle;
164 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
165 g_warning ("%s: error looking up %s handle %p",
166 __func__, mono_w32handle_get_typename (type), handle);
170 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
171 __func__, mono_w32handle_get_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
174 /* The shared state is not locked when prewait methods are called */
175 static void mutex_prewait (gpointer handle)
177 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
180 /* The shared state is not locked when prewait methods are called */
181 static void namedmutex_prewait (gpointer handle)
183 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
186 static void mutex_details (gpointer data)
188 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
190 #ifdef PTHREAD_POINTER_ID
191 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
193 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
197 static void namedmutex_details (gpointer data)
199 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
201 #ifdef PTHREAD_POINTER_ID
202 g_print ("own: %5p, count: %5u, name: \"%s\"",
203 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
205 g_print ("own: %5ld, count: %5u, name: \"%s\"",
206 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
210 static const gchar* mutex_typename (void)
215 static gsize mutex_typesize (void)
217 return sizeof (MonoW32HandleMutex);
220 static const gchar* namedmutex_typename (void)
225 static gsize namedmutex_typesize (void)
227 return sizeof (MonoW32HandleNamedMutex);
231 mono_w32mutex_init (void)
233 static MonoW32HandleOps mutex_ops = {
235 mutex_signal, /* signal */
237 mutex_is_owned, /* is_owned */
238 NULL, /* special_wait */
239 mutex_prewait, /* prewait */
240 mutex_details, /* details */
241 mutex_typename, /* typename */
242 mutex_typesize, /* typesize */
245 static MonoW32HandleOps namedmutex_ops = {
247 namedmutex_signal, /* signal */
248 namedmutex_own, /* own */
249 namedmutex_is_owned, /* is_owned */
250 NULL, /* special_wait */
251 namedmutex_prewait, /* prewait */
252 namedmutex_details, /* details */
253 namedmutex_typename, /* typename */
254 namedmutex_typesize, /* typesize */
257 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
258 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
260 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
261 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
262 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
263 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
266 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
271 mutex_handle->tid = 0;
272 mutex_handle->recursion = 0;
273 mutex_handle->abandoned = FALSE;
275 handle = mono_w32handle_new (type, mutex_handle);
276 if (handle == INVALID_HANDLE_VALUE) {
277 g_warning ("%s: error creating %s handle",
278 __func__, mono_w32handle_get_typename (type));
279 mono_w32error_set_last (ERROR_GEN_FAILURE);
283 mono_w32handle_lock_handle (handle);
286 mutex_handle_own (handle, type, &abandoned);
288 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
290 mono_w32handle_unlock_handle (handle);
292 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%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, "%s: creating %s handle",
302 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_MUTEX));
303 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
306 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
311 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
312 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
314 /* w32 seems to guarantee that opening named objects can't race each other */
315 mono_w32handle_namespace_lock ();
318 utf8_name = g_utf16_to_utf8 (name, -1, NULL, &utf8_len, NULL);
320 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
321 if (handle == INVALID_HANDLE_VALUE) {
322 /* The name has already been used for a different object. */
324 mono_w32error_set_last (ERROR_INVALID_HANDLE);
326 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
327 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
329 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
331 /* A new named mutex */
332 MonoW32HandleNamedMutex namedmutex_handle;
334 size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
335 memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len);
336 namedmutex_handle.sharedns.name [len] = '\0';
338 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, MonoString *name, MonoBoolean *created)
355 /* Need to blow away any old errors here, because code tests
356 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
357 * was freshly created */
358 mono_w32error_set_last (ERROR_SUCCESS);
361 mutex = mutex_create (owned);
363 mutex = namedmutex_create (owned, mono_string_chars (name));
365 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
373 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
375 MonoW32HandleType type;
376 MonoW32HandleMutex *mutex_handle;
380 if (handle == NULL) {
381 mono_w32error_set_last (ERROR_INVALID_HANDLE);
385 switch (type = mono_w32handle_get_type (handle)) {
386 case MONO_W32HANDLE_MUTEX:
387 case MONO_W32HANDLE_NAMEDMUTEX:
390 mono_w32error_set_last (ERROR_INVALID_HANDLE);
394 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
395 g_warning ("%s: error looking up %s handle %p",
396 __func__, mono_w32handle_get_typename (type), handle);
400 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
401 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
403 mono_w32handle_lock_handle (handle);
405 tid = pthread_self ();
407 if (mutex_handle->abandoned) {
408 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
410 } else if (!pthread_equal (mutex_handle->tid, tid)) {
413 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
414 __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
418 /* OK, we own this mutex */
419 mutex_handle->recursion--;
421 if (mutex_handle->recursion == 0) {
422 thread_disown_mutex (mono_thread_internal_current (), handle);
424 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
425 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
427 mutex_handle->tid = 0;
428 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
432 mono_w32handle_unlock_handle (handle);
438 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
443 *error = ERROR_SUCCESS;
445 /* w32 seems to guarantee that opening named objects can't race each other */
446 mono_w32handle_namespace_lock ();
448 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
450 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
451 __func__, utf8_name);
453 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
454 if (handle == INVALID_HANDLE_VALUE) {
455 /* The name has already been used for a different object. */
456 *error = ERROR_INVALID_HANDLE;
458 } else if (!handle) {
459 /* This name doesn't exist */
460 *error = ERROR_FILE_NOT_FOUND;
464 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
470 mono_w32handle_namespace_unlock ();
476 mono_w32mutex_abandon (void)
478 MonoInternalThread *internal;
480 g_assert (mono_thread_internal_current_is_attached ());
482 internal = mono_thread_internal_current ();
485 if (!internal->owned_mutexes)
488 while (internal->owned_mutexes->len) {
489 MonoW32HandleType type;
490 MonoW32HandleMutex *mutex_handle;
491 MonoNativeThreadId tid;
494 handle = g_ptr_array_index (internal->owned_mutexes, 0);
496 switch (type = mono_w32handle_get_type (handle)) {
497 case MONO_W32HANDLE_MUTEX:
498 case MONO_W32HANDLE_NAMEDMUTEX:
501 g_assert_not_reached ();
504 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
505 g_error ("%s: error looking up %s handle %p",
506 __func__, mono_w32handle_get_typename (type), handle);
509 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
510 __func__, mono_w32handle_get_typename (type), handle);
512 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
514 if (!pthread_equal (mutex_handle->tid, tid))
515 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
516 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
518 mono_w32handle_lock_handle (handle);
520 mutex_handle->recursion = 0;
521 mutex_handle->tid = 0;
522 mutex_handle->abandoned = TRUE;
524 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
526 thread_disown_mutex (internal, handle);
528 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
529 __func__, mono_w32handle_get_typename (type), handle);
531 mono_w32handle_unlock_handle (handle);
534 g_ptr_array_free (internal->owned_mutexes, TRUE);
535 internal->owned_mutexes = NULL;
538 MonoW32HandleNamespace*
539 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
541 return &mutex->sharedns;