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.
14 #include "w32handle-namespace.h"
15 #include "mono/io-layer/io-layer.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"
22 MonoNativeThreadId tid;
27 struct MonoW32HandleNamedMutex {
29 MonoW32HandleNamespace sharedns;
33 thread_own_mutex (MonoInternalThread *internal, gpointer handle)
35 mono_w32handle_ref (handle);
37 /* if we are not on the current thread, there is a
38 * race condition when allocating internal->owned_mutexes */
39 g_assert (mono_thread_internal_is_current (internal));
41 if (!internal->owned_mutexes)
42 internal->owned_mutexes = g_ptr_array_new ();
44 g_ptr_array_add (internal->owned_mutexes, handle);
48 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
52 g_assert (mono_thread_internal_is_current (internal));
54 g_assert (internal->owned_mutexes);
55 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
58 mono_w32handle_unref (handle);
62 mutex_handle_own (gpointer handle, MonoW32HandleType type, gboolean *abandoned)
64 MonoW32HandleMutex *mutex_handle;
68 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
69 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
73 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",
74 __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");
76 if (mutex_handle->recursion != 0) {
77 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
78 mutex_handle->recursion++;
80 mutex_handle->tid = pthread_self ();
81 mutex_handle->recursion = 1;
83 thread_own_mutex (mono_thread_internal_current (), handle);
86 if (mutex_handle->abandoned) {
87 mutex_handle->abandoned = FALSE;
91 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
97 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
99 MonoW32HandleMutex *mutex_handle;
101 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
102 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_typename (type), handle);
106 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
107 __func__, mono_w32handle_get_typename (type), handle);
109 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
110 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
111 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self ());
114 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
115 __func__, mono_w32handle_get_typename (type), handle, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
120 static void mutex_signal(gpointer handle)
122 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
125 static gboolean mutex_own (gpointer handle, gboolean *abandoned)
127 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
130 static gboolean mutex_is_owned (gpointer handle)
133 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
136 static void namedmutex_signal (gpointer handle)
138 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
141 /* NB, always called with the shared handle lock held */
142 static gboolean namedmutex_own (gpointer handle, gboolean *abandoned)
144 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
147 static gboolean namedmutex_is_owned (gpointer handle)
149 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
152 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
154 /* If the mutex is not currently owned, do nothing and let the
155 * usual wait carry on. If it is owned, check that the owner
156 * is still alive; if it isn't we override the previous owner
157 * and assume that process exited abnormally and failed to
160 MonoW32HandleMutex *mutex_handle;
162 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
163 g_warning ("%s: error looking up %s handle %p",
164 __func__, mono_w32handle_get_typename (type), handle);
168 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
169 __func__, mono_w32handle_get_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
172 /* The shared state is not locked when prewait methods are called */
173 static void mutex_prewait (gpointer handle)
175 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
178 /* The shared state is not locked when prewait methods are called */
179 static void namedmutex_prewait (gpointer handle)
181 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
184 static void mutex_details (gpointer data)
186 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
188 #ifdef PTHREAD_POINTER_ID
189 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
191 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
195 static void namedmutex_details (gpointer data)
197 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
199 #ifdef PTHREAD_POINTER_ID
200 g_print ("own: %5p, count: %5u, name: \"%s\"",
201 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
203 g_print ("own: %5ld, count: %5u, name: \"%s\"",
204 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
208 static const gchar* mutex_typename (void)
213 static gsize mutex_typesize (void)
215 return sizeof (MonoW32HandleMutex);
218 static const gchar* namedmutex_typename (void)
223 static gsize namedmutex_typesize (void)
225 return sizeof (MonoW32HandleNamedMutex);
229 mono_w32mutex_init (void)
231 static MonoW32HandleOps mutex_ops = {
233 mutex_signal, /* signal */
235 mutex_is_owned, /* is_owned */
236 NULL, /* special_wait */
237 mutex_prewait, /* prewait */
238 mutex_details, /* details */
239 mutex_typename, /* typename */
240 mutex_typesize, /* typesize */
243 static MonoW32HandleOps namedmutex_ops = {
245 namedmutex_signal, /* signal */
246 namedmutex_own, /* own */
247 namedmutex_is_owned, /* is_owned */
248 NULL, /* special_wait */
249 namedmutex_prewait, /* prewait */
250 namedmutex_details, /* details */
251 namedmutex_typename, /* typename */
252 namedmutex_typesize, /* typesize */
255 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
256 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
258 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
259 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
260 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
261 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
264 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
269 mutex_handle->tid = 0;
270 mutex_handle->recursion = 0;
271 mutex_handle->abandoned = FALSE;
273 handle = mono_w32handle_new (type, mutex_handle);
274 if (handle == INVALID_HANDLE_VALUE) {
275 g_warning ("%s: error creating %s handle",
276 __func__, mono_w32handle_get_typename (type));
277 SetLastError (ERROR_GEN_FAILURE);
281 mono_w32handle_lock_handle (handle);
284 mutex_handle_own (handle, type, &abandoned);
286 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
288 mono_w32handle_unlock_handle (handle);
290 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
291 __func__, mono_w32handle_get_typename (type), handle);
296 static gpointer mutex_create (gboolean owned)
298 MonoW32HandleMutex mutex_handle;
299 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
300 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_MUTEX));
301 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
304 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
309 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
310 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
312 /* w32 seems to guarantee that opening named objects can't race each other */
313 mono_w32handle_namespace_lock ();
315 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
317 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
318 if (handle == INVALID_HANDLE_VALUE) {
319 /* The name has already been used for a different object. */
321 SetLastError (ERROR_INVALID_HANDLE);
323 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
324 SetLastError (ERROR_ALREADY_EXISTS);
326 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
328 /* A new named mutex */
329 MonoW32HandleNamedMutex namedmutex_handle;
331 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
332 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
334 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
339 mono_w32handle_namespace_unlock ();
345 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
351 /* Need to blow away any old errors here, because code tests
352 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
353 * was freshly created */
354 SetLastError (ERROR_SUCCESS);
357 mutex = mutex_create (owned);
359 mutex = namedmutex_create (owned, mono_string_chars (name));
361 if (GetLastError () == ERROR_ALREADY_EXISTS)
369 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
371 MonoW32HandleType type;
372 MonoW32HandleMutex *mutex_handle;
376 if (handle == NULL) {
377 SetLastError (ERROR_INVALID_HANDLE);
381 switch (type = mono_w32handle_get_type (handle)) {
382 case MONO_W32HANDLE_MUTEX:
383 case MONO_W32HANDLE_NAMEDMUTEX:
386 SetLastError (ERROR_INVALID_HANDLE);
390 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
391 g_warning ("%s: error looking up %s handle %p",
392 __func__, mono_w32handle_get_typename (type), handle);
396 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
397 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
399 mono_w32handle_lock_handle (handle);
401 tid = pthread_self ();
403 if (mutex_handle->abandoned) {
404 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
406 } else if (!pthread_equal (mutex_handle->tid, tid)) {
409 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
410 __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
414 /* OK, we own this mutex */
415 mutex_handle->recursion--;
417 if (mutex_handle->recursion == 0) {
418 thread_disown_mutex (mono_thread_internal_current (), handle);
420 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
421 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
423 mutex_handle->tid = 0;
424 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
428 mono_w32handle_unlock_handle (handle);
434 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
439 *error = ERROR_SUCCESS;
441 /* w32 seems to guarantee that opening named objects can't race each other */
442 mono_w32handle_namespace_lock ();
444 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
446 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
447 __func__, utf8_name);
449 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
450 if (handle == INVALID_HANDLE_VALUE) {
451 /* The name has already been used for a different object. */
452 *error = ERROR_INVALID_HANDLE;
454 } else if (!handle) {
455 /* This name doesn't exist */
456 *error = ERROR_FILE_NOT_FOUND;
460 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
466 mono_w32handle_namespace_unlock ();
472 mono_w32mutex_abandon (void)
474 MonoInternalThread *internal;
476 g_assert (mono_thread_internal_current_is_attached ());
478 internal = mono_thread_internal_current ();
481 if (!internal->owned_mutexes)
484 while (internal->owned_mutexes->len) {
485 MonoW32HandleType type;
486 MonoW32HandleMutex *mutex_handle;
487 MonoNativeThreadId tid;
490 handle = g_ptr_array_index (internal->owned_mutexes, 0);
492 switch (type = mono_w32handle_get_type (handle)) {
493 case MONO_W32HANDLE_MUTEX:
494 case MONO_W32HANDLE_NAMEDMUTEX:
497 g_assert_not_reached ();
500 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
501 g_error ("%s: error looking up %s handle %p",
502 __func__, mono_w32handle_get_typename (type), handle);
505 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
506 __func__, mono_w32handle_get_typename (type), handle);
508 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
510 if (!pthread_equal (mutex_handle->tid, tid))
511 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
512 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
514 mono_w32handle_lock_handle (handle);
516 mutex_handle->recursion = 0;
517 mutex_handle->tid = 0;
518 mutex_handle->abandoned = TRUE;
520 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
522 thread_disown_mutex (internal, handle);
524 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
525 __func__, mono_w32handle_get_typename (type), handle);
527 mono_w32handle_unlock_handle (handle);
530 g_ptr_array_free (internal->owned_mutexes, TRUE);
531 internal->owned_mutexes = NULL;
534 MonoW32HandleNamespace*
535 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
537 return &mutex->sharedns;