[io-layer] Extract file (#4255)
[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/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                 SetLastError (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         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
318
319         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
320         if (handle == INVALID_HANDLE_VALUE) {
321                 /* The name has already been used for a different object. */
322                 handle = NULL;
323                 SetLastError (ERROR_INVALID_HANDLE);
324         } else if (handle) {
325                 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
326                 SetLastError (ERROR_ALREADY_EXISTS);
327
328                 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
329         } else {
330                 /* A new named mutex */
331                 MonoW32HandleNamedMutex namedmutex_handle;
332
333                 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
334                 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
335
336                 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
337         }
338
339         g_free (utf8_name);
340
341         mono_w32handle_namespace_unlock ();
342
343         return handle;
344 }
345
346 gpointer
347 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
348 {
349         gpointer mutex;
350
351         *created = TRUE;
352
353         /* Need to blow away any old errors here, because code tests
354          * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
355          * was freshly created */
356         SetLastError (ERROR_SUCCESS);
357
358         if (!name) {
359                 mutex = mutex_create (owned);
360         } else {
361                 mutex = namedmutex_create (owned, mono_string_chars (name));
362
363                 if (GetLastError () == ERROR_ALREADY_EXISTS)
364                         *created = FALSE;
365         }
366
367         return mutex;
368 }
369
370 MonoBoolean
371 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
372 {
373         MonoW32HandleType type;
374         MonoW32HandleMutex *mutex_handle;
375         pthread_t tid;
376         gboolean ret;
377
378         if (handle == NULL) {
379                 SetLastError (ERROR_INVALID_HANDLE);
380                 return FALSE;
381         }
382
383         switch (type = mono_w32handle_get_type (handle)) {
384         case MONO_W32HANDLE_MUTEX:
385         case MONO_W32HANDLE_NAMEDMUTEX:
386                 break;
387         default:
388                 SetLastError (ERROR_INVALID_HANDLE);
389                 return FALSE;
390         }
391
392         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
393                 g_warning ("%s: error looking up %s handle %p",
394                         __func__, mono_w32handle_get_typename (type), handle);
395                 return FALSE;
396         }
397
398         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
399                 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
400
401         mono_w32handle_lock_handle (handle);
402
403         tid = pthread_self ();
404
405         if (mutex_handle->abandoned) {
406                 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
407                 ret = TRUE;
408         } else if (!pthread_equal (mutex_handle->tid, tid)) {
409                 ret = FALSE;
410
411                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
412                             __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
413         } else {
414                 ret = TRUE;
415
416                 /* OK, we own this mutex */
417                 mutex_handle->recursion--;
418
419                 if (mutex_handle->recursion == 0) {
420                         thread_disown_mutex (mono_thread_internal_current (), handle);
421
422                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
423                                 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
424
425                         mutex_handle->tid = 0;
426                         mono_w32handle_set_signal_state (handle, TRUE, FALSE);
427                 }
428         }
429
430         mono_w32handle_unlock_handle (handle);
431
432         return ret;
433 }
434
435 gpointer
436 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
437 {
438         gpointer handle;
439         gchar *utf8_name;
440
441         *error = ERROR_SUCCESS;
442
443         /* w32 seems to guarantee that opening named objects can't race each other */
444         mono_w32handle_namespace_lock ();
445
446         utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
447
448         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
449                 __func__, utf8_name);
450
451         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_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, "%s: returning named mutex handle %p",
463                 __func__, handle);
464
465 cleanup:
466         g_free (utf8_name);
467
468         mono_w32handle_namespace_unlock ();
469
470         return handle;
471 }
472
473 void
474 mono_w32mutex_abandon (void)
475 {
476         MonoInternalThread *internal;
477
478         g_assert (mono_thread_internal_current_is_attached ());
479
480         internal = mono_thread_internal_current ();
481         g_assert (internal);
482
483         if (!internal->owned_mutexes)
484                 return;
485
486         while (internal->owned_mutexes->len) {
487                 MonoW32HandleType type;
488                 MonoW32HandleMutex *mutex_handle;
489                 MonoNativeThreadId tid;
490                 gpointer handle;
491
492                 handle = g_ptr_array_index (internal->owned_mutexes, 0);
493
494                 switch (type = mono_w32handle_get_type (handle)) {
495                 case MONO_W32HANDLE_MUTEX:
496                 case MONO_W32HANDLE_NAMEDMUTEX:
497                         break;
498                 default:
499                         g_assert_not_reached ();
500                 }
501
502                 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
503                         g_error ("%s: error looking up %s handle %p",
504                                 __func__, mono_w32handle_get_typename (type), handle);
505                 }
506
507                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
508                         __func__, mono_w32handle_get_typename (type), handle);
509
510                 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
511
512                 if (!pthread_equal (mutex_handle->tid, tid))
513                         g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
514                                 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
515
516                 mono_w32handle_lock_handle (handle);
517
518                 mutex_handle->recursion = 0;
519                 mutex_handle->tid = 0;
520                 mutex_handle->abandoned = TRUE;
521
522                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
523
524                 thread_disown_mutex (internal, handle);
525
526                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
527                         __func__, mono_w32handle_get_typename (type), handle);
528
529                 mono_w32handle_unlock_handle (handle);
530         }
531
532         g_ptr_array_free (internal->owned_mutexes, TRUE);
533         internal->owned_mutexes = NULL;
534 }
535
536 MonoW32HandleNamespace*
537 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
538 {
539         return &mutex->sharedns;
540 }