1 /**
2  * \file
3  * Runtime support for managed Mutex on Unix
4  *
5  * Author:
6  *	Ludovic Henry (luhenry@microsoft.com)
7  *
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10 
11 #include "w32mutex.h"
12 
13 #include <pthread.h>
14 
15 #include "w32error.h"
16 #include "w32handle-namespace.h"
17 #include "mono/metadata/object-internals.h"
18 #include "mono/utils/mono-logger-internals.h"
19 #include "mono/utils/mono-threads.h"
20 #include "mono/metadata/w32handle.h"
21 
22 #define MAX_PATH 260
23 
24 typedef struct {
25 	MonoNativeThreadId tid;
26 	guint32 recursion;
27 	gboolean abandoned;
28 } MonoW32HandleMutex;
29 
30 struct MonoW32HandleNamedMutex {
31 	MonoW32HandleMutex m;
32 	MonoW32HandleNamespace sharedns;
33 };
34 
35 gpointer
36 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error);
37 
38 static void
thread_own_mutex(MonoInternalThread * internal,gpointer handle,MonoW32Handle * handle_data)39 thread_own_mutex (MonoInternalThread *internal, gpointer handle, MonoW32Handle *handle_data)
40 {
41 	/* if we are not on the current thread, there is a
42 	 * race condition when allocating internal->owned_mutexes */
43 	g_assert (mono_thread_internal_is_current (internal));
44 
45 	if (!internal->owned_mutexes)
46 		internal->owned_mutexes = g_ptr_array_new ();
47 
48 	g_ptr_array_add (internal->owned_mutexes, mono_w32handle_duplicate (handle_data));
49 }
50 
51 static void
thread_disown_mutex(MonoInternalThread * internal,gpointer handle)52 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
53 {
54 	gboolean removed;
55 
56 	g_assert (mono_thread_internal_is_current (internal));
57 
58 	g_assert (internal->owned_mutexes);
59 	removed = g_ptr_array_remove (internal->owned_mutexes, handle);
60 	g_assert (removed);
61 
62 	mono_w32handle_close (handle);
63 }
64 
65 static void
mutex_handle_signal(MonoW32Handle * handle_data)66 mutex_handle_signal (MonoW32Handle *handle_data)
67 {
68 	MonoW32HandleMutex *mutex_handle;
69 	pthread_t tid;
70 
71 	mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
72 
73 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: signalling %s handle %p, tid: %p recursion: %d",
74 		__func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion);
75 
76 	tid = pthread_self ();
77 
78 	if (mutex_handle->abandoned) {
79 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p is abandoned",
80 			__func__, mono_w32handle_get_typename (handle_data->type), handle_data);
81 	} else if (!pthread_equal (mutex_handle->tid, tid)) {
82 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
83 			__func__, mono_w32handle_get_typename (handle_data->type), handle_data, (long)mutex_handle->tid, (long)tid);
84 	} else {
85 		/* OK, we own this mutex */
86 		mutex_handle->recursion--;
87 
88 		if (mutex_handle->recursion == 0) {
89 			thread_disown_mutex (mono_thread_internal_current (), handle_data);
90 
91 			mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d",
92 				__func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion);
93 
94 			mutex_handle->tid = 0;
95 			mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
96 		}
97 	}
98 }
99 
100 static gboolean
mutex_handle_own(MonoW32Handle * handle_data,gboolean * abandoned)101 mutex_handle_own (MonoW32Handle *handle_data, gboolean *abandoned)
102 {
103 	MonoW32HandleMutex *mutex_handle;
104 
105 	*abandoned = FALSE;
106 
107 	mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
108 
109 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: owning %s handle %p, before: [tid: %p, recursion: %d], after: [tid: %p, recursion: %d], abandoned: %s",
110 		__func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion, (gpointer) pthread_self (), mutex_handle->recursion + 1, mutex_handle->abandoned ? "true" : "false");
111 
112 	if (mutex_handle->recursion != 0) {
113 		g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
114 		mutex_handle->recursion++;
115 	} else {
116 		mutex_handle->tid = pthread_self ();
117 		mutex_handle->recursion = 1;
118 
119 		thread_own_mutex (mono_thread_internal_current (), handle_data, handle_data);
120 	}
121 
122 	if (mutex_handle->abandoned) {
123 		mutex_handle->abandoned = FALSE;
124 		*abandoned = TRUE;
125 	}
126 
127 	mono_w32handle_set_signal_state (handle_data, FALSE, FALSE);
128 
129 	return TRUE;
130 }
131 
132 static gboolean
mutex_handle_is_owned(MonoW32Handle * handle_data)133 mutex_handle_is_owned (MonoW32Handle *handle_data)
134 {
135 	MonoW32HandleMutex *mutex_handle;
136 
137 	mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
138 
139 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: testing ownership %s handle %p",
140 		__func__, mono_w32handle_get_typename (handle_data->type), handle_data);
141 
142 	if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
143 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p owned by %p",
144 			__func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self ());
145 		return TRUE;
146 	} else {
147 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
148 			__func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
149 		return FALSE;
150 	}
151 }
152 
mutex_handle_prewait(MonoW32Handle * handle_data)153 static void mutex_handle_prewait (MonoW32Handle *handle_data)
154 {
155 	/* If the mutex is not currently owned, do nothing and let the
156 	 * usual wait carry on.  If it is owned, check that the owner
157 	 * is still alive; if it isn't we override the previous owner
158 	 * and assume that process exited abnormally and failed to
159 	 * clean up.
160 	 */
161 	MonoW32HandleMutex *mutex_handle;
162 
163 	mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
164 
165 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: pre-waiting %s handle %p, owned? %s",
166 		__func__, mono_w32handle_get_typename (handle_data->type), handle_data, mutex_handle->recursion != 0 ? "true" : "false");
167 }
168 
mutex_details(MonoW32Handle * handle_data)169 static void mutex_details (MonoW32Handle *handle_data)
170 {
171 	MonoW32HandleMutex *mut = (MonoW32HandleMutex *)handle_data->specific;
172 
173 #ifdef PTHREAD_POINTER_ID
174 	g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
175 #else
176 	g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
177 #endif
178 }
179 
namedmutex_details(MonoW32Handle * handle_data)180 static void namedmutex_details (MonoW32Handle *handle_data)
181 {
182 	MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)handle_data->specific;
183 
184 #ifdef PTHREAD_POINTER_ID
185 	g_print ("own: %5p, count: %5u, name: \"%s\"",
186 		namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
187 #else
188 	g_print ("own: %5ld, count: %5u, name: \"%s\"",
189 		namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
190 #endif
191 }
192 
mutex_typename(void)193 static const gchar* mutex_typename (void)
194 {
195 	return "Mutex";
196 }
197 
mutex_typesize(void)198 static gsize mutex_typesize (void)
199 {
200 	return sizeof (MonoW32HandleMutex);
201 }
202 
namedmutex_typename(void)203 static const gchar* namedmutex_typename (void)
204 {
205 	return "N.Mutex";
206 }
207 
namedmutex_typesize(void)208 static gsize namedmutex_typesize (void)
209 {
210 	return sizeof (MonoW32HandleNamedMutex);
211 }
212 
213 void
mono_w32mutex_init(void)214 mono_w32mutex_init (void)
215 {
216 	static MonoW32HandleOps mutex_ops = {
217 		NULL,			/* close */
218 		mutex_handle_signal,	/* signal */
219 		mutex_handle_own,	/* own */
220 		mutex_handle_is_owned,	/* is_owned */
221 		NULL,			/* special_wait */
222 		mutex_handle_prewait,			/* prewait */
223 		mutex_details,	/* details */
224 		mutex_typename,	/* typename */
225 		mutex_typesize,	/* typesize */
226 	};
227 
228 	static MonoW32HandleOps namedmutex_ops = {
229 		NULL,			/* close */
230 		mutex_handle_signal,	/* signal */
231 		mutex_handle_own,	/* own */
232 		mutex_handle_is_owned,	/* is_owned */
233 		NULL,			/* special_wait */
234 		mutex_handle_prewait,	/* prewait */
235 		namedmutex_details,	/* details */
236 		namedmutex_typename,	/* typename */
237 		namedmutex_typesize,	/* typesize */
238 	};
239 
240 	mono_w32handle_register_ops (MONO_W32TYPE_MUTEX,      &mutex_ops);
241 	mono_w32handle_register_ops (MONO_W32TYPE_NAMEDMUTEX, &namedmutex_ops);
242 
243 	mono_w32handle_register_capabilities (MONO_W32TYPE_MUTEX,
244 		(MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
245 	mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDMUTEX,
246 		(MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
247 }
248 
mutex_handle_create(MonoW32HandleMutex * mutex_handle,MonoW32Type type,gboolean owned)249 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32Type type, gboolean owned)
250 {
251 	MonoW32Handle *handle_data;
252 	gpointer handle;
253 	gboolean abandoned;
254 
255 	mutex_handle->tid = 0;
256 	mutex_handle->recursion = 0;
257 	mutex_handle->abandoned = FALSE;
258 
259 	handle = mono_w32handle_new (type, mutex_handle);
260 	if (handle == INVALID_HANDLE_VALUE) {
261 		g_warning ("%s: error creating %s handle",
262 			__func__, mono_w32handle_get_typename (type));
263 		mono_w32error_set_last (ERROR_GEN_FAILURE);
264 		return NULL;
265 	}
266 
267 	if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
268 		g_error ("%s: unkown handle %p", __func__, handle);
269 
270 	if (handle_data->type != type)
271 		g_error ("%s: unknown mutex handle %p", __func__, handle);
272 
273 	mono_w32handle_lock (handle_data);
274 
275 	if (owned)
276 		mutex_handle_own (handle_data, &abandoned);
277 	else
278 		mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
279 
280 	mono_w32handle_unlock (handle_data);
281 
282 	/* Balance mono_w32handle_lookup_and_ref */
283 	mono_w32handle_unref (handle_data);
284 
285 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: created %s handle %p",
286 		__func__, mono_w32handle_get_typename (type), handle);
287 
288 	return handle;
289 }
290 
mutex_create(gboolean owned)291 static gpointer mutex_create (gboolean owned)
292 {
293 	MonoW32HandleMutex mutex_handle;
294 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle",
295 		__func__, mono_w32handle_get_typename (MONO_W32TYPE_MUTEX));
296 	return mutex_handle_create (&mutex_handle, MONO_W32TYPE_MUTEX, owned);
297 }
298 
namedmutex_create(gboolean owned,const gchar * utf8_name)299 static gpointer namedmutex_create (gboolean owned, const gchar *utf8_name)
300 {
301 	gpointer handle;
302 
303 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle",
304 		__func__, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDMUTEX));
305 
306 	/* w32 seems to guarantee that opening named objects can't race each other */
307 	mono_w32handle_namespace_lock ();
308 
309 	glong utf8_len = strlen (utf8_name);
310 
311 	handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX, utf8_name);
312 	if (handle == INVALID_HANDLE_VALUE) {
313 		/* The name has already been used for a different object. */
314 		handle = NULL;
315 		mono_w32error_set_last (ERROR_INVALID_HANDLE);
316 	} else if (handle) {
317 		/* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
318 		mono_w32error_set_last (ERROR_ALREADY_EXISTS);
319 
320 		/* mono_w32handle_namespace_search_handle already adds a ref to the handle */
321 	} else {
322 		/* A new named mutex */
323 		MonoW32HandleNamedMutex namedmutex_handle;
324 
325 		size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
326 		memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len);
327 		namedmutex_handle.sharedns.name [len] = '\0';
328 
329 		handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32TYPE_NAMEDMUTEX, owned);
330 	}
331 
332 	mono_w32handle_namespace_unlock ();
333 
334 	return handle;
335 }
336 
337 gpointer
ves_icall_System_Threading_Mutex_CreateMutex_internal(MonoBoolean owned,MonoStringHandle name,MonoBoolean * created,MonoError * error)338 ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoStringHandle name, MonoBoolean *created, MonoError *error)
339 {
340 	gpointer mutex;
341 
342 	error_init (error);
343 	*created = TRUE;
344 
345 	/* Need to blow away any old errors here, because code tests
346 	 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
347 	 * was freshly created */
348 	mono_w32error_set_last (ERROR_SUCCESS);
349 
350 	if (MONO_HANDLE_IS_NULL (name)) {
351 		mutex = mutex_create (owned);
352 	} else {
353 		gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
354 		return_val_if_nok (error, NULL);
355 
356 		mutex = namedmutex_create (owned, utf8_name);
357 
358 		if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
359 			*created = FALSE;
360 		g_free (utf8_name);
361 	}
362 
363 	return mutex;
364 }
365 
366 MonoBoolean
ves_icall_System_Threading_Mutex_ReleaseMutex_internal(gpointer handle)367 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
368 {
369 	MonoW32Handle *handle_data;
370 	MonoW32HandleMutex *mutex_handle;
371 	pthread_t tid;
372 	gboolean ret;
373 
374 	if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
375 		g_warning ("%s: unkown handle %p", __func__, handle);
376 		mono_w32error_set_last (ERROR_INVALID_HANDLE);
377 		return FALSE;
378 	}
379 
380 	if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX) {
381 		g_warning ("%s: unknown mutex handle %p", __func__, handle);
382 		mono_w32error_set_last (ERROR_INVALID_HANDLE);
383 		mono_w32handle_unref (handle_data);
384 		return FALSE;
385 	}
386 
387 	mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
388 
389 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: releasing %s handle %p, tid: %p recursion: %d",
390 		__func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
391 
392 	mono_w32handle_lock (handle_data);
393 
394 	tid = pthread_self ();
395 
396 	if (mutex_handle->abandoned) {
397 		// The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
398 		ret = TRUE;
399 	} else if (!pthread_equal (mutex_handle->tid, tid)) {
400 		ret = FALSE;
401 
402 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
403 			    __func__, mono_w32handle_get_typename (handle_data->type), handle, (long)mutex_handle->tid, (long)tid);
404 	} else {
405 		ret = TRUE;
406 
407 		/* OK, we own this mutex */
408 		mutex_handle->recursion--;
409 
410 		if (mutex_handle->recursion == 0) {
411 			thread_disown_mutex (mono_thread_internal_current (), handle);
412 
413 			mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d",
414 				__func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
415 
416 			mutex_handle->tid = 0;
417 			mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
418 		}
419 	}
420 
421 	mono_w32handle_unlock (handle_data);
422 	mono_w32handle_unref (handle_data);
423 
424 	return ret;
425 }
426 
427 gpointer
ves_icall_System_Threading_Mutex_OpenMutex_internal(MonoStringHandle name,gint32 rights G_GNUC_UNUSED,gint32 * err,MonoError * error)428 ves_icall_System_Threading_Mutex_OpenMutex_internal (MonoStringHandle name, gint32 rights G_GNUC_UNUSED, gint32 *err, MonoError *error)
429 {
430 	error_init (error);
431 	gchar *utf8_name = mono_string_handle_to_utf8 (name, error);
432 	return_val_if_nok (error, NULL);
433 	gpointer handle = mono_w32mutex_open (utf8_name, rights, err);
434 	g_free (utf8_name);
435 	return handle;
436 }
437 
438 gpointer
mono_w32mutex_open(const gchar * utf8_name,gint32 right G_GNUC_UNUSED,gint32 * error)439 mono_w32mutex_open (const gchar* utf8_name, gint32 right G_GNUC_UNUSED, gint32 *error)
440 {
441 	gpointer handle;
442 
443 	*error = ERROR_SUCCESS;
444 
445 	/* w32 seems to guarantee that opening named objects can't race each other */
446 	mono_w32handle_namespace_lock ();
447 
448 	mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: Opening named mutex [%s]",
449 		__func__, utf8_name);
450 
451 	handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_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_MUTEX, "%s: returning named mutex handle %p",
463 		__func__, handle);
464 
465 cleanup:
466 	mono_w32handle_namespace_unlock ();
467 
468 	return handle;
469 }
470 
471 void
mono_w32mutex_abandon(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 		MonoW32Handle *handle_data;
486 		MonoW32HandleMutex *mutex_handle;
487 		MonoNativeThreadId tid;
488 		gpointer handle;
489 
490 		handle = g_ptr_array_index (internal->owned_mutexes, 0);
491 
492 		if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
493 			g_error ("%s: unkown handle %p", __func__, handle);
494 
495 		if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX)
496 			g_error ("%s: unkown mutex handle %p", __func__, handle);
497 
498 		mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
499 
500 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoning %s handle %p",
501 			__func__, mono_w32handle_get_typename (handle_data->type), handle);
502 
503 		tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
504 
505 		if (!pthread_equal (mutex_handle->tid, tid))
506 			g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
507 				__func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
508 
509 		mono_w32handle_lock (handle_data);
510 
511 		mutex_handle->recursion = 0;
512 		mutex_handle->tid = 0;
513 		mutex_handle->abandoned = TRUE;
514 
515 		mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
516 
517 		thread_disown_mutex (internal, handle);
518 
519 		mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoned %s handle %p",
520 			__func__, mono_w32handle_get_typename (handle_data->type), handle);
521 
522 		mono_w32handle_unlock (handle_data);
523 		mono_w32handle_unref (handle_data);
524 	}
525 
526 	g_ptr_array_free (internal->owned_mutexes, TRUE);
527 	internal->owned_mutexes = NULL;
528 }
529 
530 MonoW32HandleNamespace*
mono_w32mutex_get_namespace(MonoW32HandleNamedMutex * mutex)531 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
532 {
533 	return &mutex->sharedns;
534 }
535