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/utils/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, guint32 *statuscode)
64 MonoW32HandleMutex *mutex_handle;
66 *statuscode = WAIT_OBJECT_0;
68 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
69 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_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_ops_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;
88 *statuscode = WAIT_ABANDONED_0;
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_ops_typename (type), handle);
106 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
107 __func__, mono_w32handle_ops_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_ops_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_ops_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, guint32 *statuscode)
127 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, statuscode);
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, guint32 *statuscode)
144 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, statuscode);
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_ops_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_ops_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)
270 mutex_handle->tid = 0;
271 mutex_handle->recursion = 0;
272 mutex_handle->abandoned = FALSE;
274 handle = mono_w32handle_new (type, mutex_handle);
275 if (handle == INVALID_HANDLE_VALUE) {
276 g_warning ("%s: error creating %s handle",
277 __func__, mono_w32handle_ops_typename (type));
278 SetLastError (ERROR_GEN_FAILURE);
282 thr_ret = mono_w32handle_lock_handle (handle);
283 g_assert (thr_ret == 0);
286 mutex_handle_own (handle, type, &statuscode);
288 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
290 thr_ret = mono_w32handle_unlock_handle (handle);
291 g_assert (thr_ret == 0);
293 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
294 __func__, mono_w32handle_ops_typename (type), handle);
299 static gpointer mutex_create (gboolean owned)
301 MonoW32HandleMutex mutex_handle;
302 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
303 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX));
304 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
307 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
312 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
313 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX));
315 /* w32 seems to guarantee that opening named objects can't race each other */
316 mono_w32handle_namespace_lock ();
318 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, 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 SetLastError (ERROR_INVALID_HANDLE);
326 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
327 SetLastError (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 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
335 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
337 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
342 mono_w32handle_namespace_unlock ();
348 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
354 /* Need to blow away any old errors here, because code tests
355 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
356 * was freshly created */
357 SetLastError (ERROR_SUCCESS);
360 mutex = mutex_create (owned);
362 mutex = namedmutex_create (owned, mono_string_chars (name));
364 if (GetLastError () == ERROR_ALREADY_EXISTS)
372 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
374 MonoW32HandleType type;
375 MonoW32HandleMutex *mutex_handle;
380 if (handle == NULL) {
381 SetLastError (ERROR_INVALID_HANDLE);
385 switch (type = mono_w32handle_get_type (handle)) {
386 case MONO_W32HANDLE_MUTEX:
387 case MONO_W32HANDLE_NAMEDMUTEX:
390 SetLastError (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_ops_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_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
403 thr_ret = mono_w32handle_lock_handle (handle);
404 g_assert (thr_ret == 0);
406 tid = pthread_self ();
408 if (mutex_handle->abandoned) {
409 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
411 } else if (!pthread_equal (mutex_handle->tid, tid)) {
414 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
415 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid);
419 /* OK, we own this mutex */
420 mutex_handle->recursion--;
422 if (mutex_handle->recursion == 0) {
423 thread_disown_mutex (mono_thread_internal_current (), handle);
425 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
426 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
428 mutex_handle->tid = 0;
429 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
433 thr_ret = mono_w32handle_unlock_handle (handle);
434 g_assert (thr_ret == 0);
440 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
445 *error = ERROR_SUCCESS;
447 /* w32 seems to guarantee that opening named objects can't race each other */
448 mono_w32handle_namespace_lock ();
450 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
452 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
453 __func__, utf8_name);
455 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
456 if (handle == INVALID_HANDLE_VALUE) {
457 /* The name has already been used for a different object. */
458 *error = ERROR_INVALID_HANDLE;
460 } else if (!handle) {
461 /* This name doesn't exist */
462 *error = ERROR_FILE_NOT_FOUND;
466 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
472 mono_w32handle_namespace_unlock ();
478 mono_w32mutex_abandon (void)
480 MonoInternalThread *internal;
482 g_assert (mono_thread_internal_current_is_attached ());
484 internal = mono_thread_internal_current ();
487 if (!internal->owned_mutexes)
490 while (internal->owned_mutexes->len) {
491 MonoW32HandleType type;
492 MonoW32HandleMutex *mutex_handle;
493 MonoNativeThreadId tid;
497 handle = g_ptr_array_index (internal->owned_mutexes, 0);
499 switch (type = mono_w32handle_get_type (handle)) {
500 case MONO_W32HANDLE_MUTEX:
501 case MONO_W32HANDLE_NAMEDMUTEX:
504 g_assert_not_reached ();
507 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
508 g_error ("%s: error looking up %s handle %p",
509 __func__, mono_w32handle_ops_typename (type), handle);
512 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
513 __func__, mono_w32handle_ops_typename (type), handle);
515 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
517 if (!pthread_equal (mutex_handle->tid, tid))
518 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
519 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
521 thr_ret = mono_w32handle_lock_handle (handle);
522 g_assert (thr_ret == 0);
524 mutex_handle->recursion = 0;
525 mutex_handle->tid = 0;
526 mutex_handle->abandoned = TRUE;
528 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
530 thread_disown_mutex (internal, handle);
532 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
533 __func__, mono_w32handle_ops_typename (type), handle);
535 thr_ret = mono_w32handle_unlock_handle (handle);
536 g_assert (thr_ret == 0);
539 g_ptr_array_free (internal->owned_mutexes, TRUE);
540 internal->owned_mutexes = NULL;
543 MonoW32HandleNamespace*
544 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
546 return &mutex->sharedns;