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