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.
11 #include "w32mutex-utils.h"
15 #include "w32handle-namespace.h"
16 #include "mono/io-layer/io-layer.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 mutex_handle_own (gpointer handle, MonoW32HandleType type, guint32 *statuscode)
35 MonoW32HandleMutex *mutex_handle;
37 *statuscode = WAIT_OBJECT_0;
39 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
40 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
44 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p, tid %p, recursion %u",
45 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
47 mono_thread_info_own_mutex (mono_thread_info_current (), handle);
49 mutex_handle->tid = pthread_self ();
50 mutex_handle->recursion++;
51 if (mutex_handle->abandoned) {
52 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: mutex handle %p was abandoned", __func__, handle);
53 mutex_handle->abandoned = FALSE;
54 *statuscode = WAIT_ABANDONED_0;
57 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
63 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
65 MonoW32HandleMutex *mutex_handle;
67 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
68 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
72 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
73 __func__, mono_w32handle_ops_typename (type), handle);
75 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
76 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
77 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self ());
80 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, but locked %d times by %p",
81 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self (), mutex_handle->recursion, (gpointer) mutex_handle->tid);
86 static void mutex_signal(gpointer handle)
88 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
91 static gboolean mutex_own (gpointer handle, guint32 *statuscode)
93 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, statuscode);
96 static gboolean mutex_is_owned (gpointer handle)
99 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
102 static void namedmutex_signal (gpointer handle)
104 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
107 /* NB, always called with the shared handle lock held */
108 static gboolean namedmutex_own (gpointer handle, guint32 *statuscode)
110 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, statuscode);
113 static gboolean namedmutex_is_owned (gpointer handle)
115 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
118 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
120 /* If the mutex is not currently owned, do nothing and let the
121 * usual wait carry on. If it is owned, check that the owner
122 * is still alive; if it isn't we override the previous owner
123 * and assume that process exited abnormally and failed to
126 MonoW32HandleMutex *mutex_handle;
128 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
129 g_warning ("%s: error looking up %s handle %p",
130 __func__, mono_w32handle_ops_typename (type), handle);
134 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
135 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
138 /* The shared state is not locked when prewait methods are called */
139 static void mutex_prewait (gpointer handle)
141 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
144 /* The shared state is not locked when prewait methods are called */
145 static void namedmutex_prewait (gpointer handle)
147 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
150 static void mutex_details (gpointer data)
152 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
154 #ifdef PTHREAD_POINTER_ID
155 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
157 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
161 static void namedmutex_details (gpointer data)
163 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
165 #ifdef PTHREAD_POINTER_ID
166 g_print ("own: %5p, count: %5u, name: \"%s\"",
167 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
169 g_print ("own: %5ld, count: %5u, name: \"%s\"",
170 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
174 static const gchar* mutex_typename (void)
179 static gsize mutex_typesize (void)
181 return sizeof (MonoW32HandleMutex);
184 static const gchar* namedmutex_typename (void)
189 static gsize namedmutex_typesize (void)
191 return sizeof (MonoW32HandleNamedMutex);
195 mono_w32mutex_init (void)
197 static MonoW32HandleOps mutex_ops = {
199 mutex_signal, /* signal */
201 mutex_is_owned, /* is_owned */
202 NULL, /* special_wait */
203 mutex_prewait, /* prewait */
204 mutex_details, /* details */
205 mutex_typename, /* typename */
206 mutex_typesize, /* typesize */
209 static MonoW32HandleOps namedmutex_ops = {
211 namedmutex_signal, /* signal */
212 namedmutex_own, /* own */
213 namedmutex_is_owned, /* is_owned */
214 NULL, /* special_wait */
215 namedmutex_prewait, /* prewait */
216 namedmutex_details, /* details */
217 namedmutex_typename, /* typename */
218 namedmutex_typesize, /* typesize */
221 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
222 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
224 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
225 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
226 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
227 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
230 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
236 mutex_handle->tid = 0;
237 mutex_handle->recursion = 0;
238 mutex_handle->abandoned = FALSE;
240 handle = mono_w32handle_new (type, mutex_handle);
241 if (handle == INVALID_HANDLE_VALUE) {
242 g_warning ("%s: error creating %s handle",
243 __func__, mono_w32handle_ops_typename (type));
244 SetLastError (ERROR_GEN_FAILURE);
248 thr_ret = mono_w32handle_lock_handle (handle);
249 g_assert (thr_ret == 0);
252 mutex_handle_own (handle, type, &statuscode);
254 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
256 thr_ret = mono_w32handle_unlock_handle (handle);
257 g_assert (thr_ret == 0);
259 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
260 __func__, mono_w32handle_ops_typename (type), handle);
265 static gpointer mutex_create (gboolean owned)
267 MonoW32HandleMutex mutex_handle;
268 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
269 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX));
270 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
273 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
278 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
279 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX));
281 /* w32 seems to guarantee that opening named objects can't race each other */
282 mono_w32handle_namespace_lock ();
284 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
286 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
287 if (handle == INVALID_HANDLE_VALUE) {
288 /* The name has already been used for a different object. */
290 SetLastError (ERROR_INVALID_HANDLE);
292 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
293 SetLastError (ERROR_ALREADY_EXISTS);
295 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
297 /* A new named mutex */
298 MonoW32HandleNamedMutex namedmutex_handle;
300 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
301 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
303 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
308 mono_w32handle_namespace_unlock ();
314 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
320 /* Need to blow away any old errors here, because code tests
321 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
322 * was freshly created */
323 SetLastError (ERROR_SUCCESS);
326 mutex = mutex_create (owned);
328 mutex = namedmutex_create (owned, mono_string_chars (name));
330 if (GetLastError () == ERROR_ALREADY_EXISTS)
338 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
340 MonoW32HandleType type;
341 MonoW32HandleMutex *mutex_handle;
346 if (handle == NULL) {
347 SetLastError (ERROR_INVALID_HANDLE);
351 switch (type = mono_w32handle_get_type (handle)) {
352 case MONO_W32HANDLE_MUTEX:
353 case MONO_W32HANDLE_NAMEDMUTEX:
356 SetLastError (ERROR_INVALID_HANDLE);
360 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
361 g_warning ("%s: error looking up %s handle %p",
362 __func__, mono_w32handle_ops_typename (type), handle);
366 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
367 __func__, mono_w32handle_ops_typename (type), handle);
369 thr_ret = mono_w32handle_lock_handle (handle);
370 g_assert (thr_ret == 0);
372 tid = pthread_self ();
374 if (mutex_handle->abandoned) {
375 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
377 } else if (!pthread_equal (mutex_handle->tid, tid)) {
380 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
381 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid);
385 /* OK, we own this mutex */
386 mutex_handle->recursion--;
388 if (mutex_handle->recursion == 0) {
389 mono_thread_info_disown_mutex (mono_thread_info_current (), handle);
391 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p",
392 __func__, mono_w32handle_ops_typename (type), handle);
394 mutex_handle->tid = 0;
395 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
399 thr_ret = mono_w32handle_unlock_handle (handle);
400 g_assert (thr_ret == 0);
406 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
411 *error = ERROR_SUCCESS;
413 /* w32 seems to guarantee that opening named objects can't race each other */
414 mono_w32handle_namespace_lock ();
416 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
419 __func__, utf8_name);
421 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
422 if (handle == INVALID_HANDLE_VALUE) {
423 /* The name has already been used for a different object. */
424 *error = ERROR_INVALID_HANDLE;
426 } else if (!handle) {
427 /* This name doesn't exist */
428 *error = ERROR_FILE_NOT_FOUND;
432 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
438 mono_w32handle_namespace_unlock ();
444 mono_w32mutex_abandon (gpointer handle, MonoNativeThreadId tid)
446 MonoW32HandleType type;
447 MonoW32HandleMutex *mutex_handle;
450 switch (type = mono_w32handle_get_type (handle)) {
451 case MONO_W32HANDLE_MUTEX:
452 case MONO_W32HANDLE_NAMEDMUTEX:
455 g_assert_not_reached ();
458 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
459 g_warning ("%s: error looking up %s handle %p",
460 __func__, mono_w32handle_ops_typename (type), handle);
464 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandon %s handle %p",
465 __func__, mono_w32handle_ops_typename (type), handle);
467 thr_ret = mono_w32handle_lock_handle (handle);
468 g_assert (thr_ret == 0);
470 if (pthread_equal (mutex_handle->tid, tid)) {
471 mutex_handle->recursion = 0;
472 mutex_handle->tid = 0;
473 mutex_handle->abandoned = TRUE;
475 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
477 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
478 __func__, mono_w32handle_ops_typename (type), handle);
481 thr_ret = mono_w32handle_unlock_handle (handle);
482 g_assert (thr_ret == 0);
485 MonoW32HandleNamespace*
486 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
488 return &mutex->sharedns;