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