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 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
55 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT] = { (WapiHandleCapability)0 };
56 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
64 #ifndef DISABLE_SOCKETS
70 &_wapi_namedmutex_ops,
72 &_wapi_namedevent_ops,
75 static void _wapi_shared_details (gpointer handle_info);
77 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
80 _wapi_console_details,
81 _wapi_shared_details, /* thread */
85 NULL, /* Nothing useful to see in a socket handle */
86 NULL, /* Nothing useful to see in a find handle */
87 _wapi_shared_details, /* process */
89 _wapi_shared_details, /* namedmutex */
90 _wapi_shared_details, /* namedsem */
91 _wapi_shared_details, /* namedevent */
94 const char *_wapi_handle_typename[] = {
113 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
114 * If 4M handles are not enough... Oh, well... we will crash.
116 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
117 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
119 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
120 static guint32 _wapi_private_handle_count = 0;
121 static guint32 _wapi_private_handle_slot_count = 0;
124 * If SHM is disabled, this will point to a hash of _WapiFileShare structures, otherwise
125 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
128 static GHashTable *file_share_hash;
129 static mono_mutex_t file_share_hash_mutex;
131 #define file_share_hash_lock() mono_os_mutex_lock (&file_share_hash_mutex)
132 #define file_share_hash_unlock() mono_os_mutex_unlock (&file_share_hash_mutex)
134 guint32 _wapi_fd_reserve;
137 * This is an internal handle which is used for handling waiting for multiple handles.
138 * Threads which wait for multiple handles wait on this one handle, and when a handle
139 * is signalled, this handle is signalled too.
141 gpointer _wapi_global_signal_handle;
143 /* Point to the mutex/cond inside _wapi_global_signal_handle */
144 mono_mutex_t *_wapi_global_signal_mutex;
145 mono_cond_t *_wapi_global_signal_cond;
148 gboolean _wapi_has_shut_down = FALSE;
150 /* Use this instead of getpid(), to cope with linuxthreads. It's a
151 * function rather than a variable lookup because we need to get at
152 * this before share_init() might have been called.
154 static pid_t _wapi_pid;
155 static mono_once_t pid_init_once = MONO_ONCE_INIT;
157 static void _wapi_handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles);
159 static void pid_init (void)
161 _wapi_pid = getpid ();
164 pid_t _wapi_getpid (void)
166 mono_once (&pid_init_once, pid_init);
172 static mono_mutex_t scan_mutex;
174 static void handle_cleanup (void)
178 /* Every shared handle we were using ought really to be closed
179 * by now, but to make sure just blow them all away. The
180 * exiting finalizer thread in particular races us to the
181 * program exit and doesn't always win, so it can be left
182 * cluttering up the shared file. Anything else left over is
185 for(i = SLOT_INDEX (0); _wapi_private_handles[i] != NULL; i++) {
186 for(j = SLOT_OFFSET (0); j < _WAPI_HANDLE_INITIAL_COUNT; j++) {
187 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles[i][j];
188 gpointer handle = GINT_TO_POINTER (i*_WAPI_HANDLE_INITIAL_COUNT+j);
190 for(k = handle_data->ref; k > 0; k--) {
191 _wapi_handle_unref_full (handle, TRUE);
196 _wapi_shm_semaphores_remove ();
198 if (file_share_hash) {
199 g_hash_table_destroy (file_share_hash);
200 mono_os_mutex_destroy (&file_share_hash_mutex);
203 for (i = 0; i < _WAPI_PRIVATE_MAX_SLOTS; ++i)
204 g_free (_wapi_private_handles [i]);
208 wapi_getdtablesize (void)
210 return eg_getdtablesize ();
216 * Initialize the io-layer.
221 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
222 == WAPI_HANDLE_COUNT);
224 _wapi_fd_reserve = wapi_getdtablesize ();
226 /* This is needed by the code in _wapi_handle_new_internal */
227 _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
231 * The entries in _wapi_private_handles reserved for fds are allocated lazily to
235 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
236 _WAPI_HANDLE_INITIAL_COUNT);
239 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
240 _wapi_private_handle_slot_count ++;
241 } while(_wapi_fd_reserve > _wapi_private_handle_count);
243 _wapi_shm_semaphores_init ();
246 mono_os_mutex_init (&scan_mutex);
248 _wapi_global_signal_handle = _wapi_handle_new (WAPI_HANDLE_EVENT, NULL);
250 _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
251 _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
253 wapi_processes_init ();
259 g_assert (_wapi_has_shut_down == FALSE);
261 _wapi_has_shut_down = TRUE;
263 _wapi_error_cleanup ();
264 _wapi_thread_cleanup ();
265 wapi_processes_cleanup ();
269 static size_t _wapi_handle_struct_size (WapiHandleType type)
274 case WAPI_HANDLE_FILE: case WAPI_HANDLE_CONSOLE: case WAPI_HANDLE_PIPE:
275 type_size = sizeof (struct _WapiHandle_file);
277 case WAPI_HANDLE_THREAD:
278 type_size = sizeof (struct _WapiHandle_thread);
280 case WAPI_HANDLE_SEM:
281 type_size = sizeof (struct _WapiHandle_sem);
283 case WAPI_HANDLE_MUTEX:
284 type_size = sizeof (struct _WapiHandle_mutex);
286 case WAPI_HANDLE_EVENT:
287 type_size = sizeof (struct _WapiHandle_event);
289 case WAPI_HANDLE_SOCKET:
290 type_size = sizeof (struct _WapiHandle_socket);
292 case WAPI_HANDLE_FIND:
293 type_size = sizeof (struct _WapiHandle_find);
295 case WAPI_HANDLE_PROCESS:
296 type_size = sizeof (struct _WapiHandle_process);
298 case WAPI_HANDLE_NAMEDMUTEX:
299 type_size = sizeof (struct _WapiHandle_namedmutex);
301 case WAPI_HANDLE_NAMEDSEM:
302 type_size = sizeof (struct _WapiHandle_namedsem);
304 case WAPI_HANDLE_NAMEDEVENT:
305 type_size = sizeof (struct _WapiHandle_namedevent);
309 g_error ("Unknown WapiHandleType: %d\n", type);
315 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
316 WapiHandleType type, gpointer handle_specific)
321 g_assert (_wapi_has_shut_down == FALSE);
324 handle->signalled = FALSE;
327 thr_ret = mono_os_cond_init (&handle->signal_cond);
328 g_assert (thr_ret == 0);
330 thr_ret = mono_os_mutex_init (&handle->signal_mutex);
331 g_assert (thr_ret == 0);
333 if (handle_specific != NULL) {
334 type_size = _wapi_handle_struct_size (type);
335 memcpy (&handle->u, handle_specific,
341 * _wapi_handle_new_internal:
342 * @type: Init handle to this type
344 * Search for a free handle and initialize it. Return the handle on
345 * success and 0 on failure. This is only called from
346 * _wapi_handle_new, and scan_mutex must be held.
348 static guint32 _wapi_handle_new_internal (WapiHandleType type,
349 gpointer handle_specific)
352 static guint32 last = 0;
353 gboolean retry = FALSE;
355 g_assert (_wapi_has_shut_down == FALSE);
357 /* A linear scan should be fast enough. Start from the last
358 * allocation, assuming that handles are allocated more often
359 * than they're freed. Leave the space reserved for file
363 if (last < _wapi_fd_reserve) {
364 last = _wapi_fd_reserve;
371 for(i = SLOT_INDEX (count); i < _wapi_private_handle_slot_count; i++) {
372 if (_wapi_private_handles [i]) {
373 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
374 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
376 if(handle->type == WAPI_HANDLE_UNUSED) {
379 _wapi_handle_init (handle, type, handle_specific);
387 if(retry && last > _wapi_fd_reserve) {
388 /* Try again from the beginning */
389 last = _wapi_fd_reserve;
393 /* Will need to expand the array. The caller will sort it out */
399 _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
401 guint32 handle_idx = 0;
405 g_assert (_wapi_has_shut_down == FALSE);
407 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating new handle of type %s", __func__,
408 _wapi_handle_typename[type]);
410 g_assert(!_WAPI_FD_HANDLE(type));
412 thr_ret = mono_os_mutex_lock (&scan_mutex);
413 g_assert (thr_ret == 0);
415 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
416 /* Try and expand the array, and have another go */
417 int idx = SLOT_INDEX (_wapi_private_handle_count);
418 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
422 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
423 _WAPI_HANDLE_INITIAL_COUNT);
425 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
426 _wapi_private_handle_slot_count ++;
429 thr_ret = mono_os_mutex_unlock (&scan_mutex);
430 g_assert (thr_ret == 0);
432 if (handle_idx == 0) {
433 /* We ran out of slots */
434 handle = _WAPI_HANDLE_INVALID;
438 /* Make sure we left the space for fd mappings */
439 g_assert (handle_idx >= _wapi_fd_reserve);
441 handle = GUINT_TO_POINTER (handle_idx);
443 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Allocated new handle %p", __func__, handle);
450 init_handles_slot (int idx)
454 thr_ret = mono_os_mutex_lock (&scan_mutex);
455 g_assert (thr_ret == 0);
457 if (_wapi_private_handles [idx] == NULL) {
458 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
459 _WAPI_HANDLE_INITIAL_COUNT);
460 g_assert (_wapi_private_handles [idx]);
463 thr_ret = mono_os_mutex_unlock (&scan_mutex);
464 g_assert (thr_ret == 0);
467 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
468 gpointer handle_specific)
470 struct _WapiHandleUnshared *handle;
473 g_assert (_wapi_has_shut_down == FALSE);
475 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating new handle of type %s", __func__,
476 _wapi_handle_typename[type]);
478 g_assert(_WAPI_FD_HANDLE(type));
480 if (fd >= _wapi_fd_reserve) {
481 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fd %d is too big", __func__, fd);
483 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
486 /* Initialize the array entries on demand */
487 if (_wapi_private_handles [SLOT_INDEX (fd)] == NULL)
488 init_handles_slot (SLOT_INDEX (fd));
490 handle = &_WAPI_PRIVATE_HANDLES(fd);
492 if (handle->type != WAPI_HANDLE_UNUSED) {
493 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: fd %d is already in use!", __func__, fd);
494 /* FIXME: clean up this handle? We can't do anything
495 * with the fd, cos thats the new one
499 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Assigning new fd handle %d", __func__, fd);
501 /* Prevent file share entries racing with us, when the file
502 * handle is only half initialised
504 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
505 g_assert(thr_ret == 0);
507 _wapi_handle_init (handle, type, handle_specific);
509 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
511 return(GUINT_TO_POINTER(fd));
515 _wapi_lookup_handle (gpointer handle, WapiHandleType type,
516 gpointer *handle_specific)
518 struct _WapiHandleUnshared *handle_data;
519 guint32 handle_idx = GPOINTER_TO_UINT(handle);
521 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
525 /* Initialize the array entries on demand */
526 if (_wapi_private_handles [SLOT_INDEX (handle_idx)] == NULL)
527 init_handles_slot (SLOT_INDEX (handle_idx));
529 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
531 if (handle_data->type != type) {
535 if (handle_specific == NULL) {
539 *handle_specific = &handle_data->u;
545 _wapi_handle_foreach (WapiHandleType type,
546 gboolean (*on_each)(gpointer test, gpointer user),
549 struct _WapiHandleUnshared *handle_data = NULL;
554 thr_ret = mono_os_mutex_lock (&scan_mutex);
555 g_assert (thr_ret == 0);
557 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
558 if (_wapi_private_handles [i]) {
559 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
560 handle_data = &_wapi_private_handles [i][k];
562 if (handle_data->type == type) {
563 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
564 if (on_each (ret, user_data) == TRUE)
571 thr_ret = mono_os_mutex_unlock (&scan_mutex);
572 g_assert (thr_ret == 0);
575 /* This might list some shared handles twice if they are already
576 * opened by this process, and the check function returns FALSE the
577 * first time. Shared handles that are created during the search are
578 * unreffed if the check function returns FALSE, so callers must not
579 * rely on the handle persisting (unless the check function returns
581 * The caller owns the returned handle.
583 gpointer _wapi_search_handle (WapiHandleType type,
584 gboolean (*check)(gpointer test, gpointer user),
586 gpointer *handle_specific,
587 gboolean search_shared)
589 struct _WapiHandleUnshared *handle_data = NULL;
592 gboolean found = FALSE;
595 thr_ret = mono_os_mutex_lock (&scan_mutex);
596 g_assert (thr_ret == 0);
598 for (i = SLOT_INDEX (0); !found && i < _wapi_private_handle_slot_count; i++) {
599 if (_wapi_private_handles [i]) {
600 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
601 handle_data = &_wapi_private_handles [i][k];
603 if (handle_data->type == type) {
604 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
605 if (check (ret, user_data) == TRUE) {
606 _wapi_handle_ref (ret);
615 thr_ret = mono_os_mutex_unlock (&scan_mutex);
616 g_assert (thr_ret == 0);
623 if(handle_specific != NULL) {
624 *handle_specific = &handle_data->u;
631 /* Returns the offset of the metadata array, or _WAPI_HANDLE_INVALID on error, or NULL for
634 gpointer _wapi_search_handle_namespace (WapiHandleType type,
637 struct _WapiHandleUnshared *handle_data;
642 g_assert(_WAPI_SHARED_NAMESPACE(type));
644 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Lookup for handle named [%s] type %s", __func__,
645 utf8_name, _wapi_handle_typename[type]);
647 thr_ret = mono_os_mutex_lock (&scan_mutex);
648 g_assert (thr_ret == 0);
650 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
651 if (!_wapi_private_handles [i])
653 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
654 WapiSharedNamespace *sharedns;
656 handle_data = &_wapi_private_handles [i][k];
658 /* Check mutex, event, semaphore, timer, job and
659 * file-mapping object names. So far only mutex,
660 * semaphore and event are implemented.
662 if (!_WAPI_SHARED_NAMESPACE (handle_data->type)) {
666 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]);
668 sharedns=(WapiSharedNamespace *)&handle_data->u;
670 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: name is [%s]", __func__, sharedns->name);
672 if (strcmp (sharedns->name, utf8_name) == 0) {
673 if (handle_data->type != type) {
674 /* Its the wrong type, so fail now */
675 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]);
676 ret = _WAPI_HANDLE_INVALID;
679 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: handle 0x%x matches name and type", __func__, i);
688 thr_ret = mono_os_mutex_unlock (&scan_mutex);
689 g_assert (thr_ret == 0);
694 void _wapi_handle_ref (gpointer handle)
696 guint32 idx = GPOINTER_TO_UINT(handle);
697 struct _WapiHandleUnshared *handle_data;
699 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
700 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Attempting to ref invalid private handle %p", __func__, handle);
704 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
705 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Attempting to ref unused handle %p", __func__, handle);
709 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
711 InterlockedIncrement ((gint32 *)&handle_data->ref);
714 g_message ("%s: %s handle %p ref now %d", __func__,
715 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
717 _WAPI_PRIVATE_HANDLES(idx).ref);
721 /* The handle must not be locked on entry to this function */
722 static void _wapi_handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles)
724 guint32 idx = GPOINTER_TO_UINT(handle);
725 gboolean destroy = FALSE, early_exit = FALSE;
728 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
732 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
733 g_warning ("%s: Attempting to unref unused handle %p",
738 /* Possible race condition here if another thread refs the
739 * handle between here and setting the type to UNUSED. I
740 * could lock a mutex, but I'm not sure that allowing a handle
741 * reference to reach 0 isn't an application bug anyway.
743 destroy = (InterlockedDecrement ((gint32 *)&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
746 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
747 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
749 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
753 /* Need to copy the handle info, reset the slot in the
754 * array, and _only then_ call the close function to
755 * avoid race conditions (eg file descriptors being
756 * closed, and another file being opened getting the
757 * same fd racing the memset())
759 struct _WapiHandleUnshared handle_data;
760 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
761 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
763 thr_ret = mono_os_mutex_lock (&scan_mutex);
765 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Destroying handle %p", __func__, handle);
767 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
768 sizeof (struct _WapiHandleUnshared));
770 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
771 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
773 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
775 /* Destroy the mutex and cond var. We hope nobody
776 * tried to grab them between the handle unlock and
777 * now, but pthreads doesn't have a
778 * "unlock_and_destroy" atomic function.
780 thr_ret = mono_os_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
781 /*WARNING gross hack to make cleanup not crash when exiting without the whole runtime teardown.*/
782 if (thr_ret == EBUSY && ignore_private_busy_handles) {
786 g_error ("Error destroying handle %p mutex due to %d\n", handle, thr_ret);
788 thr_ret = mono_os_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
789 if (thr_ret == EBUSY && ignore_private_busy_handles)
791 else if (thr_ret != 0)
792 g_error ("Error destroying handle %p cond var due to %d\n", handle, thr_ret);
795 thr_ret = mono_os_mutex_unlock (&scan_mutex);
796 g_assert (thr_ret == 0);
801 if (close_func != NULL) {
802 close_func (handle, &handle_data.u);
807 void _wapi_handle_unref (gpointer handle)
809 _wapi_handle_unref_full (handle, FALSE);
812 void _wapi_handle_register_capabilities (WapiHandleType type,
813 WapiHandleCapability caps)
815 handle_caps[type] = caps;
818 gboolean _wapi_handle_test_capabilities (gpointer handle,
819 WapiHandleCapability caps)
821 guint32 idx = GPOINTER_TO_UINT(handle);
824 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
828 type = _WAPI_PRIVATE_HANDLES(idx).type;
830 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: testing 0x%x against 0x%x (%d)", __func__,
831 handle_caps[type], caps, handle_caps[type] & caps);
833 return((handle_caps[type] & caps) != 0);
836 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
838 if (handle_ops[type] != NULL &&
839 handle_ops[type]->close != NULL) {
840 return (handle_ops[type]->close);
846 void _wapi_handle_ops_close (gpointer handle, gpointer data)
848 guint32 idx = GPOINTER_TO_UINT(handle);
851 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
855 type = _WAPI_PRIVATE_HANDLES(idx).type;
857 if (handle_ops[type] != NULL &&
858 handle_ops[type]->close != NULL) {
859 handle_ops[type]->close (handle, data);
863 void _wapi_handle_ops_signal (gpointer handle)
865 guint32 idx = GPOINTER_TO_UINT(handle);
868 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
872 type = _WAPI_PRIVATE_HANDLES(idx).type;
874 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
875 handle_ops[type]->signal (handle);
879 gboolean _wapi_handle_ops_own (gpointer handle)
881 guint32 idx = GPOINTER_TO_UINT(handle);
884 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
888 type = _WAPI_PRIVATE_HANDLES(idx).type;
890 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
891 return(handle_ops[type]->own_handle (handle));
897 gboolean _wapi_handle_ops_isowned (gpointer handle)
899 guint32 idx = GPOINTER_TO_UINT(handle);
902 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
906 type = _WAPI_PRIVATE_HANDLES(idx).type;
908 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
909 return(handle_ops[type]->is_owned (handle));
915 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout, gboolean alertable)
917 guint32 idx = GPOINTER_TO_UINT(handle);
920 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
924 type = _WAPI_PRIVATE_HANDLES(idx).type;
926 if (handle_ops[type] != NULL &&
927 handle_ops[type]->special_wait != NULL) {
928 return(handle_ops[type]->special_wait (handle, timeout, alertable));
934 void _wapi_handle_ops_prewait (gpointer handle)
936 guint32 idx = GPOINTER_TO_UINT (handle);
939 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
943 type = _WAPI_PRIVATE_HANDLES (idx).type;
945 if (handle_ops[type] != NULL &&
946 handle_ops[type]->prewait != NULL) {
947 handle_ops[type]->prewait (handle);
954 * @handle: The handle to release
956 * Closes and invalidates @handle, releasing any resources it
957 * consumes. When the last handle to a temporary or non-persistent
958 * object is closed, that object can be deleted. Closing the same
959 * handle twice is an error.
961 * Return value: %TRUE on success, %FALSE otherwise.
963 gboolean CloseHandle(gpointer handle)
965 if (handle == NULL) {
966 /* Problem: because we map file descriptors to the
967 * same-numbered handle we can't tell the difference
968 * between a bogus handle and the handle to stdin.
969 * Assume that it's the console handle if that handle
972 if (_WAPI_PRIVATE_HANDLES (0).type != WAPI_HANDLE_CONSOLE) {
973 SetLastError (ERROR_INVALID_PARAMETER);
977 if (handle == _WAPI_HANDLE_INVALID){
978 SetLastError (ERROR_INVALID_PARAMETER);
982 _wapi_handle_unref (handle);
987 /* Lots more to implement here, but this is all we need at the moment */
988 gboolean DuplicateHandle (gpointer srcprocess, gpointer src,
989 gpointer targetprocess, gpointer *target,
990 guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 options G_GNUC_UNUSED)
992 if (srcprocess != _WAPI_PROCESS_CURRENT ||
993 targetprocess != _WAPI_PROCESS_CURRENT) {
994 /* Duplicating other process's handles is not supported */
995 SetLastError (ERROR_INVALID_HANDLE);
999 if (src == _WAPI_PROCESS_CURRENT) {
1000 *target = _wapi_process_duplicate ();
1001 } else if (src == _WAPI_THREAD_CURRENT) {
1002 g_assert_not_reached ();
1004 _wapi_handle_ref (src);
1011 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1017 guint32 count, i, iter=0;
1020 WapiHandleType type;
1022 /* Lock all the handles, with backoff */
1024 for(i=0; i<numhandles; i++) {
1025 gpointer handle = handles[i];
1026 guint32 idx = GPOINTER_TO_UINT(handle);
1028 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempting to lock %p", __func__, handle);
1030 type = _WAPI_PRIVATE_HANDLES(idx).type;
1032 thr_ret = _wapi_handle_trylock_handle (handle);
1037 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: attempt failed for %p: %s", __func__,
1038 handle, strerror (thr_ret));
1041 handle = handles[i];
1042 idx = GPOINTER_TO_UINT(handle);
1044 thr_ret = _wapi_handle_unlock_handle (handle);
1045 g_assert (thr_ret == 0);
1048 /* If iter ever reaches 100 the nanosleep will
1049 * return EINVAL immediately, but we have a
1050 * design flaw if that happens.
1054 g_warning ("%s: iteration overflow!",
1059 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Backing off for %d ms", __func__,
1061 _wapi_handle_spin (10 * iter);
1067 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Locked all handles", __func__);
1072 for(i=0; i<numhandles; i++) {
1073 gpointer handle = handles[i];
1074 guint32 idx = GPOINTER_TO_UINT(handle);
1076 type = _WAPI_PRIVATE_HANDLES(idx).type;
1078 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Checking handle %p", __func__, handle);
1080 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1081 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1082 (_wapi_handle_issignalled (handle))) {
1085 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Handle %p signalled", __func__,
1093 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %d event handles signalled", __func__, count);
1095 if ((waitall == TRUE && count == numhandles) ||
1096 (waitall == FALSE && count > 0)) {
1102 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Returning %d", __func__, ret);
1109 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1114 for(i=0; i<numhandles; i++) {
1115 gpointer handle = handles[i];
1117 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unlocking handle %p", __func__, handle);
1119 thr_ret = _wapi_handle_unlock_handle (handle);
1120 g_assert (thr_ret == 0);
1125 signal_handle_and_unref (gpointer handle)
1128 mono_mutex_t *mutex;
1133 /* If we reach here, then interrupt token is set to the flag value, which
1134 * means that the target thread is either
1135 * - before the first CAS in timedwait, which means it won't enter the wait.
1136 * - it is after the first CAS, so it is already waiting, or it will enter
1137 * the wait, and it will be interrupted by the broadcast. */
1138 idx = GPOINTER_TO_UINT (handle);
1139 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1140 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1142 mono_os_mutex_lock (mutex);
1143 mono_os_cond_broadcast (cond);
1144 mono_os_mutex_unlock (mutex);
1146 _wapi_handle_unref (handle);
1150 _wapi_handle_timedwait_signal_handle (gpointer handle, guint32 timeout, gboolean poll, gboolean *alerted)
1155 mono_mutex_t *mutex;
1157 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: waiting for %p (type %s)", __func__, handle,
1158 _wapi_handle_typename[_wapi_handle_type (handle)]);
1163 idx = GPOINTER_TO_UINT(handle);
1166 mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
1169 _wapi_handle_ref (handle);
1172 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1173 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1176 res = mono_os_cond_timedwait (cond, mutex, timeout);
1178 /* This is needed when waiting for process handles */
1181 * pthread_cond_(timed)wait() can return 0 even if the condition was not
1182 * signalled. This happens at least on Darwin. We surface this, i.e., we
1183 * get spurious wake-ups.
1185 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
1187 res = mono_os_cond_timedwait (cond, mutex, timeout);
1189 if (timeout < 100) {
1190 /* Real timeout is less than 100ms time */
1191 res = mono_os_cond_timedwait (cond, mutex, timeout);
1193 res = mono_os_cond_timedwait (cond, mutex, 100);
1195 /* Mask the fake timeout, this will cause
1196 * another poll if the cond was not really signaled
1198 if (res == ETIMEDOUT)
1205 mono_thread_info_uninstall_interrupt (alerted);
1207 /* if it is alerted, then the handle is unref in the interrupt callback */
1208 _wapi_handle_unref (handle);
1216 _wapi_free_share_info (_WapiFileShare *share_info)
1218 file_share_hash_lock ();
1219 g_hash_table_remove (file_share_hash, share_info);
1220 file_share_hash_unlock ();
1221 /* The hashtable dtor frees share_info */
1225 wapi_share_info_equal (gconstpointer ka, gconstpointer kb)
1227 const _WapiFileShare *s1 = (const _WapiFileShare *)ka;
1228 const _WapiFileShare *s2 = (const _WapiFileShare *)kb;
1230 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
1234 wapi_share_info_hash (gconstpointer data)
1236 const _WapiFileShare *s = (const _WapiFileShare *)data;
1241 gboolean _wapi_handle_get_or_set_share (guint64 device, guint64 inode,
1242 guint32 new_sharemode,
1244 guint32 *old_sharemode,
1245 guint32 *old_access,
1246 struct _WapiFileShare **share_info)
1248 struct _WapiFileShare *file_share;
1250 gboolean exists = FALSE;
1252 /* Prevent new entries racing with us */
1253 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1254 g_assert (thr_ret == 0);
1259 * Instead of allocating a 4MB array, we use a hash table to keep track of this
1260 * info. This is needed even if SHM is disabled, to track sharing inside
1261 * the current process.
1263 if (!file_share_hash) {
1264 file_share_hash = g_hash_table_new_full (wapi_share_info_hash, wapi_share_info_equal, NULL, g_free);
1265 mono_os_mutex_init_recursive (&file_share_hash_mutex);
1268 tmp.device = device;
1271 file_share_hash_lock ();
1273 file_share = (_WapiFileShare *)g_hash_table_lookup (file_share_hash, &tmp);
1275 *old_sharemode = file_share->sharemode;
1276 *old_access = file_share->access;
1277 *share_info = file_share;
1279 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1282 file_share = g_new0 (_WapiFileShare, 1);
1284 file_share->device = device;
1285 file_share->inode = inode;
1286 file_share->opened_by_pid = _wapi_getpid ();
1287 file_share->sharemode = new_sharemode;
1288 file_share->access = new_access;
1289 file_share->handle_refs = 1;
1290 *share_info = file_share;
1292 g_hash_table_insert (file_share_hash, file_share, file_share);
1295 file_share_hash_unlock ();
1297 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1302 void _wapi_handle_dump (void)
1304 struct _WapiHandleUnshared *handle_data;
1308 thr_ret = mono_os_mutex_lock (&scan_mutex);
1309 g_assert (thr_ret == 0);
1311 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1312 if (_wapi_private_handles [i]) {
1313 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1314 handle_data = &_wapi_private_handles [i][k];
1316 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1320 g_print ("%3x [%7s] %s %d ",
1321 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1322 _wapi_handle_typename[handle_data->type],
1323 handle_data->signalled?"Sg":"Un",
1325 if (handle_details[handle_data->type])
1326 handle_details[handle_data->type](&handle_data->u);
1332 thr_ret = mono_os_mutex_unlock (&scan_mutex);
1333 g_assert (thr_ret == 0);
1336 static void _wapi_shared_details (gpointer handle_info)
1338 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1340 g_print ("offset: 0x%x", shared->offset);