2 * handles.c: Generic and internal operations on handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2011 Novell, Inc.
8 * Copyright 2011 Xamarin Inc
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include <sys/types.h>
22 #ifdef HAVE_SYS_SOCKET_H
23 # include <sys/socket.h>
28 #ifdef HAVE_SYS_MMAN_H
29 # include <sys/mman.h>
35 #ifdef HAVE_SYS_RESOURCE_H
36 # include <sys/resource.h>
39 #include <mono/io-layer/wapi.h>
40 #include <mono/io-layer/wapi-private.h>
41 #include <mono/io-layer/handles-private.h>
42 #include <mono/io-layer/shared.h>
43 #include <mono/io-layer/process-private.h>
44 #include <mono/io-layer/io-trace.h>
46 #include <mono/utils/mono-os-mutex.h>
47 #include <mono/utils/mono-proclib.h>
48 #include <mono/utils/mono-threads.h>
49 #include <mono/utils/mono-once.h>
50 #include <mono/utils/mono-logger-internals.h>
53 #define _WAPI_PRIVATE_MAX_SLOTS (1024 * 16)
55 /* must be a power of 2 */
56 #define _WAPI_HANDLE_INITIAL_COUNT (256)
58 struct _WapiHandleUnshared
63 mono_mutex_t signal_mutex;
64 mono_cond_t signal_cond;
68 struct _WapiHandle_event event;
69 struct _WapiHandle_file file;
70 struct _WapiHandle_find find;
71 struct _WapiHandle_mutex mutex;
72 struct _WapiHandle_sem sem;
73 struct _WapiHandle_socket sock;
74 struct _WapiHandle_thread thread;
75 struct _WapiHandle_process process;
76 struct _WapiHandle_shared_ref shared;
77 struct _WapiHandle_namedmutex namedmutex;
78 struct _WapiHandle_namedsem namedsem;
79 struct _WapiHandle_namedevent namedevent;
83 typedef struct _WapiHandleUnshared _WapiHandleUnshared;
85 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
87 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT] = { (WapiHandleCapability)0 };
88 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
96 #ifndef DISABLE_SOCKETS
102 &_wapi_namedmutex_ops,
104 &_wapi_namedevent_ops,
107 static void _wapi_shared_details (gpointer handle_info);
109 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
112 _wapi_console_details,
113 _wapi_shared_details, /* thread */
117 NULL, /* Nothing useful to see in a socket handle */
118 NULL, /* Nothing useful to see in a find handle */
119 _wapi_shared_details, /* process */
121 _wapi_shared_details, /* namedmutex */
122 _wapi_shared_details, /* namedsem */
123 _wapi_shared_details, /* namedevent */
126 const char *_wapi_handle_typename[] = {
145 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
146 * If 4M handles are not enough... Oh, well... we will crash.
148 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
149 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
151 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
152 static guint32 _wapi_private_handle_count = 0;
153 static guint32 _wapi_private_handle_slot_count = 0;
156 * If SHM is disabled, this will point to a hash of _WapiFileShare structures, otherwise
157 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
160 static GHashTable *file_share_hash;
161 static mono_mutex_t file_share_hash_mutex;
163 #define file_share_hash_lock() mono_os_mutex_lock (&file_share_hash_mutex)
164 #define file_share_hash_unlock() mono_os_mutex_unlock (&file_share_hash_mutex)
166 guint32 _wapi_fd_reserve;
169 * This is an internal handle which is used for handling waiting for multiple handles.
170 * Threads which wait for multiple handles wait on this one handle, and when a handle
171 * is signalled, this handle is signalled too.
173 static gpointer _wapi_global_signal_handle;
175 /* Point to the mutex/cond inside _wapi_global_signal_handle */
176 static mono_mutex_t *_wapi_global_signal_mutex;
177 static mono_cond_t *_wapi_global_signal_cond;
180 gboolean _wapi_has_shut_down = FALSE;
182 /* Use this instead of getpid(), to cope with linuxthreads. It's a
183 * function rather than a variable lookup because we need to get at
184 * this before share_init() might have been called.
186 static pid_t _wapi_pid;
187 static mono_once_t pid_init_once = MONO_ONCE_INIT;
189 static void _wapi_handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles);
191 static void pid_init (void)
193 _wapi_pid = getpid ();
196 pid_t _wapi_getpid (void)
198 mono_once (&pid_init_once, pid_init);
204 static mono_mutex_t scan_mutex;
206 #define _WAPI_PRIVATE_HANDLES(x) (_wapi_private_handles [SLOT_INDEX ((guint32) x)][SLOT_OFFSET ((guint32) x)])
209 _WAPI_PRIVATE_HAVE_SLOT (guint32 x)
211 return (x / _WAPI_PRIVATE_MAX_SLOTS) < _WAPI_PRIVATE_MAX_SLOTS && _wapi_private_handles [SLOT_INDEX (x)];
215 _WAPI_PRIVATE_VALID_SLOT (guint32 x)
217 return SLOT_INDEX (x) < _WAPI_PRIVATE_MAX_SLOTS;
221 _wapi_handle_type (gpointer handle)
223 guint32 idx = GPOINTER_TO_UINT(handle);
225 if (!_WAPI_PRIVATE_VALID_SLOT (idx) || !_WAPI_PRIVATE_HAVE_SLOT (idx))
226 return WAPI_HANDLE_UNUSED; /* An impossible type */
228 return _WAPI_PRIVATE_HANDLES(idx).type;
232 _wapi_handle_set_signal_state (gpointer handle, gboolean state, gboolean broadcast)
234 guint32 idx = GPOINTER_TO_UINT(handle);
235 struct _WapiHandleUnshared *handle_data;
238 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
242 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
245 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
246 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
250 /* Tell everyone blocking on a single handle */
252 /* The condition the global signal cond is waiting on is the signalling of
253 * _any_ handle. So lock it before setting the signalled state.
255 thr_ret = mono_os_mutex_lock (_wapi_global_signal_mutex);
257 g_warning ("Bad call to mono_os_mutex_lock result %d for global signal mutex", thr_ret);
258 g_assert (thr_ret == 0);
260 /* This function _must_ be called with
261 * handle->signal_mutex locked
263 handle_data->signalled=state;
265 if (broadcast == TRUE) {
266 thr_ret = mono_os_cond_broadcast (&handle_data->signal_cond);
268 g_warning ("Bad call to mono_os_cond_broadcast result %d for handle %p", thr_ret, handle);
269 g_assert (thr_ret == 0);
271 thr_ret = mono_os_cond_signal (&handle_data->signal_cond);
273 g_warning ("Bad call to mono_os_cond_signal result %d for handle %p", thr_ret, handle);
274 g_assert (thr_ret == 0);
277 /* Tell everyone blocking on multiple handles that something
280 thr_ret = mono_os_cond_broadcast (_wapi_global_signal_cond);
282 g_warning ("Bad call to mono_os_cond_broadcast result %d for handle %p", thr_ret, handle);
283 g_assert (thr_ret == 0);
285 thr_ret = mono_os_mutex_unlock (_wapi_global_signal_mutex);
287 g_warning ("Bad call to mono_os_mutex_unlock result %d for global signal mutex", thr_ret);
288 g_assert (thr_ret == 0);
290 handle_data->signalled=state;
295 _wapi_handle_issignalled (gpointer handle)
297 guint32 idx = GPOINTER_TO_UINT(handle);
299 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
303 return _WAPI_PRIVATE_HANDLES (idx).signalled;
307 _wapi_handle_lock_signal_mutex (void)
310 g_message ("%s: lock global signal mutex", __func__);
313 return(mono_os_mutex_lock (_wapi_global_signal_mutex));
317 _wapi_handle_unlock_signal_mutex (void)
320 g_message ("%s: unlock global signal mutex", __func__);
323 return(mono_os_mutex_unlock (_wapi_global_signal_mutex));
327 _wapi_handle_lock_handle (gpointer handle)
329 guint32 idx = GPOINTER_TO_UINT(handle);
332 g_message ("%s: locking handle %p", __func__, handle);
335 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
339 _wapi_handle_ref (handle);
341 return(mono_os_mutex_lock (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex));
345 _wapi_handle_trylock_handle (gpointer handle)
347 guint32 idx = GPOINTER_TO_UINT(handle);
351 g_message ("%s: locking handle %p", __func__, handle);
354 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
358 _wapi_handle_ref (handle);
360 ret = mono_os_mutex_trylock (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
362 _wapi_handle_unref (handle);
369 _wapi_handle_unlock_handle (gpointer handle)
371 guint32 idx = GPOINTER_TO_UINT(handle);
375 g_message ("%s: unlocking handle %p", __func__, handle);
378 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
382 ret = mono_os_mutex_unlock (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
384 _wapi_handle_unref (handle);
389 static void handle_cleanup (void)
393 /* Every shared handle we were using ought really to be closed
394 * by now, but to make sure just blow them all away. The
395 * exiting finalizer thread in particular races us to the
396 * program exit and doesn't always win, so it can be left
397 * cluttering up the shared file. Anything else left over is
400 for(i = SLOT_INDEX (0); _wapi_private_handles[i] != NULL; i++) {
401 for(j = SLOT_OFFSET (0); j < _WAPI_HANDLE_INITIAL_COUNT; j++) {
402 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles[i][j];
403 gpointer handle = GINT_TO_POINTER (i*_WAPI_HANDLE_INITIAL_COUNT+j);
405 for(k = handle_data->ref; k > 0; k--) {
406 _wapi_handle_unref_full (handle, TRUE);
411 _wapi_shm_semaphores_remove ();
413 if (file_share_hash) {
414 g_hash_table_destroy (file_share_hash);
415 mono_os_mutex_destroy (&file_share_hash_mutex);
418 for (i = 0; i < _WAPI_PRIVATE_MAX_SLOTS; ++i)
419 g_free (_wapi_private_handles [i]);
423 wapi_getdtablesize (void)
425 return eg_getdtablesize ();
431 * Initialize the io-layer.
436 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
437 == WAPI_HANDLE_COUNT);
439 _wapi_fd_reserve = wapi_getdtablesize ();
441 /* This is needed by the code in _wapi_handle_new_internal */
442 _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
446 * The entries in _wapi_private_handles reserved for fds are allocated lazily to
450 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
451 _WAPI_HANDLE_INITIAL_COUNT);
454 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
455 _wapi_private_handle_slot_count ++;
456 } while(_wapi_fd_reserve > _wapi_private_handle_count);
458 _wapi_shm_semaphores_init ();
461 mono_os_mutex_init (&scan_mutex);
463 _wapi_global_signal_handle = _wapi_handle_new (WAPI_HANDLE_EVENT, NULL);
465 _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
466 _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
468 wapi_processes_init ();
474 g_assert (_wapi_has_shut_down == FALSE);
476 _wapi_has_shut_down = TRUE;
478 _wapi_error_cleanup ();
479 _wapi_thread_cleanup ();
480 wapi_processes_cleanup ();
484 static size_t _wapi_handle_struct_size (WapiHandleType type)
489 case WAPI_HANDLE_FILE: case WAPI_HANDLE_CONSOLE: case WAPI_HANDLE_PIPE:
490 type_size = sizeof (struct _WapiHandle_file);
492 case WAPI_HANDLE_THREAD:
493 type_size = sizeof (struct _WapiHandle_thread);
495 case WAPI_HANDLE_SEM:
496 type_size = sizeof (struct _WapiHandle_sem);
498 case WAPI_HANDLE_MUTEX:
499 type_size = sizeof (struct _WapiHandle_mutex);
501 case WAPI_HANDLE_EVENT:
502 type_size = sizeof (struct _WapiHandle_event);
504 case WAPI_HANDLE_SOCKET:
505 type_size = sizeof (struct _WapiHandle_socket);
507 case WAPI_HANDLE_FIND:
508 type_size = sizeof (struct _WapiHandle_find);
510 case WAPI_HANDLE_PROCESS:
511 type_size = sizeof (struct _WapiHandle_process);
513 case WAPI_HANDLE_NAMEDMUTEX:
514 type_size = sizeof (struct _WapiHandle_namedmutex);
516 case WAPI_HANDLE_NAMEDSEM:
517 type_size = sizeof (struct _WapiHandle_namedsem);
519 case WAPI_HANDLE_NAMEDEVENT:
520 type_size = sizeof (struct _WapiHandle_namedevent);
524 g_error ("Unknown WapiHandleType: %d\n", type);
530 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
531 WapiHandleType type, gpointer handle_specific)
536 g_assert (_wapi_has_shut_down == FALSE);
539 handle->signalled = FALSE;
542 thr_ret = mono_os_cond_init (&handle->signal_cond);
543 g_assert (thr_ret == 0);
545 thr_ret = mono_os_mutex_init (&handle->signal_mutex);
546 g_assert (thr_ret == 0);
548 if (handle_specific != NULL) {
549 type_size = _wapi_handle_struct_size (type);
550 memcpy (&handle->u, handle_specific,
556 * _wapi_handle_new_internal:
557 * @type: Init handle to this type
559 * Search for a free handle and initialize it. Return the handle on
560 * success and 0 on failure. This is only called from
561 * _wapi_handle_new, and scan_mutex must be held.
563 static guint32 _wapi_handle_new_internal (WapiHandleType type,
564 gpointer handle_specific)
567 static guint32 last = 0;
568 gboolean retry = FALSE;
570 g_assert (_wapi_has_shut_down == FALSE);
572 /* A linear scan should be fast enough. Start from the last
573 * allocation, assuming that handles are allocated more often
574 * than they're freed. Leave the space reserved for file
578 if (last < _wapi_fd_reserve) {
579 last = _wapi_fd_reserve;
586 for(i = SLOT_INDEX (count); i < _wapi_private_handle_slot_count; i++) {
587 if (_wapi_private_handles [i]) {
588 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
589 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
591 if(handle->type == WAPI_HANDLE_UNUSED) {
594 _wapi_handle_init (handle, type, handle_specific);
602 if(retry && last > _wapi_fd_reserve) {
603 /* Try again from the beginning */
604 last = _wapi_fd_reserve;
608 /* Will need to expand the array. The caller will sort it out */
614 _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
616 guint32 handle_idx = 0;
620 g_assert (_wapi_has_shut_down == FALSE);
622 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating new handle of type %s", __func__,
623 _wapi_handle_typename[type]);
625 g_assert(!_WAPI_FD_HANDLE(type));
627 thr_ret = mono_os_mutex_lock (&scan_mutex);
628 g_assert (thr_ret == 0);
630 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
631 /* Try and expand the array, and have another go */
632 int idx = SLOT_INDEX (_wapi_private_handle_count);
633 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
637 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
638 _WAPI_HANDLE_INITIAL_COUNT);
640 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
641 _wapi_private_handle_slot_count ++;
644 thr_ret = mono_os_mutex_unlock (&scan_mutex);
645 g_assert (thr_ret == 0);
647 if (handle_idx == 0) {
648 /* We ran out of slots */
649 handle = _WAPI_HANDLE_INVALID;
653 /* Make sure we left the space for fd mappings */
654 g_assert (handle_idx >= _wapi_fd_reserve);
656 handle = GUINT_TO_POINTER (handle_idx);
658 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Allocated new handle %p", __func__, handle);
665 init_handles_slot (int idx)
669 thr_ret = mono_os_mutex_lock (&scan_mutex);
670 g_assert (thr_ret == 0);
672 if (_wapi_private_handles [idx] == NULL) {
673 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
674 _WAPI_HANDLE_INITIAL_COUNT);
675 g_assert (_wapi_private_handles [idx]);
678 thr_ret = mono_os_mutex_unlock (&scan_mutex);
679 g_assert (thr_ret == 0);
682 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
683 gpointer handle_specific)
685 struct _WapiHandleUnshared *handle;
688 g_assert (_wapi_has_shut_down == FALSE);
690 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating new handle of type %s", __func__,
691 _wapi_handle_typename[type]);
693 g_assert(_WAPI_FD_HANDLE(type));
695 if (fd >= _wapi_fd_reserve) {
696 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fd %d is too big", __func__, fd);
698 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
701 /* Initialize the array entries on demand */
702 if (_wapi_private_handles [SLOT_INDEX (fd)] == NULL)
703 init_handles_slot (SLOT_INDEX (fd));
705 handle = &_WAPI_PRIVATE_HANDLES(fd);
707 if (handle->type != WAPI_HANDLE_UNUSED) {
708 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fd %d is already in use!", __func__, fd);
709 /* FIXME: clean up this handle? We can't do anything
710 * with the fd, cos thats the new one
714 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Assigning new fd handle %d", __func__, fd);
716 /* Prevent file share entries racing with us, when the file
717 * handle is only half initialised
719 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
720 g_assert(thr_ret == 0);
722 _wapi_handle_init (handle, type, handle_specific);
724 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
726 return(GUINT_TO_POINTER(fd));
730 _wapi_lookup_handle (gpointer handle, WapiHandleType type,
731 gpointer *handle_specific)
733 struct _WapiHandleUnshared *handle_data;
734 guint32 handle_idx = GPOINTER_TO_UINT(handle);
736 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
740 /* Initialize the array entries on demand */
741 if (_wapi_private_handles [SLOT_INDEX (handle_idx)] == NULL)
742 init_handles_slot (SLOT_INDEX (handle_idx));
744 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
746 if (handle_data->type != type) {
750 if (handle_specific == NULL) {
754 *handle_specific = &handle_data->u;
760 _wapi_handle_foreach (WapiHandleType type,
761 gboolean (*on_each)(gpointer test, gpointer user),
764 struct _WapiHandleUnshared *handle_data = NULL;
769 thr_ret = mono_os_mutex_lock (&scan_mutex);
770 g_assert (thr_ret == 0);
772 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
773 if (_wapi_private_handles [i]) {
774 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
775 handle_data = &_wapi_private_handles [i][k];
777 if (handle_data->type == type) {
778 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
779 if (on_each (ret, user_data) == TRUE)
786 thr_ret = mono_os_mutex_unlock (&scan_mutex);
787 g_assert (thr_ret == 0);
790 /* This might list some shared handles twice if they are already
791 * opened by this process, and the check function returns FALSE the
792 * first time. Shared handles that are created during the search are
793 * unreffed if the check function returns FALSE, so callers must not
794 * rely on the handle persisting (unless the check function returns
796 * The caller owns the returned handle.
798 gpointer _wapi_search_handle (WapiHandleType type,
799 gboolean (*check)(gpointer test, gpointer user),
801 gpointer *handle_specific,
802 gboolean search_shared)
804 struct _WapiHandleUnshared *handle_data = NULL;
807 gboolean found = FALSE;
810 thr_ret = mono_os_mutex_lock (&scan_mutex);
811 g_assert (thr_ret == 0);
813 for (i = SLOT_INDEX (0); !found && i < _wapi_private_handle_slot_count; i++) {
814 if (_wapi_private_handles [i]) {
815 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
816 handle_data = &_wapi_private_handles [i][k];
818 if (handle_data->type == type) {
819 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
820 if (check (ret, user_data) == TRUE) {
821 _wapi_handle_ref (ret);
830 thr_ret = mono_os_mutex_unlock (&scan_mutex);
831 g_assert (thr_ret == 0);
838 if(handle_specific != NULL) {
839 *handle_specific = &handle_data->u;
846 /* Returns the offset of the metadata array, or _WAPI_HANDLE_INVALID on error, or NULL for
849 gpointer _wapi_search_handle_namespace (WapiHandleType type,
852 struct _WapiHandleUnshared *handle_data;
857 g_assert(_WAPI_SHARED_NAMESPACE(type));
859 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Lookup for handle named [%s] type %s", __func__,
860 utf8_name, _wapi_handle_typename[type]);
862 thr_ret = mono_os_mutex_lock (&scan_mutex);
863 g_assert (thr_ret == 0);
865 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
866 if (!_wapi_private_handles [i])
868 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
869 WapiSharedNamespace *sharedns;
871 handle_data = &_wapi_private_handles [i][k];
873 /* Check mutex, event, semaphore, timer, job and
874 * file-mapping object names. So far only mutex,
875 * semaphore and event are implemented.
877 if (!_WAPI_SHARED_NAMESPACE (handle_data->type)) {
881 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[handle_data->type]);
883 switch (handle_data->type) {
884 case WAPI_HANDLE_NAMEDMUTEX: sharedns = &handle_data->u.namedmutex.sharedns; break;
885 case WAPI_HANDLE_NAMEDSEM: sharedns = &handle_data->u.namedsem.sharedns; break;
886 case WAPI_HANDLE_NAMEDEVENT: sharedns = &handle_data->u.namedevent.sharedns; break;
888 g_assert_not_reached ();
891 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is [%s]", __func__, sharedns->name);
893 if (strcmp (sharedns->name, utf8_name) == 0) {
894 if (handle_data->type != type) {
895 /* Its the wrong type, so fail now */
896 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[handle_data->type]);
897 ret = _WAPI_HANDLE_INVALID;
900 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle 0x%x matches name and type", __func__, i);
909 thr_ret = mono_os_mutex_unlock (&scan_mutex);
910 g_assert (thr_ret == 0);
915 void _wapi_handle_ref (gpointer handle)
917 guint32 idx = GPOINTER_TO_UINT(handle);
918 struct _WapiHandleUnshared *handle_data;
920 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
921 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Attempting to ref invalid private handle %p", __func__, handle);
925 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
926 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Attempting to ref unused handle %p", __func__, handle);
930 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
932 InterlockedIncrement ((gint32 *)&handle_data->ref);
935 g_message ("%s: %s handle %p ref now %d", __func__,
936 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
938 _WAPI_PRIVATE_HANDLES(idx).ref);
942 /* The handle must not be locked on entry to this function */
943 static void _wapi_handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles)
945 guint32 idx = GPOINTER_TO_UINT(handle);
946 gboolean destroy = FALSE, early_exit = FALSE;
949 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
953 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
954 g_warning ("%s: Attempting to unref unused handle %p",
959 /* Possible race condition here if another thread refs the
960 * handle between here and setting the type to UNUSED. I
961 * could lock a mutex, but I'm not sure that allowing a handle
962 * reference to reach 0 isn't an application bug anyway.
964 destroy = (InterlockedDecrement ((gint32 *)&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
967 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
968 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
970 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
974 /* Need to copy the handle info, reset the slot in the
975 * array, and _only then_ call the close function to
976 * avoid race conditions (eg file descriptors being
977 * closed, and another file being opened getting the
978 * same fd racing the memset())
980 struct _WapiHandleUnshared handle_data;
981 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
982 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
984 thr_ret = mono_os_mutex_lock (&scan_mutex);
986 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Destroying handle %p", __func__, handle);
988 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
989 sizeof (struct _WapiHandleUnshared));
991 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
992 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
994 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
996 /* Destroy the mutex and cond var. We hope nobody
997 * tried to grab them between the handle unlock and
998 * now, but pthreads doesn't have a
999 * "unlock_and_destroy" atomic function.
1001 thr_ret = mono_os_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
1002 /*WARNING gross hack to make cleanup not crash when exiting without the whole runtime teardown.*/
1003 if (thr_ret == EBUSY && ignore_private_busy_handles) {
1007 g_error ("Error destroying handle %p mutex due to %d\n", handle, thr_ret);
1009 thr_ret = mono_os_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
1010 if (thr_ret == EBUSY && ignore_private_busy_handles)
1012 else if (thr_ret != 0)
1013 g_error ("Error destroying handle %p cond var due to %d\n", handle, thr_ret);
1016 thr_ret = mono_os_mutex_unlock (&scan_mutex);
1017 g_assert (thr_ret == 0);
1022 if (close_func != NULL) {
1023 close_func (handle, &handle_data.u);
1028 void _wapi_handle_unref (gpointer handle)
1030 _wapi_handle_unref_full (handle, FALSE);
1033 void _wapi_handle_register_capabilities (WapiHandleType type,
1034 WapiHandleCapability caps)
1036 handle_caps[type] = caps;
1039 gboolean _wapi_handle_test_capabilities (gpointer handle,
1040 WapiHandleCapability caps)
1042 guint32 idx = GPOINTER_TO_UINT(handle);
1043 WapiHandleType type;
1045 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1049 type = _WAPI_PRIVATE_HANDLES(idx).type;
1051 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing 0x%x against 0x%x (%d)", __func__,
1052 handle_caps[type], caps, handle_caps[type] & caps);
1054 return((handle_caps[type] & caps) != 0);
1057 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
1059 if (handle_ops[type] != NULL &&
1060 handle_ops[type]->close != NULL) {
1061 return (handle_ops[type]->close);
1067 void _wapi_handle_ops_close (gpointer handle, gpointer data)
1069 guint32 idx = GPOINTER_TO_UINT(handle);
1070 WapiHandleType type;
1072 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1076 type = _WAPI_PRIVATE_HANDLES(idx).type;
1078 if (handle_ops[type] != NULL &&
1079 handle_ops[type]->close != NULL) {
1080 handle_ops[type]->close (handle, data);
1084 void _wapi_handle_ops_signal (gpointer handle)
1086 guint32 idx = GPOINTER_TO_UINT(handle);
1087 WapiHandleType type;
1089 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1093 type = _WAPI_PRIVATE_HANDLES(idx).type;
1095 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1096 handle_ops[type]->signal (handle);
1100 gboolean _wapi_handle_ops_own (gpointer handle)
1102 guint32 idx = GPOINTER_TO_UINT(handle);
1103 WapiHandleType type;
1105 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1109 type = _WAPI_PRIVATE_HANDLES(idx).type;
1111 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1112 return(handle_ops[type]->own_handle (handle));
1118 gboolean _wapi_handle_ops_isowned (gpointer handle)
1120 guint32 idx = GPOINTER_TO_UINT(handle);
1121 WapiHandleType type;
1123 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1127 type = _WAPI_PRIVATE_HANDLES(idx).type;
1129 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1130 return(handle_ops[type]->is_owned (handle));
1136 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout, gboolean alertable)
1138 guint32 idx = GPOINTER_TO_UINT(handle);
1139 WapiHandleType type;
1141 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1142 return(WAIT_FAILED);
1145 type = _WAPI_PRIVATE_HANDLES(idx).type;
1147 if (handle_ops[type] != NULL &&
1148 handle_ops[type]->special_wait != NULL) {
1149 return(handle_ops[type]->special_wait (handle, timeout, alertable));
1151 return(WAIT_FAILED);
1155 void _wapi_handle_ops_prewait (gpointer handle)
1157 guint32 idx = GPOINTER_TO_UINT (handle);
1158 WapiHandleType type;
1160 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1164 type = _WAPI_PRIVATE_HANDLES (idx).type;
1166 if (handle_ops[type] != NULL &&
1167 handle_ops[type]->prewait != NULL) {
1168 handle_ops[type]->prewait (handle);
1175 * @handle: The handle to release
1177 * Closes and invalidates @handle, releasing any resources it
1178 * consumes. When the last handle to a temporary or non-persistent
1179 * object is closed, that object can be deleted. Closing the same
1180 * handle twice is an error.
1182 * Return value: %TRUE on success, %FALSE otherwise.
1184 gboolean CloseHandle(gpointer handle)
1186 if (handle == NULL) {
1187 /* Problem: because we map file descriptors to the
1188 * same-numbered handle we can't tell the difference
1189 * between a bogus handle and the handle to stdin.
1190 * Assume that it's the console handle if that handle
1193 if (_WAPI_PRIVATE_HANDLES (0).type != WAPI_HANDLE_CONSOLE) {
1194 SetLastError (ERROR_INVALID_PARAMETER);
1198 if (handle == _WAPI_HANDLE_INVALID){
1199 SetLastError (ERROR_INVALID_PARAMETER);
1203 _wapi_handle_unref (handle);
1208 /* Lots more to implement here, but this is all we need at the moment */
1209 gboolean DuplicateHandle (gpointer srcprocess, gpointer src,
1210 gpointer targetprocess, gpointer *target,
1211 guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 options G_GNUC_UNUSED)
1213 if (srcprocess != _WAPI_PROCESS_CURRENT ||
1214 targetprocess != _WAPI_PROCESS_CURRENT) {
1215 /* Duplicating other process's handles is not supported */
1216 SetLastError (ERROR_INVALID_HANDLE);
1220 if (src == _WAPI_PROCESS_CURRENT) {
1221 *target = _wapi_process_duplicate ();
1222 } else if (src == _WAPI_THREAD_CURRENT) {
1223 g_assert_not_reached ();
1225 _wapi_handle_ref (src);
1232 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1238 guint32 count, i, iter=0;
1241 WapiHandleType type;
1243 /* Lock all the handles, with backoff */
1245 for(i=0; i<numhandles; i++) {
1246 gpointer handle = handles[i];
1247 guint32 idx = GPOINTER_TO_UINT(handle);
1249 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempting to lock %p", __func__, handle);
1251 type = _WAPI_PRIVATE_HANDLES(idx).type;
1253 thr_ret = _wapi_handle_trylock_handle (handle);
1258 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt failed for %p: %s", __func__,
1259 handle, strerror (thr_ret));
1262 handle = handles[i];
1263 idx = GPOINTER_TO_UINT(handle);
1265 thr_ret = _wapi_handle_unlock_handle (handle);
1266 g_assert (thr_ret == 0);
1269 /* If iter ever reaches 100 the nanosleep will
1270 * return EINVAL immediately, but we have a
1271 * design flaw if that happens.
1275 g_warning ("%s: iteration overflow!",
1280 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Backing off for %d ms", __func__,
1282 _wapi_handle_spin (10 * iter);
1288 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locked all handles", __func__);
1293 for(i=0; i<numhandles; i++) {
1294 gpointer handle = handles[i];
1295 guint32 idx = GPOINTER_TO_UINT(handle);
1297 type = _WAPI_PRIVATE_HANDLES(idx).type;
1299 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Checking handle %p", __func__, handle);
1301 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1302 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1303 (_wapi_handle_issignalled (handle))) {
1306 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %p signalled", __func__,
1314 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %d event handles signalled", __func__, count);
1316 if ((waitall == TRUE && count == numhandles) ||
1317 (waitall == FALSE && count > 0)) {
1323 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning %d", __func__, ret);
1330 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1335 for(i=0; i<numhandles; i++) {
1336 gpointer handle = handles[i];
1338 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
1340 thr_ret = _wapi_handle_unlock_handle (handle);
1341 g_assert (thr_ret == 0);
1346 signal_handle_and_unref (gpointer handle)
1349 mono_mutex_t *mutex;
1354 /* If we reach here, then interrupt token is set to the flag value, which
1355 * means that the target thread is either
1356 * - before the first CAS in timedwait, which means it won't enter the wait.
1357 * - it is after the first CAS, so it is already waiting, or it will enter
1358 * the wait, and it will be interrupted by the broadcast. */
1359 idx = GPOINTER_TO_UINT (handle);
1360 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1361 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1363 mono_os_mutex_lock (mutex);
1364 mono_os_cond_broadcast (cond);
1365 mono_os_mutex_unlock (mutex);
1367 _wapi_handle_unref (handle);
1371 _wapi_handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
1373 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, poll, alerted);
1377 _wapi_handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1382 mono_mutex_t *mutex;
1384 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: waiting for %p (type %s)", __func__, handle,
1385 _wapi_handle_typename[_wapi_handle_type (handle)]);
1390 idx = GPOINTER_TO_UINT(handle);
1393 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1396 _wapi_handle_ref (handle);
1399 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1400 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1403 res = mono_os_cond_timedwait (cond, mutex, timeout);
1405 /* This is needed when waiting for process handles */
1408 * pthread_cond_(timed)wait() can return 0 even if the condition was not
1409 * signalled. This happens at least on Darwin. We surface this, i.e., we
1410 * get spurious wake-ups.
1412 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
1414 res = mono_os_cond_timedwait (cond, mutex, timeout);
1416 if (timeout < 100) {
1417 /* Real timeout is less than 100ms time */
1418 res = mono_os_cond_timedwait (cond, mutex, timeout);
1420 res = mono_os_cond_timedwait (cond, mutex, 100);
1422 /* Mask the fake timeout, this will cause
1423 * another poll if the cond was not really signaled
1425 if (res == ETIMEDOUT)
1432 mono_thread_info_uninstall_interrupt (alerted);
1434 /* if it is alerted, then the handle is unref in the interrupt callback */
1435 _wapi_handle_unref (handle);
1443 _wapi_free_share_info (_WapiFileShare *share_info)
1445 file_share_hash_lock ();
1446 g_hash_table_remove (file_share_hash, share_info);
1447 file_share_hash_unlock ();
1448 /* The hashtable dtor frees share_info */
1452 wapi_share_info_equal (gconstpointer ka, gconstpointer kb)
1454 const _WapiFileShare *s1 = (const _WapiFileShare *)ka;
1455 const _WapiFileShare *s2 = (const _WapiFileShare *)kb;
1457 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
1461 wapi_share_info_hash (gconstpointer data)
1463 const _WapiFileShare *s = (const _WapiFileShare *)data;
1468 gboolean _wapi_handle_get_or_set_share (guint64 device, guint64 inode,
1469 guint32 new_sharemode,
1471 guint32 *old_sharemode,
1472 guint32 *old_access,
1473 struct _WapiFileShare **share_info)
1475 struct _WapiFileShare *file_share;
1477 gboolean exists = FALSE;
1479 /* Prevent new entries racing with us */
1480 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1481 g_assert (thr_ret == 0);
1486 * Instead of allocating a 4MB array, we use a hash table to keep track of this
1487 * info. This is needed even if SHM is disabled, to track sharing inside
1488 * the current process.
1490 if (!file_share_hash) {
1491 file_share_hash = g_hash_table_new_full (wapi_share_info_hash, wapi_share_info_equal, NULL, g_free);
1492 mono_os_mutex_init_recursive (&file_share_hash_mutex);
1495 tmp.device = device;
1498 file_share_hash_lock ();
1500 file_share = (_WapiFileShare *)g_hash_table_lookup (file_share_hash, &tmp);
1502 *old_sharemode = file_share->sharemode;
1503 *old_access = file_share->access;
1504 *share_info = file_share;
1506 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1509 file_share = g_new0 (_WapiFileShare, 1);
1511 file_share->device = device;
1512 file_share->inode = inode;
1513 file_share->opened_by_pid = _wapi_getpid ();
1514 file_share->sharemode = new_sharemode;
1515 file_share->access = new_access;
1516 file_share->handle_refs = 1;
1517 *share_info = file_share;
1519 g_hash_table_insert (file_share_hash, file_share, file_share);
1522 file_share_hash_unlock ();
1524 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1529 void _wapi_handle_dump (void)
1531 struct _WapiHandleUnshared *handle_data;
1535 thr_ret = mono_os_mutex_lock (&scan_mutex);
1536 g_assert (thr_ret == 0);
1538 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1539 if (_wapi_private_handles [i]) {
1540 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1541 handle_data = &_wapi_private_handles [i][k];
1543 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1547 g_print ("%3x [%7s] %s %d ",
1548 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1549 _wapi_handle_typename[handle_data->type],
1550 handle_data->signalled?"Sg":"Un",
1552 if (handle_details[handle_data->type])
1553 handle_details[handle_data->type](&handle_data->u);
1559 thr_ret = mono_os_mutex_unlock (&scan_mutex);
1560 g_assert (thr_ret == 0);
1563 static void _wapi_shared_details (gpointer handle_info)
1565 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1567 g_print ("offset: 0x%x", shared->offset);