a39f5ca276163804a73af82022d485a634a10562
[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 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, gboolean *abandoned)
63 {
64         MonoW32HandleMutex *mutex_handle;
65
66         *abandoned = FALSE;
67
68         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
69                 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_get_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_get_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                 *abandoned = TRUE;
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_get_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_get_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_get_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_get_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, gboolean *abandoned)
126 {
127         return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, abandoned);
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, gboolean *abandoned)
143 {
144         return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, abandoned);
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_get_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_get_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         gboolean abandoned;
268
269         mutex_handle->tid = 0;
270         mutex_handle->recursion = 0;
271         mutex_handle->abandoned = FALSE;
272
273         handle = mono_w32handle_new (type, mutex_handle);
274         if (handle == INVALID_HANDLE_VALUE) {
275                 g_warning ("%s: error creating %s handle",
276                         __func__, mono_w32handle_get_typename (type));
277                 SetLastError (ERROR_GEN_FAILURE);
278                 return NULL;
279         }
280
281         mono_w32handle_lock_handle (handle);
282
283         if (owned)
284                 mutex_handle_own (handle, type, &abandoned);
285         else
286                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
287
288         mono_w32handle_unlock_handle (handle);
289
290         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
291                 __func__, mono_w32handle_get_typename (type), handle);
292
293         return handle;
294 }
295
296 static gpointer mutex_create (gboolean owned)
297 {
298         MonoW32HandleMutex mutex_handle;
299         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
300                 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_MUTEX));
301         return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
302 }
303
304 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
305 {
306         gpointer handle;
307         gchar *utf8_name;
308
309         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
310                 __func__, mono_w32handle_get_typename (MONO_W32HANDLE_NAMEDMUTEX));
311
312         /* w32 seems to guarantee that opening named objects can't race each other */
313         mono_w32handle_namespace_lock ();
314
315         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
316
317         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
318         if (handle == INVALID_HANDLE_VALUE) {
319                 /* The name has already been used for a different object. */
320                 handle = NULL;
321                 SetLastError (ERROR_INVALID_HANDLE);
322         } else if (handle) {
323                 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
324                 SetLastError (ERROR_ALREADY_EXISTS);
325
326                 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
327         } else {
328                 /* A new named mutex */
329                 MonoW32HandleNamedMutex namedmutex_handle;
330
331                 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
332                 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
333
334                 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
335         }
336
337         g_free (utf8_name);
338
339         mono_w32handle_namespace_unlock ();
340
341         return handle;
342 }
343
344 gpointer
345 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
346 {
347         gpointer mutex;
348
349         *created = TRUE;
350
351         /* Need to blow away any old errors here, because code tests
352          * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
353          * was freshly created */
354         SetLastError (ERROR_SUCCESS);
355
356         if (!name) {
357                 mutex = mutex_create (owned);
358         } else {
359                 mutex = namedmutex_create (owned, mono_string_chars (name));
360
361                 if (GetLastError () == ERROR_ALREADY_EXISTS)
362                         *created = FALSE;
363         }
364
365         return mutex;
366 }
367
368 MonoBoolean
369 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
370 {
371         MonoW32HandleType type;
372         MonoW32HandleMutex *mutex_handle;
373         pthread_t tid;
374         gboolean ret;
375
376         if (handle == NULL) {
377                 SetLastError (ERROR_INVALID_HANDLE);
378                 return FALSE;
379         }
380
381         switch (type = mono_w32handle_get_type (handle)) {
382         case MONO_W32HANDLE_MUTEX:
383         case MONO_W32HANDLE_NAMEDMUTEX:
384                 break;
385         default:
386                 SetLastError (ERROR_INVALID_HANDLE);
387                 return FALSE;
388         }
389
390         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
391                 g_warning ("%s: error looking up %s handle %p",
392                         __func__, mono_w32handle_get_typename (type), handle);
393                 return FALSE;
394         }
395
396         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p, tid: %p recursion: %d",
397                 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
398
399         mono_w32handle_lock_handle (handle);
400
401         tid = pthread_self ();
402
403         if (mutex_handle->abandoned) {
404                 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
405                 ret = TRUE;
406         } else if (!pthread_equal (mutex_handle->tid, tid)) {
407                 ret = FALSE;
408
409                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
410                             __func__, mono_w32handle_get_typename (type), handle, (long)mutex_handle->tid, (long)tid);
411         } else {
412                 ret = TRUE;
413
414                 /* OK, we own this mutex */
415                 mutex_handle->recursion--;
416
417                 if (mutex_handle->recursion == 0) {
418                         thread_disown_mutex (mono_thread_internal_current (), handle);
419
420                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p, tid: %p recusion : %d",
421                                 __func__, mono_w32handle_get_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
422
423                         mutex_handle->tid = 0;
424                         mono_w32handle_set_signal_state (handle, TRUE, FALSE);
425                 }
426         }
427
428         mono_w32handle_unlock_handle (handle);
429
430         return ret;
431 }
432
433 gpointer
434 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
435 {
436         gpointer handle;
437         gchar *utf8_name;
438
439         *error = ERROR_SUCCESS;
440
441         /* w32 seems to guarantee that opening named objects can't race each other */
442         mono_w32handle_namespace_lock ();
443
444         utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
445
446         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
447                 __func__, utf8_name);
448
449         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
450         if (handle == INVALID_HANDLE_VALUE) {
451                 /* The name has already been used for a different object. */
452                 *error = ERROR_INVALID_HANDLE;
453                 goto cleanup;
454         } else if (!handle) {
455                 /* This name doesn't exist */
456                 *error = ERROR_FILE_NOT_FOUND;
457                 goto cleanup;
458         }
459
460         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
461                 __func__, handle);
462
463 cleanup:
464         g_free (utf8_name);
465
466         mono_w32handle_namespace_unlock ();
467
468         return handle;
469 }
470
471 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                 MonoW32HandleType type;
486                 MonoW32HandleMutex *mutex_handle;
487                 MonoNativeThreadId tid;
488                 gpointer handle;
489
490                 handle = g_ptr_array_index (internal->owned_mutexes, 0);
491
492                 switch (type = mono_w32handle_get_type (handle)) {
493                 case MONO_W32HANDLE_MUTEX:
494                 case MONO_W32HANDLE_NAMEDMUTEX:
495                         break;
496                 default:
497                         g_assert_not_reached ();
498                 }
499
500                 if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
501                         g_error ("%s: error looking up %s handle %p",
502                                 __func__, mono_w32handle_get_typename (type), handle);
503                 }
504
505                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoning %s handle %p",
506                         __func__, mono_w32handle_get_typename (type), handle);
507
508                 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
509
510                 if (!pthread_equal (mutex_handle->tid, tid))
511                         g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
512                                 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
513
514                 mono_w32handle_lock_handle (handle);
515
516                 mutex_handle->recursion = 0;
517                 mutex_handle->tid = 0;
518                 mutex_handle->abandoned = TRUE;
519
520                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
521
522                 thread_disown_mutex (internal, handle);
523
524                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
525                         __func__, mono_w32handle_get_typename (type), handle);
526
527                 mono_w32handle_unlock_handle (handle);
528         }
529
530         g_ptr_array_free (internal->owned_mutexes, TRUE);
531         internal->owned_mutexes = NULL;
532 }
533
534 MonoW32HandleNamespace*
535 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
536 {
537         return &mutex->sharedns;
538 }