X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fw32mutex-unix.c;h=ab2ef003ddf9db938f2479bb5612d9657666b993;hb=87b1ca3bd8fadef8502f5c7e3fad16b2418bede7;hp=d7a3c03dbd48c9b67aefe00deff653925eaaf192;hpb=25e0b3971549af535528315399bbda380d287679;p=mono.git diff --git a/mono/metadata/w32mutex-unix.c b/mono/metadata/w32mutex-unix.c index d7a3c03dbd4..ab2ef003ddf 100644 --- a/mono/metadata/w32mutex-unix.c +++ b/mono/metadata/w32mutex-unix.c @@ -1,5 +1,6 @@ -/* - * w32mutex-unix.c: Runtime support for managed Mutex on Unix +/** + * \file + * Runtime support for managed Mutex on Unix * * Author: * Ludovic Henry (luhenry@microsoft.com) @@ -8,19 +9,22 @@ */ #include "w32mutex.h" -#include "w32mutex-utils.h" #include +#include "w32error.h" #include "w32handle-namespace.h" -#include "mono/io-layer/io-layer.h" +#include "mono/metadata/object-internals.h" #include "mono/utils/mono-logger-internals.h" #include "mono/utils/mono-threads.h" -#include "mono/utils/w32handle.h" +#include "mono/metadata/w32handle.h" + +#define MAX_PATH 260 typedef struct { MonoNativeThreadId tid; guint32 recursion; + gboolean abandoned; } MonoW32HandleMutex; struct MonoW32HandleNamedMutex { @@ -28,23 +32,97 @@ struct MonoW32HandleNamedMutex { MonoW32HandleNamespace sharedns; }; +gpointer +mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error); + +static void +thread_own_mutex (MonoInternalThread *internal, gpointer handle) +{ + /* if we are not on the current thread, there is a + * race condition when allocating internal->owned_mutexes */ + g_assert (mono_thread_internal_is_current (internal)); + + if (!internal->owned_mutexes) + internal->owned_mutexes = g_ptr_array_new (); + + g_ptr_array_add (internal->owned_mutexes, mono_w32handle_duplicate (handle)); +} + +static void +thread_disown_mutex (MonoInternalThread *internal, gpointer handle) +{ + gboolean removed; + + g_assert (mono_thread_internal_is_current (internal)); + + g_assert (internal->owned_mutexes); + removed = g_ptr_array_remove (internal->owned_mutexes, handle); + g_assert (removed); + + mono_w32handle_close (handle); +} + +static void +mutex_handle_signal (gpointer handle, MonoW32HandleType type, MonoW32HandleMutex *mutex_handle) +{ + pthread_t tid; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: signalling %s handle %p, tid: %p recursion: %d", + __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); + + tid = pthread_self (); + + if (mutex_handle->abandoned) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p is abandoned", + __func__, mono_w32handle_get_typename (type), handle); + } else if (!pthread_equal (mutex_handle->tid, tid)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)", + __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid); + } else { + /* OK, we own this mutex */ + mutex_handle->recursion--; + + if (mutex_handle->recursion == 0) { + thread_disown_mutex (mono_thread_internal_current (), handle); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d", + __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); + + mutex_handle->tid = 0; + mono_w32handle_set_signal_state (handle, TRUE, FALSE); + } + } +} + static gboolean -mutex_handle_own (gpointer handle, MonoW32HandleType type) +mutex_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned) { MonoW32HandleMutex *mutex_handle; + *abandoned = FALSE; + if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { - g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle); + g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle); return FALSE; } - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p, tid %p, recursion %u", - __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); + 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", + __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"); - mono_thread_info_own_mutex (mono_thread_info_current (), handle); + if (mutex_handle->recursion != 0) { + g_assert (pthread_equal (pthread_self (), mutex_handle->tid)); + mutex_handle->recursion++; + } else { + mutex_handle->tid = pthread_self (); + mutex_handle->recursion = 1; - mutex_handle->tid = pthread_self (); - mutex_handle->recursion++; + thread_own_mutex (mono_thread_internal_current (), handle); + } + + if (mutex_handle->abandoned) { + mutex_handle->abandoned = FALSE; + *abandoned = TRUE; + } mono_w32handle_set_signal_state (handle, FALSE, FALSE); @@ -57,49 +135,48 @@ mutex_handle_is_owned (gpointer handle, MonoW32HandleType type) MonoW32HandleMutex *mutex_handle; if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { - g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle); + g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle); return FALSE; } mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + __func__, mono_w32handle_get_typename (type), handle); if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p", - __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self ()); + __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self ()); return TRUE; } else { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, but locked %d times by %p", - __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self (), mutex_handle->recursion, (gpointer) mutex_handle->tid); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, tid: %p recursion: %d", + __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion); return FALSE; } } -static void mutex_signal(gpointer handle) +static void mutex_signal(gpointer handle, gpointer handle_specific) { - ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle); + mutex_handle_signal (handle, MONO_W32HANDLE_MUTEX, (MonoW32HandleMutex*) handle_specific); } -static gboolean mutex_own (gpointer handle) +static gboolean mutex_own (gpointer handle, gboolean *abandoned) { - return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX); + return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned); } static gboolean mutex_is_owned (gpointer handle) { - return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX); } -static void namedmutex_signal (gpointer handle) +static void namedmutex_signal (gpointer handle, gpointer handle_specific) { - ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle); + mutex_handle_signal (handle, MONO_W32HANDLE_NAMEDMUTEX, (MonoW32HandleMutex*) handle_specific); } /* NB, always called with the shared handle lock held */ -static gboolean namedmutex_own (gpointer handle) +static gboolean namedmutex_own (gpointer handle, gboolean *abandoned) { - return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX); + return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned); } static gboolean namedmutex_is_owned (gpointer handle) @@ -119,12 +196,12 @@ static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type) if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { g_warning ("%s: error looking up %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + __func__, mono_w32handle_get_typename (type), handle); return; } mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s", - __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false"); + __func__, mono_w32handle_get_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false"); } /* The shared state is not locked when prewait methods are called */ @@ -222,32 +299,31 @@ mono_w32mutex_init (void) static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned) { gpointer handle; - int thr_ret; + gboolean abandoned; mutex_handle->tid = 0; mutex_handle->recursion = 0; + mutex_handle->abandoned = FALSE; handle = mono_w32handle_new (type, mutex_handle); if (handle == INVALID_HANDLE_VALUE) { g_warning ("%s: error creating %s handle", - __func__, mono_w32handle_ops_typename (type)); - SetLastError (ERROR_GEN_FAILURE); + __func__, mono_w32handle_get_typename (type)); + mono_w32error_set_last (ERROR_GEN_FAILURE); return NULL; } - thr_ret = mono_w32handle_lock_handle (handle); - g_assert (thr_ret == 0); + mono_w32handle_lock_handle (handle); if (owned) - mutex_handle_own (handle, type); + mutex_handle_own (handle, type, &abandoned); else mono_w32handle_set_signal_state (handle, TRUE, FALSE); - thr_ret = mono_w32handle_unlock_handle (handle); - g_assert (thr_ret == 0); + mono_w32handle_unlock_handle (handle); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + __func__, mono_w32handle_get_typename (type), handle); return handle; } @@ -256,70 +332,72 @@ static gpointer mutex_create (gboolean owned) { MonoW32HandleMutex mutex_handle; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle", - __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX)); + __func__, mono_w32handle_get_typename (MONO_W32HANDLE_MUTEX)); return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned); } -static gpointer namedmutex_create (gboolean owned, const gunichar2 *name) +static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name) { gpointer handle; - gchar *utf8_name; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle", - __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX)); + __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX)); /* w32 seems to guarantee that opening named objects can't race each other */ mono_w32handle_namespace_lock (); - utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL); + glong utf8_len = strlen (utf8_name); handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name); if (handle == INVALID_HANDLE_VALUE) { /* The name has already been used for a different object. */ handle = NULL; - SetLastError (ERROR_INVALID_HANDLE); + mono_w32error_set_last (ERROR_INVALID_HANDLE); } else if (handle) { /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */ - SetLastError (ERROR_ALREADY_EXISTS); + mono_w32error_set_last (ERROR_ALREADY_EXISTS); - /* this is used as creating a new handle */ - mono_w32handle_ref (handle); + /* mono_w32handle_namespace_search_handle already adds a ref to the handle */ } else { /* A new named mutex */ MonoW32HandleNamedMutex namedmutex_handle; - strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH); - namedmutex_handle.sharedns.name [MAX_PATH] = '\0'; + size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH; + memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len); + namedmutex_handle.sharedns.name [len] = '\0'; handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned); } - g_free (utf8_name); - mono_w32handle_namespace_unlock (); return handle; } gpointer -ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created) +ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error) { gpointer mutex; + error_init (error); *created = TRUE; /* Need to blow away any old errors here, because code tests * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex * was freshly created */ - SetLastError (ERROR_SUCCESS); + mono_w32error_set_last (ERROR_SUCCESS); - if (!name) { + if (MONO_HANDLE_IS_NULL (name)) { mutex = mutex_create (owned); } else { - mutex = namedmutex_create (owned, mono_string_chars (name)); + gchar *utf8_name = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + + mutex = namedmutex_create (owned, utf8_name); - if (GetLastError () == ERROR_ALREADY_EXISTS) + if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS) *created = FALSE; + g_free (utf8_name); } return mutex; @@ -331,11 +409,10 @@ ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle) MonoW32HandleType type; MonoW32HandleMutex *mutex_handle; pthread_t tid; - int thr_ret; gboolean ret; if (handle == NULL) { - SetLastError (ERROR_INVALID_HANDLE); + mono_w32error_set_last (ERROR_INVALID_HANDLE); return FALSE; } @@ -344,29 +421,31 @@ ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle) case MONO_W32HANDLE_NAMEDMUTEX: break; default: - SetLastError (ERROR_INVALID_HANDLE); + mono_w32error_set_last (ERROR_INVALID_HANDLE); return FALSE; } if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { g_warning ("%s: error looking up %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + __func__, mono_w32handle_get_typename (type), handle); return FALSE; } - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d", + __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); - thr_ret = mono_w32handle_lock_handle (handle); - g_assert (thr_ret == 0); + mono_w32handle_lock_handle (handle); tid = pthread_self (); - if (!pthread_equal (mutex_handle->tid, tid)) { + if (mutex_handle->abandoned) { + // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes + ret = TRUE; + } else if (!pthread_equal (mutex_handle->tid, tid)) { ret = FALSE; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)", - __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid); + __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid); } else { ret = TRUE; @@ -374,35 +453,42 @@ ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle) mutex_handle->recursion--; if (mutex_handle->recursion == 0) { - mono_thread_info_disown_mutex (mono_thread_info_current (), handle); + thread_disown_mutex (mono_thread_internal_current (), handle); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d", + __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion); mutex_handle->tid = 0; mono_w32handle_set_signal_state (handle, TRUE, FALSE); } } - thr_ret = mono_w32handle_unlock_handle (handle); - g_assert (thr_ret == 0); + mono_w32handle_unlock_handle (handle); return ret; } gpointer -ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error) +ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error) +{ + error_init (error); + gchar *utf8_name = mono_string_handle_to_utf8 (name, error); + return_val_if_nok (error, NULL); + gpointer handle = mono_w32mutex_open (utf8_name, rights, err); + g_free (utf8_name); + return handle; +} + +gpointer +mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error) { gpointer handle; - gchar *utf8_name; *error = ERROR_SUCCESS; /* w32 seems to guarantee that opening named objects can't race each other */ mono_w32handle_namespace_lock (); - utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]", __func__, utf8_name); @@ -421,52 +507,72 @@ ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 ri __func__, handle); cleanup: - g_free (utf8_name); - mono_w32handle_namespace_unlock (); return handle; } void -mono_w32mutex_abandon (gpointer handle, MonoNativeThreadId tid) +mono_w32mutex_abandon (void) { - MonoW32HandleType type; - MonoW32HandleMutex *mutex_handle; - int thr_ret; + MonoInternalThread *internal; - switch (type = mono_w32handle_get_type (handle)) { - case MONO_W32HANDLE_MUTEX: - case MONO_W32HANDLE_NAMEDMUTEX: - break; - default: - g_assert_not_reached (); - } + g_assert (mono_thread_internal_current_is_attached ()); - if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { - g_warning ("%s: error looking up %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + internal = mono_thread_internal_current (); + g_assert (internal); + + if (!internal->owned_mutexes) return; - } - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandon %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + while (internal->owned_mutexes->len) { + MonoW32HandleType type; + MonoW32HandleMutex *mutex_handle; + MonoNativeThreadId tid; + gpointer handle; + + handle = g_ptr_array_index (internal->owned_mutexes, 0); - thr_ret = mono_w32handle_lock_handle (handle); - g_assert (thr_ret == 0); + switch (type = mono_w32handle_get_type (handle)) { + case MONO_W32HANDLE_MUTEX: + case MONO_W32HANDLE_NAMEDMUTEX: + break; + default: + g_assert_not_reached (); + } + + if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) { + g_error ("%s: error looking up %s handle %p", + __func__, mono_w32handle_get_typename (type), handle); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p", + __func__, mono_w32handle_get_typename (type), handle); + + tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid); + + if (!pthread_equal (mutex_handle->tid, tid)) + g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p", + __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid); + + mono_w32handle_lock_handle (handle); - if (pthread_equal (mutex_handle->tid, tid)) { mutex_handle->recursion = 0; mutex_handle->tid = 0; + mutex_handle->abandoned = TRUE; mono_w32handle_set_signal_state (handle, TRUE, FALSE); + thread_disown_mutex (internal, handle); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p", - __func__, mono_w32handle_ops_typename (type), handle); + __func__, mono_w32handle_get_typename (type), handle); + + mono_w32handle_unlock_handle (handle); } - thr_ret = mono_w32handle_unlock_handle (handle); - g_assert (thr_ret == 0); + g_ptr_array_free (internal->owned_mutexes, TRUE); + internal->owned_mutexes = NULL; } MonoW32HandleNamespace*