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;
26 struct MonoW32HandleNamedMutex {
28 MonoW32HandleNamespace sharedns;
32 mutex_handle_own (gpointer handle, MonoW32HandleType type)
34 MonoW32HandleMutex *mutex_handle;
36 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
37 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
41 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p, tid %p, recursion %u",
42 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
44 mono_thread_info_own_mutex (mono_thread_info_current (), handle);
46 mutex_handle->tid = pthread_self ();
47 mutex_handle->recursion++;
49 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
55 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
57 MonoW32HandleMutex *mutex_handle;
59 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
60 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
64 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
65 __func__, mono_w32handle_ops_typename (type), handle);
67 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
68 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
69 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self ());
72 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, but locked %d times by %p",
73 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self (), mutex_handle->recursion, (gpointer) mutex_handle->tid);
78 static void mutex_signal(gpointer handle)
80 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
83 static gboolean mutex_own (gpointer handle)
85 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX);
88 static gboolean mutex_is_owned (gpointer handle)
91 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
94 static void namedmutex_signal (gpointer handle)
96 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
99 /* NB, always called with the shared handle lock held */
100 static gboolean namedmutex_own (gpointer handle)
102 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX);
105 static gboolean namedmutex_is_owned (gpointer handle)
107 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
110 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
112 /* If the mutex is not currently owned, do nothing and let the
113 * usual wait carry on. If it is owned, check that the owner
114 * is still alive; if it isn't we override the previous owner
115 * and assume that process exited abnormally and failed to
118 MonoW32HandleMutex *mutex_handle;
120 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
121 g_warning ("%s: error looking up %s handle %p",
122 __func__, mono_w32handle_ops_typename (type), handle);
126 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
127 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
130 /* The shared state is not locked when prewait methods are called */
131 static void mutex_prewait (gpointer handle)
133 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
136 /* The shared state is not locked when prewait methods are called */
137 static void namedmutex_prewait (gpointer handle)
139 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
142 static void mutex_details (gpointer data)
144 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
146 #ifdef PTHREAD_POINTER_ID
147 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
149 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
153 static void namedmutex_details (gpointer data)
155 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
157 #ifdef PTHREAD_POINTER_ID
158 g_print ("own: %5p, count: %5u, name: \"%s\"",
159 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
161 g_print ("own: %5ld, count: %5u, name: \"%s\"",
162 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
166 static const gchar* mutex_typename (void)
171 static gsize mutex_typesize (void)
173 return sizeof (MonoW32HandleMutex);
176 static const gchar* namedmutex_typename (void)
181 static gsize namedmutex_typesize (void)
183 return sizeof (MonoW32HandleNamedMutex);
187 mono_w32mutex_init (void)
189 static MonoW32HandleOps mutex_ops = {
191 mutex_signal, /* signal */
193 mutex_is_owned, /* is_owned */
194 NULL, /* special_wait */
195 mutex_prewait, /* prewait */
196 mutex_details, /* details */
197 mutex_typename, /* typename */
198 mutex_typesize, /* typesize */
201 static MonoW32HandleOps namedmutex_ops = {
203 namedmutex_signal, /* signal */
204 namedmutex_own, /* own */
205 namedmutex_is_owned, /* is_owned */
206 NULL, /* special_wait */
207 namedmutex_prewait, /* prewait */
208 namedmutex_details, /* details */
209 namedmutex_typename, /* typename */
210 namedmutex_typesize, /* typesize */
213 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
214 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
216 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
217 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
218 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
219 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
222 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
227 mutex_handle->tid = 0;
228 mutex_handle->recursion = 0;
230 handle = mono_w32handle_new (type, mutex_handle);
231 if (handle == INVALID_HANDLE_VALUE) {
232 g_warning ("%s: error creating %s handle",
233 __func__, mono_w32handle_ops_typename (type));
234 SetLastError (ERROR_GEN_FAILURE);
238 thr_ret = mono_w32handle_lock_handle (handle);
239 g_assert (thr_ret == 0);
242 mutex_handle_own (handle, type);
244 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
246 thr_ret = mono_w32handle_unlock_handle (handle);
247 g_assert (thr_ret == 0);
249 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
250 __func__, mono_w32handle_ops_typename (type), handle);
255 static gpointer mutex_create (gboolean owned)
257 MonoW32HandleMutex mutex_handle;
258 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
259 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX));
260 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
263 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
268 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
269 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX));
271 /* w32 seems to guarantee that opening named objects can't race each other */
272 mono_w32handle_namespace_lock ();
274 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
276 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
277 if (handle == INVALID_HANDLE_VALUE) {
278 /* The name has already been used for a different object. */
280 SetLastError (ERROR_INVALID_HANDLE);
282 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
283 SetLastError (ERROR_ALREADY_EXISTS);
285 /* this is used as creating a new handle */
286 mono_w32handle_ref (handle);
288 /* A new named mutex */
289 MonoW32HandleNamedMutex namedmutex_handle;
291 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
292 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
294 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
299 mono_w32handle_namespace_unlock ();
305 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
311 /* Need to blow away any old errors here, because code tests
312 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
313 * was freshly created */
314 SetLastError (ERROR_SUCCESS);
317 mutex = mutex_create (owned);
319 mutex = namedmutex_create (owned, mono_string_chars (name));
321 if (GetLastError () == ERROR_ALREADY_EXISTS)
329 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
331 MonoW32HandleType type;
332 MonoW32HandleMutex *mutex_handle;
337 if (handle == NULL) {
338 SetLastError (ERROR_INVALID_HANDLE);
342 switch (type = mono_w32handle_get_type (handle)) {
343 case MONO_W32HANDLE_MUTEX:
344 case MONO_W32HANDLE_NAMEDMUTEX:
347 SetLastError (ERROR_INVALID_HANDLE);
351 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
352 g_warning ("%s: error looking up %s handle %p",
353 __func__, mono_w32handle_ops_typename (type), handle);
357 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
358 __func__, mono_w32handle_ops_typename (type), handle);
360 thr_ret = mono_w32handle_lock_handle (handle);
361 g_assert (thr_ret == 0);
363 tid = pthread_self ();
365 if (!pthread_equal (mutex_handle->tid, tid)) {
368 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
369 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid);
373 /* OK, we own this mutex */
374 mutex_handle->recursion--;
376 if (mutex_handle->recursion == 0) {
377 mono_thread_info_disown_mutex (mono_thread_info_current (), handle);
379 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p",
380 __func__, mono_w32handle_ops_typename (type), handle);
382 mutex_handle->tid = 0;
383 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
387 thr_ret = mono_w32handle_unlock_handle (handle);
388 g_assert (thr_ret == 0);
394 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
399 *error = ERROR_SUCCESS;
401 /* w32 seems to guarantee that opening named objects can't race each other */
402 mono_w32handle_namespace_lock ();
404 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
406 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
407 __func__, utf8_name);
409 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
410 if (handle == INVALID_HANDLE_VALUE) {
411 /* The name has already been used for a different object. */
412 *error = ERROR_INVALID_HANDLE;
414 } else if (!handle) {
415 /* This name doesn't exist */
416 *error = ERROR_FILE_NOT_FOUND;
420 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
426 mono_w32handle_namespace_unlock ();
432 mono_w32mutex_abandon (gpointer handle, MonoNativeThreadId tid)
434 MonoW32HandleType type;
435 MonoW32HandleMutex *mutex_handle;
438 switch (type = mono_w32handle_get_type (handle)) {
439 case MONO_W32HANDLE_MUTEX:
440 case MONO_W32HANDLE_NAMEDMUTEX:
443 g_assert_not_reached ();
446 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
447 g_warning ("%s: error looking up %s handle %p",
448 __func__, mono_w32handle_ops_typename (type), handle);
452 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandon %s handle %p",
453 __func__, mono_w32handle_ops_typename (type), handle);
455 thr_ret = mono_w32handle_lock_handle (handle);
456 g_assert (thr_ret == 0);
458 if (pthread_equal (mutex_handle->tid, tid)) {
459 mutex_handle->recursion = 0;
460 mutex_handle->tid = 0;
462 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
464 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
465 __func__, mono_w32handle_ops_typename (type), handle);
468 thr_ret = mono_w32handle_unlock_handle (handle);
469 g_assert (thr_ret == 0);
472 MonoW32HandleNamespace*
473 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
475 return &mutex->sharedns;