Correctly handle abandoned mutexes on non-Windows platforms
[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 #include "w32mutex-utils.h"
12
13 #include <pthread.h>
14
15 #include "w32handle-namespace.h"
16 #include "mono/io-layer/io-layer.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 } MonoW32HandleMutex;
25
26 struct MonoW32HandleNamedMutex {
27         MonoW32HandleMutex m;
28         MonoW32HandleNamespace sharedns;
29 };
30
31 static gboolean
32 mutex_handle_own (gpointer handle, MonoW32HandleType type, guint32 *statuscode)
33 {
34         MonoW32HandleMutex *mutex_handle;
35
36         *statuscode = WAIT_OBJECT_0;
37
38         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
39                 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
40                 return FALSE;
41         }
42
43         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p, tid %p, recursion %u",
44                 __func__, mono_w32handle_ops_typename (type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
45
46         mono_thread_info_own_mutex (mono_thread_info_current (), handle);
47
48         mutex_handle->tid = pthread_self ();
49         mutex_handle->recursion++;
50
51         mono_w32handle_set_signal_state (handle, FALSE, FALSE);
52
53         return TRUE;
54 }
55
56 static gboolean
57 mutex_handle_is_owned (gpointer handle, MonoW32HandleType type)
58 {
59         MonoW32HandleMutex *mutex_handle;
60
61         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
62                 g_warning ("%s: error looking up %s handle %p", __func__, mono_w32handle_ops_typename (type), handle);
63                 return FALSE;
64         }
65
66         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing ownership %s handle %p",
67                 __func__, mono_w32handle_ops_typename (type), handle);
68
69         if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
70                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p owned by %p",
71                         __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self ());
72                 return TRUE;
73         } else {
74                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p not owned by %p, but locked %d times by %p",
75                         __func__, mono_w32handle_ops_typename (type), handle, (gpointer) pthread_self (), mutex_handle->recursion, (gpointer) mutex_handle->tid);
76                 return FALSE;
77         }
78 }
79
80 static void mutex_signal(gpointer handle)
81 {
82         ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
83 }
84
85 static gboolean mutex_own (gpointer handle, guint32 *statuscode)
86 {
87         return mutex_handle_own (handle, MONO_W32HANDLE_MUTEX, statuscode);
88 }
89
90 static gboolean mutex_is_owned (gpointer handle)
91 {
92         
93         return mutex_handle_is_owned (handle, MONO_W32HANDLE_MUTEX);
94 }
95
96 static void namedmutex_signal (gpointer handle)
97 {
98         ves_icall_System_Threading_Mutex_ReleaseMutex_internal (handle);
99 }
100
101 /* NB, always called with the shared handle lock held */
102 static gboolean namedmutex_own (gpointer handle, guint32 *statuscode)
103 {
104         return mutex_handle_own (handle, MONO_W32HANDLE_NAMEDMUTEX, statuscode);
105 }
106
107 static gboolean namedmutex_is_owned (gpointer handle)
108 {
109         return mutex_handle_is_owned (handle, MONO_W32HANDLE_NAMEDMUTEX);
110 }
111
112 static void mutex_handle_prewait (gpointer handle, MonoW32HandleType type)
113 {
114         /* If the mutex is not currently owned, do nothing and let the
115          * usual wait carry on.  If it is owned, check that the owner
116          * is still alive; if it isn't we override the previous owner
117          * and assume that process exited abnormally and failed to
118          * clean up.
119          */
120         MonoW32HandleMutex *mutex_handle;
121
122         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
123                 g_warning ("%s: error looking up %s handle %p",
124                         __func__, mono_w32handle_ops_typename (type), handle);
125                 return;
126         }
127
128         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: pre-waiting %s handle %p, owned? %s",
129                 __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->recursion != 0 ? "true" : "false");
130 }
131
132 /* The shared state is not locked when prewait methods are called */
133 static void mutex_prewait (gpointer handle)
134 {
135         mutex_handle_prewait (handle, MONO_W32HANDLE_MUTEX);
136 }
137
138 /* The shared state is not locked when prewait methods are called */
139 static void namedmutex_prewait (gpointer handle)
140 {
141         mutex_handle_prewait (handle, MONO_W32HANDLE_NAMEDMUTEX);
142 }
143
144 static void mutex_details (gpointer data)
145 {
146         MonoW32HandleMutex *mut = (MonoW32HandleMutex *)data;
147         
148 #ifdef PTHREAD_POINTER_ID
149         g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
150 #else
151         g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
152 #endif
153 }
154
155 static void namedmutex_details (gpointer data)
156 {
157         MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)data;
158         
159 #ifdef PTHREAD_POINTER_ID
160         g_print ("own: %5p, count: %5u, name: \"%s\"",
161                 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
162 #else
163         g_print ("own: %5ld, count: %5u, name: \"%s\"",
164                 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
165 #endif
166 }
167
168 static const gchar* mutex_typename (void)
169 {
170         return "Mutex";
171 }
172
173 static gsize mutex_typesize (void)
174 {
175         return sizeof (MonoW32HandleMutex);
176 }
177
178 static const gchar* namedmutex_typename (void)
179 {
180         return "N.Mutex";
181 }
182
183 static gsize namedmutex_typesize (void)
184 {
185         return sizeof (MonoW32HandleNamedMutex);
186 }
187
188 void
189 mono_w32mutex_init (void)
190 {
191         static MonoW32HandleOps mutex_ops = {
192                 NULL,                   /* close */
193                 mutex_signal,           /* signal */
194                 mutex_own,              /* own */
195                 mutex_is_owned,         /* is_owned */
196                 NULL,                   /* special_wait */
197                 mutex_prewait,                  /* prewait */
198                 mutex_details,  /* details */
199                 mutex_typename, /* typename */
200                 mutex_typesize, /* typesize */
201         };
202
203         static MonoW32HandleOps namedmutex_ops = {
204                 NULL,                   /* close */
205                 namedmutex_signal,      /* signal */
206                 namedmutex_own,         /* own */
207                 namedmutex_is_owned,    /* is_owned */
208                 NULL,                   /* special_wait */
209                 namedmutex_prewait,     /* prewait */
210                 namedmutex_details,     /* details */
211                 namedmutex_typename,    /* typename */
212                 namedmutex_typesize,    /* typesize */
213         };
214
215         mono_w32handle_register_ops (MONO_W32HANDLE_MUTEX,      &mutex_ops);
216         mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDMUTEX, &namedmutex_ops);
217
218         mono_w32handle_register_capabilities (MONO_W32HANDLE_MUTEX,
219                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
220         mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDMUTEX,
221                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
222 }
223
224 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32HandleType type, gboolean owned)
225 {
226         gpointer handle;
227         int thr_ret;
228         guint32 statuscode;
229
230         mutex_handle->tid = 0;
231         mutex_handle->recursion = 0;
232
233         handle = mono_w32handle_new (type, mutex_handle);
234         if (handle == INVALID_HANDLE_VALUE) {
235                 g_warning ("%s: error creating %s handle",
236                         __func__, mono_w32handle_ops_typename (type));
237                 SetLastError (ERROR_GEN_FAILURE);
238                 return NULL;
239         }
240
241         thr_ret = mono_w32handle_lock_handle (handle);
242         g_assert (thr_ret == 0);
243
244         if (owned)
245                 mutex_handle_own (handle, type, &statuscode);
246         else
247                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
248
249         thr_ret = mono_w32handle_unlock_handle (handle);
250         g_assert (thr_ret == 0);
251
252         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
253                 __func__, mono_w32handle_ops_typename (type), handle);
254
255         return handle;
256 }
257
258 static gpointer mutex_create (gboolean owned)
259 {
260         MonoW32HandleMutex mutex_handle;
261         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
262                 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_MUTEX));
263         return mutex_handle_create (&mutex_handle, MONO_W32HANDLE_MUTEX, owned);
264 }
265
266 static gpointer namedmutex_create (gboolean owned, const gunichar2 *name)
267 {
268         gpointer handle;
269         gchar *utf8_name;
270
271         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle",
272                 __func__, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDMUTEX));
273
274         /* w32 seems to guarantee that opening named objects can't race each other */
275         mono_w32handle_namespace_lock ();
276
277         utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
278
279         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
280         if (handle == INVALID_HANDLE_VALUE) {
281                 /* The name has already been used for a different object. */
282                 handle = NULL;
283                 SetLastError (ERROR_INVALID_HANDLE);
284         } else if (handle) {
285                 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
286                 SetLastError (ERROR_ALREADY_EXISTS);
287
288                 /* this is used as creating a new handle */
289                 mono_w32handle_ref (handle);
290         } else {
291                 /* A new named mutex */
292                 MonoW32HandleNamedMutex namedmutex_handle;
293
294                 strncpy (&namedmutex_handle.sharedns.name [0], utf8_name, MAX_PATH);
295                 namedmutex_handle.sharedns.name [MAX_PATH] = '\0';
296
297                 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32HANDLE_NAMEDMUTEX, owned);
298         }
299
300         g_free (utf8_name);
301
302         mono_w32handle_namespace_unlock ();
303
304         return handle;
305 }
306
307 gpointer
308 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created)
309 {
310         gpointer mutex;
311
312         *created = TRUE;
313
314         /* Need to blow away any old errors here, because code tests
315          * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
316          * was freshly created */
317         SetLastError (ERROR_SUCCESS);
318
319         if (!name) {
320                 mutex = mutex_create (owned);
321         } else {
322                 mutex = namedmutex_create (owned, mono_string_chars (name));
323
324                 if (GetLastError () == ERROR_ALREADY_EXISTS)
325                         *created = FALSE;
326         }
327
328         return mutex;
329 }
330
331 MonoBoolean
332 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
333 {
334         MonoW32HandleType type;
335         MonoW32HandleMutex *mutex_handle;
336         pthread_t tid;
337         int thr_ret;
338         gboolean ret;
339
340         if (handle == NULL) {
341                 SetLastError (ERROR_INVALID_HANDLE);
342                 return FALSE;
343         }
344
345         switch (type = mono_w32handle_get_type (handle)) {
346         case MONO_W32HANDLE_MUTEX:
347         case MONO_W32HANDLE_NAMEDMUTEX:
348                 break;
349         default:
350                 SetLastError (ERROR_INVALID_HANDLE);
351                 return FALSE;
352         }
353
354         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
355                 g_warning ("%s: error looking up %s handle %p",
356                         __func__, mono_w32handle_ops_typename (type), handle);
357                 return FALSE;
358         }
359
360         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
361                 __func__, mono_w32handle_ops_typename (type), handle);
362
363         thr_ret = mono_w32handle_lock_handle (handle);
364         g_assert (thr_ret == 0);
365
366         tid = pthread_self ();
367
368         if (!pthread_equal (mutex_handle->tid, tid)) {
369                 ret = FALSE;
370
371                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
372                         __func__, mono_w32handle_ops_typename (type), handle, mutex_handle->tid, tid);
373         } else {
374                 ret = TRUE;
375
376                 /* OK, we own this mutex */
377                 mutex_handle->recursion--;
378
379                 if (mutex_handle->recursion == 0) {
380                         mono_thread_info_disown_mutex (mono_thread_info_current (), handle);
381
382                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking %s handle %p",
383                                 __func__, mono_w32handle_ops_typename (type), handle);
384
385                         mutex_handle->tid = 0;
386                         mono_w32handle_set_signal_state (handle, TRUE, FALSE);
387                 }
388         }
389
390         thr_ret = mono_w32handle_unlock_handle (handle);
391         g_assert (thr_ret == 0);
392
393         return ret;
394 }
395
396 gpointer
397 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoString *name, gint32 rights G_GNUC_UNUSED, gint32 *error)
398 {
399         gpointer handle;
400         gchar *utf8_name;
401
402         *error = ERROR_SUCCESS;
403
404         /* w32 seems to guarantee that opening named objects can't race each other */
405         mono_w32handle_namespace_lock ();
406
407         utf8_name = g_utf16_to_utf8 (mono_string_chars (name), -1, NULL, NULL, NULL);
408
409         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named mutex [%s]",
410                 __func__, utf8_name);
411
412         handle = mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDMUTEX, utf8_name);
413         if (handle == INVALID_HANDLE_VALUE) {
414                 /* The name has already been used for a different object. */
415                 *error = ERROR_INVALID_HANDLE;
416                 goto cleanup;
417         } else if (!handle) {
418                 /* This name doesn't exist */
419                 *error = ERROR_FILE_NOT_FOUND;
420                 goto cleanup;
421         }
422
423         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named mutex handle %p",
424                 __func__, handle);
425
426 cleanup:
427         g_free (utf8_name);
428
429         mono_w32handle_namespace_unlock ();
430
431         return handle;
432 }
433
434 void
435 mono_w32mutex_abandon (gpointer handle, MonoNativeThreadId tid)
436 {
437         MonoW32HandleType type;
438         MonoW32HandleMutex *mutex_handle;
439         int thr_ret;
440
441         switch (type = mono_w32handle_get_type (handle)) {
442         case MONO_W32HANDLE_MUTEX:
443         case MONO_W32HANDLE_NAMEDMUTEX:
444                 break;
445         default:
446                 g_assert_not_reached ();
447         }
448
449         if (!mono_w32handle_lookup (handle, type, (gpointer *)&mutex_handle)) {
450                 g_warning ("%s: error looking up %s handle %p",
451                         __func__, mono_w32handle_ops_typename (type), handle);
452                 return;
453         }
454
455         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandon %s handle %p",
456                 __func__, mono_w32handle_ops_typename (type), handle);
457
458         thr_ret = mono_w32handle_lock_handle (handle);
459         g_assert (thr_ret == 0);
460
461         if (pthread_equal (mutex_handle->tid, tid)) {
462                 mutex_handle->recursion = 0;
463                 mutex_handle->tid = 0;
464
465                 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
466
467                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: abandoned %s handle %p",
468                         __func__, mono_w32handle_ops_typename (type), handle);
469         }
470
471         thr_ret = mono_w32handle_unlock_handle (handle);
472         g_assert (thr_ret == 0);
473 }
474
475 MonoW32HandleNamespace*
476 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
477 {
478         return &mutex->sharedns;
479 }