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