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, guint32 *statuscode)
34 MonoW32HandleMutex *mutex_handle;
36 *statuscode = WAIT_OBJECT_0;
38 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
39 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
43 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p, tid %p, recursion %u",
44 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
46 mono_thread_info_own_mutex (mono_thread_info_current (), handle);
48 mutex_handle->tid = pthread_self ();
49 mutex_handle->recursion++;
51 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
57 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
59 MonoW32HandleMutex *mutex_handle;
61 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
62 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
66 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
67 __func__, mono_w32handle_ops_typename (type), handle);
69 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
70 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
71 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self ());
74 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, but locked %d times by %p",
75 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self (), mutex_handle->recursion, (gpointer) mutex_handle->tid);
80 static void mutex_signal(gpointer handle)
82 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
85 static gboolean mutex_own (gpointer handle, guint32 *statuscode)
87 return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, statuscode);
90 static gboolean mutex_is_owned (gpointer handle)
93 return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
96 static void namedmutex_signal (gpointer handle)
98 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
101 /* NB, always called with the shared handle lock held */
102 static gboolean namedmutex_own (gpointer handle, guint32 *statuscode)
104 return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, statuscode);
107 static gboolean namedmutex_is_owned (gpointer handle)
109 return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
112 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
114 /* If the mutex is not currently owned, do nothing and let the
115 * usual wait carry on. If it is owned, check that the owner
116 * is still alive; if it isn't we override the previous owner
117 * and assume that process exited abnormally and failed to
120 MonoW32HandleMutex *mutex_handle;
122 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
123 g_warning ("%s: error looking up %s handle %p",
124 __func__, mono_w32handle_ops_typename (type), handle);
128 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
129 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
132 /* The shared state is not locked when prewait methods are called */
133 static void mutex_prewait (gpointer handle)
135 mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
138 /* The shared state is not locked when prewait methods are called */
139 static void namedmutex_prewait (gpointer handle)
141 mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
144 static void mutex_details (gpointer data)
146 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
148 #ifdef PTHREAD_POINTER_ID
149 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
151 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
155 static void namedmutex_details (gpointer data)
157 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
159 #ifdef PTHREAD_POINTER_ID
160 g_print ("own: %5p, count: %5u, name: \"%s\"",
161 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
163 g_print ("own: %5ld, count: %5u, name: \"%s\"",
164 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
168 static const gchar* mutex_typename (void)
173 static gsize mutex_typesize (void)
175 return sizeof (MonoW32HandleMutex);
178 static const gchar* namedmutex_typename (void)
183 static gsize namedmutex_typesize (void)
185 return sizeof (MonoW32HandleNamedMutex);
189 mono_w32mutex_init (void)
191 static MonoW32HandleOps mutex_ops = {
193 mutex_signal, /* signal */
195 mutex_is_owned, /* is_owned */
196 NULL, /* special_wait */
197 mutex_prewait, /* prewait */
198 mutex_details, /* details */
199 mutex_typename, /* typename */
200 mutex_typesize, /* typesize */
203 static MonoW32HandleOps namedmutex_ops = {
205 namedmutex_signal, /* signal */
206 namedmutex_own, /* own */
207 namedmutex_is_owned, /* is_owned */
208 NULL, /* special_wait */
209 namedmutex_prewait, /* prewait */
210 namedmutex_details, /* details */
211 namedmutex_typename, /* typename */
212 namedmutex_typesize, /* typesize */
215 mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX, &mutex_ops);
216 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
218 mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
219 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
220 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
221 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
224 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
230 mutex_handle->tid = 0;
231 mutex_handle->recursion = 0;
233 handle = mono_w32handle_new (type, mutex_handle);
234 if (handle == INVALID_HANDLE_VALUE) {
235 g_warning ("%s: error creating %s handle",
236 __func__, mono_w32handle_ops_typename (type));
237 SetLastError (ERROR_GEN_FAILURE);
241 thr_ret = mono_w32handle_lock_handle (handle);
242 g_assert (thr_ret == 0);
245 mutex_handle_own (handle, type, &statuscode);
247 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
249 thr_ret = mono_w32handle_unlock_handle (handle);
250 g_assert (thr_ret == 0);
252 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
253 __func__, mono_w32handle_ops_typename (type), handle);
258 static gpointer mutex_create (gboolean owned)
260 MonoW32HandleMutex mutex_handle;
261 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
262 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX));
263 return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
266 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
271 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
272 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX));
274 /* w32 seems to guarantee that opening named objects can't race each other */
275 mono_w32handle_namespace_lock ();
277 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
279 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
280 if (handle == INVALID_HANDLE_VALUE) {
281 /* The name has already been used for a different object. */
283 SetLastError (ERROR_INVALID_HANDLE);
285 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
286 SetLastError (ERROR_ALREADY_EXISTS);
288 /* this is used as creating a new handle */
289 mono_w32handle_ref (handle);
291 /* A new named mutex */
292 MonoW32HandleNamedMutex namedmutex_handle;
294 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
295 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
297 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
302 mono_w32handle_namespace_unlock ();
308 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
314 /* Need to blow away any old errors here, because code tests
315 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
316 * was freshly created */
317 SetLastError (ERROR_SUCCESS);
320 mutex = mutex_create (owned);
322 mutex = namedmutex_create (owned, mono_string_chars (name));
324 if (GetLastError () == ERROR_ALREADY_EXISTS)
332 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
334 MonoW32HandleType type;
335 MonoW32HandleMutex *mutex_handle;
340 if (handle == NULL) {
341 SetLastError (ERROR_INVALID_HANDLE);
345 switch (type = mono_w32handle_get_type (handle)) {
346 case MONO_W32HANDLE_MUTEX:
347 case MONO_W32HANDLE_NAMEDMUTEX:
350 SetLastError (ERROR_INVALID_HANDLE);
354 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
355 g_warning ("%s: error looking up %s handle %p",
356 __func__, mono_w32handle_ops_typename (type), handle);
360 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
361 __func__, mono_w32handle_ops_typename (type), handle);
363 thr_ret = mono_w32handle_lock_handle (handle);
364 g_assert (thr_ret == 0);
366 tid = pthread_self ();
368 if (!pthread_equal (mutex_handle->tid, tid)) {
371 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
372 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid);
376 /* OK, we own this mutex */
377 mutex_handle->recursion--;
379 if (mutex_handle->recursion == 0) {
380 mono_thread_info_disown_mutex (mono_thread_info_current (), handle);
382 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p",
383 __func__, mono_w32handle_ops_typename (type), handle);
385 mutex_handle->tid = 0;
386 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
390 thr_ret = mono_w32handle_unlock_handle (handle);
391 g_assert (thr_ret == 0);
397 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
402 *error = ERROR_SUCCESS;
404 /* w32 seems to guarantee that opening named objects can't race each other */
405 mono_w32handle_namespace_lock ();
407 utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
409 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
410 __func__, utf8_name);
412 handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
413 if (handle == INVALID_HANDLE_VALUE) {
414 /* The name has already been used for a different object. */
415 *error = ERROR_INVALID_HANDLE;
417 } else if (!handle) {
418 /* This name doesn't exist */
419 *error = ERROR_FILE_NOT_FOUND;
423 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
429 mono_w32handle_namespace_unlock ();
435 mono_w32mutex_abandon (gpointer handle, MonoNativeThreadId tid)
437 MonoW32HandleType type;
438 MonoW32HandleMutex *mutex_handle;
441 switch (type = mono_w32handle_get_type (handle)) {
442 case MONO_W32HANDLE_MUTEX:
443 case MONO_W32HANDLE_NAMEDMUTEX:
446 g_assert_not_reached ();
449 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
450 g_warning ("%s: error looking up %s handle %p",
451 __func__, mono_w32handle_ops_typename (type), handle);
455 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandon %s handle %p",
456 __func__, mono_w32handle_ops_typename (type), handle);
458 thr_ret = mono_w32handle_lock_handle (handle);
459 g_assert (thr_ret == 0);
461 if (pthread_equal (mutex_handle->tid, tid)) {
462 mutex_handle->recursion = 0;
463 mutex_handle->tid = 0;
465 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
467 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
468 __func__, mono_w32handle_ops_typename (type), handle);
471 thr_ret = mono_w32handle_unlock_handle (handle);
472 g_assert (thr_ret == 0);
475 MonoW32HandleNamespace*
476 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
478 return &mutex->sharedns;