1 /**
2 * \file
3 * Runtime support for managed Mutex on Unix
4 *
5 * Author:
6 * Ludovic Henry (luhenry@microsoft.com)
7 *
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
10
11 #include "w32mutex.h"
12
13 #include <pthread.h>
14
15 #include "w32error.h"
16 #include "w32handle-namespace.h"
17 #include "mono/metadata/object-internals.h"
18 #include "mono/utils/mono-logger-internals.h"
19 #include "mono/utils/mono-threads.h"
20 #include "mono/metadata/w32handle.h"
21
22 #define MAX_PATH 260
23
24 typedef struct {
25 MonoNativeThreadId tid;
26 guint32 recursion;
27 gboolean abandoned;
28 } MonoW32HandleMutex;
29
30 struct MonoW32HandleNamedMutex {
31 MonoW32HandleMutex m;
32 MonoW32HandleNamespace sharedns;
33 };
34
35 gpointer
36 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error);
37
38 static void
thread_own_mutex(MonoInternalThread * internal,gpointer handle,MonoW32Handle * handle_data)39 thread_own_mutex (MonoInternalThread *internal, gpointer handle, MonoW32Handle *handle_data)
40 {
41 /* if we are not on the current thread, there is a
42 * race condition when allocating internal->owned_mutexes */
43 g_assert (mono_thread_internal_is_current (internal));
44
45 if (!internal->owned_mutexes)
46 internal->owned_mutexes = g_ptr_array_new ();
47
48 g_ptr_array_add (internal->owned_mutexes, mono_w32handle_duplicate (handle_data));
49 }
50
51 static void
thread_disown_mutex(MonoInternalThread * internal,gpointer handle)52 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
53 {
54 gboolean removed;
55
56 g_assert (mono_thread_internal_is_current (internal));
57
58 g_assert (internal->owned_mutexes);
59 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
60 g_assert (removed);
61
62 mono_w32handle_close (handle);
63 }
64
65 static void
mutex_handle_signal(MonoW32Handle * handle_data)66 mutex_handle_signal (MonoW32Handle *handle_data)
67 {
68 MonoW32HandleMutex *mutex_handle;
69 pthread_t tid;
70
71 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
72
73 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: signalling %s handle %p, tid: %p recursion: %d",
74 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion);
75
76 tid = pthread_self ();
77
78 if (mutex_handle->abandoned) {
79 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p is abandoned",
80 __func__, mono_w32handle_get_typename (handle_data->type), handle_data);
81 } else if (!pthread_equal (mutex_handle->tid, tid)) {
82 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
83 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (long)mutex_handle->tid, (long)tid);
84 } else {
85 /* OK, we own this mutex */
86 mutex_handle->recursion--;
87
88 if (mutex_handle->recursion == 0) {
89 thread_disown_mutex (mono_thread_internal_current (), handle_data);
90
91 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d",
92 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion);
93
94 mutex_handle->tid = 0;
95 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
96 }
97 }
98 }
99
100 static gboolean
mutex_handle_own(MonoW32Handle * handle_data,gboolean * abandoned)101 mutex_handle_own (MonoW32Handle *handle_data, gboolean *abandoned)
102 {
103 MonoW32HandleMutex *mutex_handle;
104
105 *abandoned = FALSE;
106
107 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
108
109 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: owning %s handle %p, before: [tid: %p, recursion: %d], after: [tid: %p, recursion: %d], abandoned: %s",
110 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion, (gpointer) pthread_self (), mutex_handle->recursion + 1, mutex_handle->abandoned ? "true" : "false");
111
112 if (mutex_handle->recursion != 0) {
113 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
114 mutex_handle->recursion++;
115 } else {
116 mutex_handle->tid = pthread_self ();
117 mutex_handle->recursion = 1;
118
119 thread_own_mutex (mono_thread_internal_current (), handle_data, handle_data);
120 }
121
122 if (mutex_handle->abandoned) {
123 mutex_handle->abandoned = FALSE;
124 *abandoned = TRUE;
125 }
126
127 mono_w32handle_set_signal_state (handle_data, FALSE, FALSE);
128
129 return TRUE;
130 }
131
132 static gboolean
mutex_handle_is_owned(MonoW32Handle * handle_data)133 mutex_handle_is_owned (MonoW32Handle *handle_data)
134 {
135 MonoW32HandleMutex *mutex_handle;
136
137 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
138
139 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: testing ownership %s handle %p",
140 __func__, mono_w32handle_get_typename (handle_data->type), handle_data);
141
142 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
143 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p owned by %p",
144 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self ());
145 return TRUE;
146 } else {
147 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
148 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
149 return FALSE;
150 }
151 }
152
mutex_handle_prewait(MonoW32Handle * handle_data)153 static void mutex_handle_prewait (MonoW32Handle *handle_data)
154 {
155 /* If the mutex is not currently owned, do nothing and let the
156 * usual wait carry on. If it is owned, check that the owner
157 * is still alive; if it isn't we override the previous owner
158 * and assume that process exited abnormally and failed to
159 * clean up.
160 */
161 MonoW32HandleMutex *mutex_handle;
162
163 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
164
165 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: pre-waiting %s handle %p, owned? %s",
166 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, mutex_handle->recursion != 0 ? "true" : "false");
167 }
168
mutex_details(MonoW32Handle * handle_data)169 static void mutex_details (MonoW32Handle *handle_data)
170 {
171 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)handle_data->specific;
172
173 #ifdef PTHREAD_POINTER_ID
174 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
175 #else
176 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
177 #endif
178 }
179
namedmutex_details(MonoW32Handle * handle_data)180 static void namedmutex_details (MonoW32Handle *handle_data)
181 {
182 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)handle_data->specific;
183
184 #ifdef PTHREAD_POINTER_ID
185 g_print ("own: %5p, count: %5u, name: \"%s\"",
186 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
187 #else
188 g_print ("own: %5ld, count: %5u, name: \"%s\"",
189 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
190 #endif
191 }
192
mutex_typename(void)193 static const gchar* mutex_typename (void)
194 {
195 return "Mutex";
196 }
197
mutex_typesize(void)198 static gsize mutex_typesize (void)
199 {
200 return sizeof (MonoW32HandleMutex);
201 }
202
namedmutex_typename(void)203 static const gchar* namedmutex_typename (void)
204 {
205 return "N.Mutex";
206 }
207
namedmutex_typesize(void)208 static gsize namedmutex_typesize (void)
209 {
210 return sizeof (MonoW32HandleNamedMutex);
211 }
212
213 void
mono_w32mutex_init(void)214 mono_w32mutex_init (void)
215 {
216 static MonoW32HandleOps mutex_ops = {
217 NULL, /* close */
218 mutex_handle_signal, /* signal */
219 mutex_handle_own, /* own */
220 mutex_handle_is_owned, /* is_owned */
221 NULL, /* special_wait */
222 mutex_handle_prewait, /* prewait */
223 mutex_details, /* details */
224 mutex_typename, /* typename */
225 mutex_typesize, /* typesize */
226 };
227
228 static MonoW32HandleOps namedmutex_ops = {
229 NULL, /* close */
230 mutex_handle_signal, /* signal */
231 mutex_handle_own, /* own */
232 mutex_handle_is_owned, /* is_owned */
233 NULL, /* special_wait */
234 mutex_handle_prewait, /* prewait */
235 namedmutex_details, /* details */
236 namedmutex_typename, /* typename */
237 namedmutex_typesize, /* typesize */
238 };
239
240 mono_w32handle_register_ops (MONO_W32TYPE_MUTEX, &mutex_ops);
241 mono_w32handle_register_ops (MONO_W32TYPE_NAMEDMUTEX, &namedmutex_ops);
242
243 mono_w32handle_register_capabilities (MONO_W32TYPE_MUTEX,
244 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
245 mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDMUTEX,
246 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
247 }
248
mutex_handle_create(MonoW32HandleMutex * mutex_handle,MonoW32Type type,gboolean owned)249 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32Type type, gboolean owned)
250 {
251 MonoW32Handle *handle_data;
252 gpointer handle;
253 gboolean abandoned;
254
255 mutex_handle->tid = 0;
256 mutex_handle->recursion = 0;
257 mutex_handle->abandoned = FALSE;
258
259 handle = mono_w32handle_new (type, mutex_handle);
260 if (handle == INVALID_HANDLE_VALUE) {
261 g_warning ("%s: error creating %s handle",
262 __func__, mono_w32handle_get_typename (type));
263 mono_w32error_set_last (ERROR_GEN_FAILURE);
264 return NULL;
265 }
266
267 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
268 g_error ("%s: unkown handle %p", __func__, handle);
269
270 if (handle_data->type != type)
271 g_error ("%s: unknown mutex handle %p", __func__, handle);
272
273 mono_w32handle_lock (handle_data);
274
275 if (owned)
276 mutex_handle_own (handle_data, &abandoned);
277 else
278 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
279
280 mono_w32handle_unlock (handle_data);
281
282 /* Balance mono_w32handle_lookup_and_ref */
283 mono_w32handle_unref (handle_data);
284
285 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: created %s handle %p",
286 __func__, mono_w32handle_get_typename (type), handle);
287
288 return handle;
289 }
290
mutex_create(gboolean owned)291 static gpointer mutex_create (gboolean owned)
292 {
293 MonoW32HandleMutex mutex_handle;
294 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle",
295 __func__, mono_w32handle_get_typename (MONO_W32TYPE_MUTEX));
296 return mutex_handle_create (&mutex_handle, MONO_W32TYPE_MUTEX, owned);
297 }
298
namedmutex_create(gboolean owned,const gchar * utf8_name)299 static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name)
300 {
301 gpointer handle;
302
303 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle",
304 __func__, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDMUTEX));
305
306 /* w32 seems to guarantee that opening named objects can't race each other */
307 mono_w32handle_namespace_lock ();
308
309 glong utf8_len = strlen (utf8_name);
310
311 handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX, utf8_name);
312 if (handle == INVALID_HANDLE_VALUE) {
313 /* The name has already been used for a different object. */
314 handle = NULL;
315 mono_w32error_set_last (ERROR_INVALID_HANDLE);
316 } else if (handle) {
317 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
318 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
319
320 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
321 } else {
322 /* A new named mutex */
323 MonoW32HandleNamedMutex namedmutex_handle;
324
325 size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
326 memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len);
327 namedmutex_handle.sharedns.name [len] = '\0';
328
329 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32TYPE_NAMEDMUTEX, owned);
330 }
331
332 mono_w32handle_namespace_unlock ();
333
334 return handle;
335 }
336
337 gpointer
ves_icall_System_Threading_Mutex_CreateMutex_internal(MonoBoolean owned,MonoStringHandle name,MonoBoolean * created,MonoError * error)338 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error)
339 {
340 gpointer mutex;
341
342 error_init (error);
343 *created = TRUE;
344
345 /* Need to blow away any old errors here, because code tests
346 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
347 * was freshly created */
348 mono_w32error_set_last (ERROR_SUCCESS);
349
350 if (MONO_HANDLE_IS_NULL (name)) {
351 mutex = mutex_create (owned);
352 } else {
353 gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
354 return_val_if_nok (error, NULL);
355
356 mutex = namedmutex_create (owned, utf8_name);
357
358 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
359 *created = FALSE;
360 g_free (utf8_name);
361 }
362
363 return mutex;
364 }
365
366 MonoBoolean
ves_icall_System_Threading_Mutex_ReleaseMutex_internal(gpointer handle)367 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
368 {
369 MonoW32Handle *handle_data;
370 MonoW32HandleMutex *mutex_handle;
371 pthread_t tid;
372 gboolean ret;
373
374 if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
375 g_warning ("%s: unkown handle %p", __func__, handle);
376 mono_w32error_set_last (ERROR_INVALID_HANDLE);
377 return FALSE;
378 }
379
380 if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX) {
381 g_warning ("%s: unknown mutex handle %p", __func__, handle);
382 mono_w32error_set_last (ERROR_INVALID_HANDLE);
383 mono_w32handle_unref (handle_data);
384 return FALSE;
385 }
386
387 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
388
389 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: releasing %s handle %p, tid: %p recursion: %d",
390 __func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
391
392 mono_w32handle_lock (handle_data);
393
394 tid = pthread_self ();
395
396 if (mutex_handle->abandoned) {
397 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
398 ret = TRUE;
399 } else if (!pthread_equal (mutex_handle->tid, tid)) {
400 ret = FALSE;
401
402 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
403 __func__, mono_w32handle_get_typename (handle_data->type), handle, (long)mutex_handle->tid, (long)tid);
404 } else {
405 ret = TRUE;
406
407 /* OK, we own this mutex */
408 mutex_handle->recursion--;
409
410 if (mutex_handle->recursion == 0) {
411 thread_disown_mutex (mono_thread_internal_current (), handle);
412
413 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d",
414 __func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
415
416 mutex_handle->tid = 0;
417 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
418 }
419 }
420
421 mono_w32handle_unlock (handle_data);
422 mono_w32handle_unref (handle_data);
423
424 return ret;
425 }
426
427 gpointer
ves_icall_System_Threading_Mutex_OpenMutex_internal(MonoStringHandle name,gint32 rights G_GNUC_UNUSED,gint32 * err,MonoError * error)428 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error)
429 {
430 error_init (error);
431 gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
432 return_val_if_nok (error, NULL);
433 gpointer handle = mono_w32mutex_open (utf8_name, rights, err);
434 g_free (utf8_name);
435 return handle;
436 }
437
438 gpointer
mono_w32mutex_open(const gchar * utf8_name,gint32 right G_GNUC_UNUSED,gint32 * error)439 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error)
440 {
441 gpointer handle;
442
443 *error = ERROR_SUCCESS;
444
445 /* w32 seems to guarantee that opening named objects can't race each other */
446 mono_w32handle_namespace_lock ();
447
448 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: Opening named mutex [%s]",
449 __func__, utf8_name);
450
451 handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_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;
455 goto cleanup;
456 } else if (!handle) {
457 /* This name doesn't exist */
458 *error = ERROR_FILE_NOT_FOUND;
459 goto cleanup;
460 }
461
462 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: returning named mutex handle %p",
463 __func__, handle);
464
465 cleanup:
466 mono_w32handle_namespace_unlock ();
467
468 return handle;
469 }
470
471 void
mono_w32mutex_abandon(void)472 mono_w32mutex_abandon (void)
473 {
474 MonoInternalThread *internal;
475
476 g_assert (mono_thread_internal_current_is_attached ());
477
478 internal = mono_thread_internal_current ();
479 g_assert (internal);
480
481 if (!internal->owned_mutexes)
482 return;
483
484 while (internal->owned_mutexes->len) {
485 MonoW32Handle *handle_data;
486 MonoW32HandleMutex *mutex_handle;
487 MonoNativeThreadId tid;
488 gpointer handle;
489
490 handle = g_ptr_array_index (internal->owned_mutexes, 0);
491
492 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
493 g_error ("%s: unkown handle %p", __func__, handle);
494
495 if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX)
496 g_error ("%s: unkown mutex handle %p", __func__, handle);
497
498 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
499
500 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoning %s handle %p",
501 __func__, mono_w32handle_get_typename (handle_data->type), handle);
502
503 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
504
505 if (!pthread_equal (mutex_handle->tid, tid))
506 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
507 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
508
509 mono_w32handle_lock (handle_data);
510
511 mutex_handle->recursion = 0;
512 mutex_handle->tid = 0;
513 mutex_handle->abandoned = TRUE;
514
515 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
516
517 thread_disown_mutex (internal, handle);
518
519 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoned %s handle %p",
520 __func__, mono_w32handle_get_typename (handle_data->type), handle);
521
522 mono_w32handle_unlock (handle_data);
523 mono_w32handle_unref (handle_data);
524 }
525
526 g_ptr_array_free (internal->owned_mutexes, TRUE);
527 internal->owned_mutexes = NULL;
528 }
529
530 MonoW32HandleNamespace*
mono_w32mutex_get_namespace(MonoW32HandleNamedMutex * mutex)531 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
532 {
533 return &mutex->sharedns;
534 }
535