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"
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 SetLastError (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 ();
317 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
319 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
320 if (handle == INVALID_HANDLE_VALUE) {
321 /* The name has already been used for a different object. */
323 SetLastError (ERROR_INVALID_HANDLE);
325 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
326 SetLastError (ERROR_ALREADY_EXISTS);
328 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
330 /* A new named mutex */
331 MonoW32HandleNamedMutex namedmutex_handle;
333 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
334 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
336 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
341 mono_w32handle_namespace_unlock ();
347 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
353 /* Need to blow away any old errors here, because code tests
354 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
355 * was freshly created */
356 SetLastError (ERROR_SUCCESS);
359 mutex = mutex_create (owned);
361 mutex = namedmutex_create (owned, mono_string_chars (name));
363 if (GetLastError () == ERROR_ALREADY_EXISTS)
371 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
373 MonoW32HandleType type;
374 MonoW32HandleMutex *mutex_handle;
378 if (handle == NULL) {
379 SetLastError (ERROR_INVALID_HANDLE);
383 switch (type = mono_w32handle_get_type (handle)) {
384 case MONO_W32HANDLE_MUTEX:
385 case MONO_W32HANDLE_NAMEDMUTEX:
388 SetLastError (ERROR_INVALID_HANDLE);
392 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
393 g_warning ("%s: error looking up %s handle %p",
394 __func__, mono_w32handle_get_typename (type), handle);
398 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
399 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
401 mono_w32handle_lock_handle (handle);
403 tid = pthread_self ();
405 if (mutex_handle->abandoned) {
406 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
408 } else if (!pthread_equal (mutex_handle->tid, tid)) {
411 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
412 __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
416 /* OK, we own this mutex */
417 mutex_handle->recursion--;
419 if (mutex_handle->recursion == 0) {
420 thread_disown_mutex (mono_thread_internal_current (), handle);
422 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
423 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
425 mutex_handle->tid = 0;
426 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
430 mono_w32handle_unlock_handle (handle);
436 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
441 *error = ERROR_SUCCESS;
443 /* w32 seems to guarantee that opening named objects can't race each other */
444 mono_w32handle_namespace_lock ();
446 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
448 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
449 __func__, utf8_name);
451 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
452 if (handle == INVALID_HANDLE_VALUE) {
453 /* The name has already been used for a different object. */
454 *error = ERROR_INVALID_HANDLE;
456 } else if (!handle) {
457 /* This name doesn't exist */
458 *error = ERROR_FILE_NOT_FOUND;
462 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
468 mono_w32handle_namespace_unlock ();
474 mono_w32mutex_abandon (void)
476 MonoInternalThread *internal;
478 g_assert (mono_thread_internal_current_is_attached ());
480 internal = mono_thread_internal_current ();
483 if (!internal->owned_mutexes)
486 while (internal->owned_mutexes->len) {
487 MonoW32HandleType type;
488 MonoW32HandleMutex *mutex_handle;
489 MonoNativeThreadId tid;
492 handle = g_ptr_array_index (internal->owned_mutexes, 0);
494 switch (type = mono_w32handle_get_type (handle)) {
495 case MONO_W32HANDLE_MUTEX:
496 case MONO_W32HANDLE_NAMEDMUTEX:
499 g_assert_not_reached ();
502 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
503 g_error ("%s: error looking up %s handle %p",
504 __func__, mono_w32handle_get_typename (type), handle);
507 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
508 __func__, mono_w32handle_get_typename (type), handle);
510 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
512 if (!pthread_equal (mutex_handle->tid, tid))
513 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
514 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
516 mono_w32handle_lock_handle (handle);
518 mutex_handle->recursion = 0;
519 mutex_handle->tid = 0;
520 mutex_handle->abandoned = TRUE;
522 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
524 thread_disown_mutex (internal, handle);
526 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
527 __func__, mono_w32handle_get_typename (type), handle);
529 mono_w32handle_unlock_handle (handle);
532 g_ptr_array_free (internal->owned_mutexes, TRUE);
533 internal->owned_mutexes = NULL;
536 MonoW32HandleNamespace*
537 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
539 return &mutex->sharedns;