[netcore] Implement Thread.GetCurrentProcessorId (#18450)
[mono-project.git] / mono / metadata / w32mutex-unix.c
blob0e1da775ff9612496c2800dbe0451322982735d0
1 /**
2 * \file
3 * Runtime support for managed Mutex on Unix
5 * Author:
6 * Ludovic Henry (luhenry@microsoft.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
11 #include "w32mutex.h"
13 #include <pthread.h>
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 #include "icall-decl.h"
23 #define MAX_PATH 260
25 typedef struct {
26 MonoNativeThreadId tid;
27 guint32 recursion;
28 gboolean abandoned;
29 } MonoW32HandleMutex;
31 struct MonoW32HandleNamedMutex {
32 MonoW32HandleMutex m;
33 MonoW32HandleNamespace sharedns;
36 static gpointer
37 mono_w32mutex_open (const char* utf8_name, gint32 rights G_GNUC_UNUSED, gint32 *win32error);
39 static void
40 thread_own_mutex (MonoInternalThread *internal, gpointer handle, MonoW32Handle *handle_data)
42 // Thread and InternalThread are pinned/mature.
43 // Take advantage of that and do not use handles here.
45 /* if we are not on the current thread, there is a
46 * race condition when allocating internal->owned_mutexes */
47 g_assert (mono_thread_internal_is_current (internal));
49 if (!internal->owned_mutexes)
50 internal->owned_mutexes = g_ptr_array_new ();
52 g_ptr_array_add (internal->owned_mutexes, mono_w32handle_duplicate (handle_data));
55 static void
56 thread_disown_mutex (MonoInternalThread *internal, gpointer handle)
58 // Thread and InternalThread are pinned/mature.
59 // Take advantage of that and do not use handles here.
60 gboolean removed;
62 g_assert (mono_thread_internal_is_current (internal));
64 g_assert (internal->owned_mutexes);
65 removed = g_ptr_array_remove (internal->owned_mutexes, handle);
66 g_assert (removed);
68 mono_w32handle_close (handle);
71 static gint32
72 mutex_handle_signal (MonoW32Handle *handle_data)
74 MonoW32HandleMutex *mutex_handle;
75 pthread_t tid;
77 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
79 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: signalling %s handle %p, tid: %p recursion: %d",
80 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion);
82 tid = pthread_self ();
84 if (mutex_handle->abandoned) {
85 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p is abandoned",
86 __func__, mono_w32handle_get_typename (handle_data->type), handle_data);
87 } else if (!pthread_equal (mutex_handle->tid, tid)) {
88 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
89 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (long)mutex_handle->tid, (long)tid);
90 return MONO_W32HANDLE_WAIT_RET_NOT_OWNED_BY_CALLER;
91 } else {
92 /* OK, we own this mutex */
93 mutex_handle->recursion--;
95 if (mutex_handle->recursion == 0) {
96 thread_disown_mutex (mono_thread_internal_current (), handle_data);
98 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d",
99 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) mutex_handle->tid, mutex_handle->recursion);
101 mutex_handle->tid = 0;
102 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
105 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
108 static gboolean
109 mutex_handle_own (MonoW32Handle *handle_data, gboolean *abandoned)
111 MonoW32HandleMutex *mutex_handle;
113 *abandoned = FALSE;
115 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
117 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",
118 __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");
120 if (mutex_handle->recursion != 0) {
121 g_assert (pthread_equal (pthread_self (), mutex_handle->tid));
122 mutex_handle->recursion++;
123 } else {
124 mutex_handle->tid = pthread_self ();
125 mutex_handle->recursion = 1;
127 thread_own_mutex (mono_thread_internal_current (), handle_data, handle_data);
130 if (mutex_handle->abandoned) {
131 mutex_handle->abandoned = FALSE;
132 *abandoned = TRUE;
135 mono_w32handle_set_signal_state (handle_data, FALSE, FALSE);
136 return TRUE;
139 static gboolean
140 mutex_handle_is_owned (MonoW32Handle *handle_data)
142 MonoW32HandleMutex *mutex_handle;
144 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
146 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: testing ownership %s handle %p",
147 __func__, mono_w32handle_get_typename (handle_data->type), handle_data);
149 if (mutex_handle->recursion > 0 && pthread_equal (mutex_handle->tid, pthread_self ())) {
150 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p owned by %p",
151 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self ());
152 return TRUE;
153 } else {
154 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: %s handle %p not owned by %p, tid: %p recursion: %d",
155 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, (gpointer) pthread_self (), (gpointer) mutex_handle->tid, mutex_handle->recursion);
156 return FALSE;
160 static void mutex_handle_prewait (MonoW32Handle *handle_data)
162 /* If the mutex is not currently owned, do nothing and let the
163 * usual wait carry on. If it is owned, check that the owner
164 * is still alive; if it isn't we override the previous owner
165 * and assume that process exited abnormally and failed to
166 * clean up.
168 MonoW32HandleMutex *mutex_handle;
170 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
172 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: pre-waiting %s handle %p, owned? %s",
173 __func__, mono_w32handle_get_typename (handle_data->type), handle_data, mutex_handle->recursion != 0 ? "true" : "false");
176 static void mutex_details (MonoW32Handle *handle_data)
178 MonoW32HandleMutex *mut = (MonoW32HandleMutex *)handle_data->specific;
180 #ifdef PTHREAD_POINTER_ID
181 g_print ("own: %5p, count: %5u", mut->tid, mut->recursion);
182 #else
183 g_print ("own: %5ld, count: %5u", mut->tid, mut->recursion);
184 #endif
187 static void namedmutex_details (MonoW32Handle *handle_data)
189 MonoW32HandleNamedMutex *namedmut = (MonoW32HandleNamedMutex *)handle_data->specific;
191 #ifdef PTHREAD_POINTER_ID
192 g_print ("own: %5p, count: %5u, name: \"%s\"",
193 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
194 #else
195 g_print ("own: %5ld, count: %5u, name: \"%s\"",
196 namedmut->m.tid, namedmut->m.recursion, namedmut->sharedns.name);
197 #endif
200 static const gchar* mutex_typename (void)
202 return "Mutex";
205 static gsize mutex_typesize (void)
207 return sizeof (MonoW32HandleMutex);
210 static const gchar* namedmutex_typename (void)
212 return "N.Mutex";
215 static gsize namedmutex_typesize (void)
217 return sizeof (MonoW32HandleNamedMutex);
220 void
221 mono_w32mutex_init (void)
223 static const MonoW32HandleOps mutex_ops = {
224 NULL, /* close */
225 mutex_handle_signal, /* signal */
226 mutex_handle_own, /* own */
227 mutex_handle_is_owned, /* is_owned */
228 NULL, /* special_wait */
229 mutex_handle_prewait, /* prewait */
230 mutex_details, /* details */
231 mutex_typename, /* typename */
232 mutex_typesize, /* typesize */
235 static const MonoW32HandleOps namedmutex_ops = {
236 NULL, /* close */
237 mutex_handle_signal, /* signal */
238 mutex_handle_own, /* own */
239 mutex_handle_is_owned, /* is_owned */
240 NULL, /* special_wait */
241 mutex_handle_prewait, /* prewait */
242 namedmutex_details, /* details */
243 namedmutex_typename, /* typename */
244 namedmutex_typesize, /* typesize */
247 mono_w32handle_register_ops (MONO_W32TYPE_MUTEX, &mutex_ops);
248 mono_w32handle_register_ops (MONO_W32TYPE_NAMEDMUTEX, &namedmutex_ops);
250 mono_w32handle_register_capabilities (MONO_W32TYPE_MUTEX,
251 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
252 mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDMUTEX,
253 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL | MONO_W32HANDLE_CAP_OWN));
256 static gpointer mutex_handle_create (MonoW32HandleMutex *mutex_handle, MonoW32Type type, gboolean owned)
258 MonoW32Handle *handle_data;
259 gpointer handle;
260 gboolean abandoned;
262 mutex_handle->tid = 0;
263 mutex_handle->recursion = 0;
264 mutex_handle->abandoned = FALSE;
266 handle = mono_w32handle_new (type, mutex_handle);
267 if (handle == INVALID_HANDLE_VALUE) {
268 g_warning ("%s: error creating %s handle",
269 __func__, mono_w32handle_get_typename (type));
270 mono_w32error_set_last (ERROR_GEN_FAILURE);
271 return NULL;
274 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
275 g_error ("%s: unkown handle %p", __func__, handle);
277 if (handle_data->type != type)
278 g_error ("%s: unknown mutex handle %p", __func__, handle);
280 mono_w32handle_lock (handle_data);
282 if (owned)
283 mutex_handle_own (handle_data, &abandoned);
284 else
285 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
287 mono_w32handle_unlock (handle_data);
289 /* Balance mono_w32handle_lookup_and_ref */
290 mono_w32handle_unref (handle_data);
292 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: created %s handle %p",
293 __func__, mono_w32handle_get_typename (type), handle);
295 return handle;
298 static gpointer mutex_create (gboolean owned)
300 MonoW32HandleMutex mutex_handle;
301 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle",
302 __func__, mono_w32handle_get_typename (MONO_W32TYPE_MUTEX));
303 return mutex_handle_create (&mutex_handle, MONO_W32TYPE_MUTEX, owned);
306 static gpointer
307 namedmutex_create (gboolean owned, const char *utf8_name, gsize utf8_len)
309 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: creating %s handle",
310 __func__, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDMUTEX));
312 // Opening named objects does not race.
313 mono_w32handle_namespace_lock ();
315 gpointer handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX, utf8_name);
317 if (handle == INVALID_HANDLE_VALUE) {
318 /* The name has already been used for a different object. */
319 handle = NULL;
320 mono_w32error_set_last (ERROR_INVALID_HANDLE);
321 } else if (handle) {
322 /* Not an error, but this is how the caller is informed that the mutex wasn't freshly created */
323 mono_w32error_set_last (ERROR_ALREADY_EXISTS);
325 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
326 } else {
327 /* A new named mutex */
328 MonoW32HandleNamedMutex namedmutex_handle;
330 // FIXME Silent truncation.
332 size_t len = utf8_len < MAX_PATH ? utf8_len : MAX_PATH;
333 memcpy (&namedmutex_handle.sharedns.name [0], utf8_name, len);
334 namedmutex_handle.sharedns.name [len] = '\0';
336 handle = mutex_handle_create ((MonoW32HandleMutex*) &namedmutex_handle, MONO_W32TYPE_NAMEDMUTEX, owned);
339 mono_w32handle_namespace_unlock ();
341 return handle;
344 gpointer
345 ves_icall_System_Threading_Mutex_CreateMutex_icall (MonoBoolean owned, const gunichar2 *name,
346 gint32 name_length, MonoBoolean *created, MonoError *error)
348 gpointer mutex;
350 *created = TRUE;
352 /* Need to blow away any old errors here, because code tests
353 * for ERROR_ALREADY_EXISTS on success (!) to see if a mutex
354 * was freshly created */
355 mono_w32error_set_last (ERROR_SUCCESS);
357 if (!name) {
358 mutex = mutex_create (owned);
359 } else {
360 gsize utf8_name_length = 0;
361 char *utf8_name = mono_utf16_to_utf8len (name, name_length, &utf8_name_length, error);
362 return_val_if_nok (error, NULL);
364 mutex = namedmutex_create (owned, utf8_name, utf8_name_length);
366 if (mono_w32error_get_last () == ERROR_ALREADY_EXISTS)
367 *created = FALSE;
368 g_free (utf8_name);
371 return mutex;
374 MonoBoolean
375 ves_icall_System_Threading_Mutex_ReleaseMutex_internal (gpointer handle)
377 MonoW32Handle *handle_data;
378 MonoW32HandleMutex *mutex_handle;
379 pthread_t tid;
380 gboolean ret;
382 if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
383 g_warning ("%s: unkown handle %p", __func__, handle);
384 mono_w32error_set_last (ERROR_INVALID_HANDLE);
385 return FALSE;
388 if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX) {
389 g_warning ("%s: unknown mutex handle %p", __func__, handle);
390 mono_w32error_set_last (ERROR_INVALID_HANDLE);
391 mono_w32handle_unref (handle_data);
392 return FALSE;
395 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
397 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: releasing %s handle %p, tid: %p recursion: %d",
398 __func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
400 mono_w32handle_lock (handle_data);
402 tid = pthread_self ();
404 if (mutex_handle->abandoned) {
405 // The Win32 ReleaseMutex() function returns TRUE for abandoned mutexes
406 ret = TRUE;
407 } else if (!pthread_equal (mutex_handle->tid, tid)) {
408 ret = FALSE;
410 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: we don't own %s handle %p (owned by %ld, me %ld)",
411 __func__, mono_w32handle_get_typename (handle_data->type), handle, (long)mutex_handle->tid, (long)tid);
412 } else {
413 ret = TRUE;
415 /* OK, we own this mutex */
416 mutex_handle->recursion--;
418 if (mutex_handle->recursion == 0) {
419 thread_disown_mutex (mono_thread_internal_current (), handle);
421 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: unlocking %s handle %p, tid: %p recusion : %d",
422 __func__, mono_w32handle_get_typename (handle_data->type), handle, (gpointer) mutex_handle->tid, mutex_handle->recursion);
424 mutex_handle->tid = 0;
425 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
429 mono_w32handle_unlock (handle_data);
430 mono_w32handle_unref (handle_data);
432 return ret;
435 gpointer
436 ves_icall_System_Threading_Mutex_OpenMutex_icall (const gunichar2 *name, gint32 name_length, gint32 rights G_GNUC_UNUSED, gint32 *win32error, MonoError *error)
438 *win32error = ERROR_SUCCESS;
439 char *utf8_name = mono_utf16_to_utf8 (name, name_length, error);
440 return_val_if_nok (error, NULL);
441 gpointer handle = mono_w32mutex_open (utf8_name, rights, win32error);
442 g_free (utf8_name);
443 return handle;
446 gpointer
447 mono_w32mutex_open (const char* utf8_name, gint32 rights G_GNUC_UNUSED, gint32 *win32error)
449 *win32error = ERROR_SUCCESS;
451 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: Opening named mutex [%s]",
452 __func__, utf8_name);
454 // Opening named objects does not race.
455 mono_w32handle_namespace_lock ();
457 gpointer handle = mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDMUTEX, utf8_name);
459 mono_w32handle_namespace_unlock ();
461 if (handle == INVALID_HANDLE_VALUE) {
462 /* The name has already been used for a different object. */
463 *win32error = ERROR_INVALID_HANDLE;
464 return handle;
465 } else if (!handle) {
466 /* This name doesn't exist */
467 *win32error = ERROR_FILE_NOT_FOUND;
468 return handle;
471 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: returning named mutex handle %p",
472 __func__, handle);
474 return handle;
477 void
478 mono_w32mutex_abandon (MonoInternalThread *internal)
480 // Thread and InternalThread are pinned/mature.
481 // Take advantage of that and do not use handles here.
482 g_assert (mono_thread_internal_is_current (internal));
484 if (!internal->owned_mutexes)
485 return;
487 while (internal->owned_mutexes->len) {
488 MonoW32Handle *handle_data;
489 MonoW32HandleMutex *mutex_handle;
490 MonoNativeThreadId tid;
491 gpointer handle;
493 handle = g_ptr_array_index (internal->owned_mutexes, 0);
495 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
496 g_error ("%s: unkown handle %p", __func__, handle);
498 if (handle_data->type != MONO_W32TYPE_MUTEX && handle_data->type != MONO_W32TYPE_NAMEDMUTEX)
499 g_error ("%s: unkown mutex handle %p", __func__, handle);
501 mutex_handle = (MonoW32HandleMutex*) handle_data->specific;
503 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoning %s handle %p",
504 __func__, mono_w32handle_get_typename (handle_data->type), handle);
506 tid = MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid);
508 if (!pthread_equal (mutex_handle->tid, tid))
509 g_error ("%s: trying to release mutex %p acquired by thread %p from thread %p",
510 __func__, handle, (gpointer) mutex_handle->tid, (gpointer) tid);
512 mono_w32handle_lock (handle_data);
514 mutex_handle->recursion = 0;
515 mutex_handle->tid = 0;
516 mutex_handle->abandoned = TRUE;
518 mono_w32handle_set_signal_state (handle_data, TRUE, FALSE);
520 thread_disown_mutex (internal, handle);
522 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_MUTEX, "%s: abandoned %s handle %p",
523 __func__, mono_w32handle_get_typename (handle_data->type), handle);
525 mono_w32handle_unlock (handle_data);
526 mono_w32handle_unref (handle_data);
529 g_ptr_array_free (internal->owned_mutexes, TRUE);
530 internal->owned_mutexes = NULL;
533 MonoW32HandleNamespace*
534 mono_w32mutex_get_namespace (MonoW32HandleNamedMutex *mutex)
536 return &mutex->sharedns;