2 * handles.c: Generic and internal operations on handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2006 Novell, Inc.
16 #include <sys/types.h>
17 #ifdef HAVE_SYS_SOCKET_H
18 # include <sys/socket.h>
23 #ifdef HAVE_SYS_MMAN_H
24 # include <sys/mman.h>
31 #include <mono/io-layer/wapi.h>
32 #include <mono/io-layer/wapi-private.h>
33 #include <mono/io-layer/handles-private.h>
34 #include <mono/io-layer/mono-mutex.h>
35 #include <mono/io-layer/misc-private.h>
36 #include <mono/io-layer/shared.h>
37 #include <mono/io-layer/collection.h>
38 #include <mono/io-layer/process-private.h>
39 #include <mono/io-layer/critical-section-private.h>
44 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
46 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
47 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
55 #ifndef DISABLE_SOCKETS
61 &_wapi_namedmutex_ops,
63 &_wapi_namedevent_ops,
66 static void _wapi_shared_details (gpointer handle_info);
68 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
71 _wapi_console_details,
72 _wapi_shared_details, /* thread */
76 NULL, /* Nothing useful to see in a socket handle */
77 NULL, /* Nothing useful to see in a find handle */
78 _wapi_shared_details, /* process */
80 _wapi_shared_details, /* namedmutex */
81 _wapi_shared_details, /* namedsem */
82 _wapi_shared_details, /* namedevent */
85 const char *_wapi_handle_typename[] = {
104 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
105 * If 4M handles are not enough... Oh, well... we will crash.
107 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
108 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
110 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
111 static guint32 _wapi_private_handle_count = 0;
112 static guint32 _wapi_private_handle_slot_count = 0;
114 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
117 * If SHM is enabled, this will point to shared memory, otherwise it will be NULL.
119 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
122 * If SHM is disabled, this will point to a hash of _WapiFileShare structures, otherwise
123 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
126 static GHashTable *file_share_hash;
127 static CRITICAL_SECTION file_share_hash_mutex;
129 #define file_share_hash_lock() EnterCriticalSection (&file_share_hash_mutex)
130 #define file_share_hash_unlock() LeaveCriticalSection (&file_share_hash_mutex)
132 guint32 _wapi_fd_reserve;
135 * This is an internal handle which is used for handling waiting for multiple handles.
136 * Threads which wait for multiple handles wait on this one handle, and when a handle
137 * is signalled, this handle is signalled too.
139 static gpointer _wapi_global_signal_handle;
141 /* Point to the mutex/cond inside _wapi_global_signal_handle */
142 mono_mutex_t *_wapi_global_signal_mutex;
143 pthread_cond_t *_wapi_global_signal_cond;
146 gboolean _wapi_has_shut_down = FALSE;
148 /* Use this instead of getpid(), to cope with linuxthreads. It's a
149 * function rather than a variable lookup because we need to get at
150 * this before share_init() might have been called.
152 static pid_t _wapi_pid;
153 static mono_once_t pid_init_once = MONO_ONCE_INIT;
155 static gpointer _wapi_handle_real_new (WapiHandleType type, gpointer handle_specific);
157 static void pid_init (void)
159 _wapi_pid = getpid ();
162 pid_t _wapi_getpid (void)
164 mono_once (&pid_init_once, pid_init);
170 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
172 static void handle_cleanup (void)
176 _wapi_process_signal_self ();
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 int type = handle_data->type;
191 if (_WAPI_SHARED_HANDLE (type)) {
192 gpointer handle = GINT_TO_POINTER (i*_WAPI_HANDLE_INITIAL_COUNT+j);
194 if (type == WAPI_HANDLE_THREAD) {
195 /* Special-case thread handles
196 * because they need extra
197 * cleanup. This also avoids
198 * a race condition between
199 * the application exit and
200 * the finalizer thread - if
201 * it finishes up between now
202 * and actual app termination
203 * it will find all its handle
204 * details have been blown
205 * away, so this sets those
208 _wapi_thread_set_termination_details (handle, 0);
211 for(k = handle_data->ref; k > 0; k--) {
213 g_message ("%s: unreffing %s handle %p", __func__, _wapi_handle_typename[type], handle);
216 _wapi_handle_unref (handle);
222 _wapi_shm_semaphores_remove ();
224 _wapi_shm_detach (WAPI_SHM_DATA);
225 _wapi_shm_detach (WAPI_SHM_FILESHARE);
227 if (file_share_hash) {
228 g_hash_table_destroy (file_share_hash);
229 DeleteCriticalSection (&file_share_hash_mutex);
232 for (i = 0; i < _WAPI_PRIVATE_MAX_SLOTS; ++i)
233 g_free (_wapi_private_handles [i]);
236 void _wapi_cleanup ()
238 g_assert (_wapi_has_shut_down == FALSE);
240 _wapi_has_shut_down = TRUE;
242 _wapi_critical_section_cleanup ();
243 _wapi_error_cleanup ();
244 _wapi_thread_cleanup ();
247 static mono_once_t shared_init_once = MONO_ONCE_INIT;
248 static void shared_init (void)
250 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
251 == WAPI_HANDLE_COUNT);
253 _wapi_fd_reserve = getdtablesize();
255 /* This is needed by the code in _wapi_handle_new_internal */
256 _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
260 * The entries in _wapi_private_handles reserved for fds are allocated lazily to
264 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
265 _WAPI_HANDLE_INITIAL_COUNT);
268 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
269 _wapi_private_handle_slot_count ++;
270 } while(_wapi_fd_reserve > _wapi_private_handle_count);
272 _wapi_shm_semaphores_init ();
274 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
275 g_assert (_wapi_shared_layout != NULL);
277 if (_wapi_shm_enabled ()) {
278 /* This allocates a 4mb array, so do it only if SHM is enabled */
279 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
280 g_assert (_wapi_fileshare_layout != NULL);
283 #if !defined (DISABLE_SHARED_HANDLES)
284 if (_wapi_shm_enabled ())
285 _wapi_collection_init ();
288 /* Can't call wapi_handle_new as it calls us recursively */
289 _wapi_global_signal_handle = _wapi_handle_real_new (WAPI_HANDLE_EVENT, NULL);
291 _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
292 _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
294 /* Using g_atexit here instead of an explicit function call in
295 * a cleanup routine lets us cope when a third-party library
296 * calls exit (eg if an X client loses the connection to its
299 g_atexit (handle_cleanup);
302 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
304 gpointer handle_specific)
306 g_assert (_wapi_has_shut_down == FALSE);
309 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
310 handle->signalled = FALSE;
311 handle->handle_refs = 1;
313 if (handle_specific != NULL) {
314 memcpy (&handle->u, handle_specific, sizeof (handle->u));
318 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
319 WapiHandleType type, gpointer handle_specific)
323 g_assert (_wapi_has_shut_down == FALSE);
326 handle->signalled = FALSE;
329 if (!_WAPI_SHARED_HANDLE(type)) {
330 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
331 g_assert (thr_ret == 0);
333 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
334 g_assert (thr_ret == 0);
336 if (handle_specific != NULL) {
337 memcpy (&handle->u, handle_specific,
343 static guint32 _wapi_handle_new_shared (WapiHandleType type,
344 gpointer handle_specific)
347 static guint32 last = 1;
350 g_assert (_wapi_has_shut_down == FALSE);
352 /* Leave the first slot empty as a guard */
354 /* FIXME: expandable array */
355 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
356 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
358 if(handle->type == WAPI_HANDLE_UNUSED) {
359 thr_ret = _wapi_handle_lock_shared_handles ();
360 g_assert (thr_ret == 0);
362 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
365 _wapi_handle_init_shared (handle, type,
368 _wapi_handle_unlock_shared_handles ();
372 /* Someone else beat us to it, just
377 _wapi_handle_unlock_shared_handles ();
382 /* Try again from the beginning */
387 /* Will need to expand the array. The caller will sort it out */
393 * _wapi_handle_new_internal:
394 * @type: Init handle to this type
396 * Search for a free handle and initialize it. Return the handle on
397 * success and 0 on failure. This is only called from
398 * _wapi_handle_new, and scan_mutex must be held.
400 static guint32 _wapi_handle_new_internal (WapiHandleType type,
401 gpointer handle_specific)
404 static guint32 last = 0;
405 gboolean retry = FALSE;
407 g_assert (_wapi_has_shut_down == FALSE);
409 /* A linear scan should be fast enough. Start from the last
410 * allocation, assuming that handles are allocated more often
411 * than they're freed. Leave the space reserved for file
415 if (last < _wapi_fd_reserve) {
416 last = _wapi_fd_reserve;
423 for(i = SLOT_INDEX (count); i < _wapi_private_handle_slot_count; i++) {
424 if (_wapi_private_handles [i]) {
425 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
426 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
428 if(handle->type == WAPI_HANDLE_UNUSED) {
431 _wapi_handle_init (handle, type, handle_specific);
439 if(retry && last > _wapi_fd_reserve) {
440 /* Try again from the beginning */
441 last = _wapi_fd_reserve;
445 /* Will need to expand the array. The caller will sort it out */
450 static gpointer _wapi_handle_real_new (WapiHandleType type, gpointer handle_specific)
452 guint32 handle_idx = 0;
457 g_message ("%s: Creating new handle of type %s", __func__,
458 _wapi_handle_typename[type]);
461 g_assert(!_WAPI_FD_HANDLE(type));
463 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
464 (void *)&scan_mutex);
465 thr_ret = mono_mutex_lock (&scan_mutex);
466 g_assert (thr_ret == 0);
468 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
469 /* Try and expand the array, and have another go */
470 int idx = SLOT_INDEX (_wapi_private_handle_count);
471 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
475 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
476 _WAPI_HANDLE_INITIAL_COUNT);
478 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
479 _wapi_private_handle_slot_count ++;
482 thr_ret = mono_mutex_unlock (&scan_mutex);
483 g_assert (thr_ret == 0);
484 pthread_cleanup_pop (0);
486 if (handle_idx == 0) {
487 /* We ran out of slots */
488 handle = _WAPI_HANDLE_INVALID;
492 /* Make sure we left the space for fd mappings */
493 g_assert (handle_idx >= _wapi_fd_reserve);
495 handle = GUINT_TO_POINTER (handle_idx);
498 g_message ("%s: Allocated new handle %p", __func__, handle);
501 if (_WAPI_SHARED_HANDLE(type)) {
502 /* Add the shared section too */
505 ref = _wapi_handle_new_shared (type, handle_specific);
507 _wapi_handle_collect ();
508 _wapi_process_reap ();
509 ref = _wapi_handle_new_shared (type, handle_specific);
511 /* FIXME: grow the arrays */
512 handle = _WAPI_HANDLE_INVALID;
517 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
519 g_message ("%s: New shared handle at offset 0x%x", __func__,
528 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
530 g_assert (_wapi_has_shut_down == FALSE);
532 mono_once (&shared_init_once, shared_init);
534 return _wapi_handle_real_new (type, handle_specific);
537 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
540 guint32 handle_idx = 0;
541 gpointer handle = INVALID_HANDLE_VALUE;
543 struct _WapiHandleShared *shared;
544 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
546 g_assert (_wapi_has_shut_down == FALSE);
548 mono_once (&shared_init_once, shared_init);
551 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
552 _wapi_handle_typename[type], offset);
555 g_assert(!_WAPI_FD_HANDLE(type));
556 g_assert(_WAPI_SHARED_HANDLE(type));
557 g_assert(offset != 0);
559 shared = &_wapi_shared_layout->handles[offset];
561 /* Bump up the timestamp for this offset */
562 InterlockedExchange ((gint32 *)&shared->timestamp, now);
565 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
566 (void *)&scan_mutex);
567 thr_ret = mono_mutex_lock (&scan_mutex);
568 g_assert (thr_ret == 0);
570 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
571 if (_wapi_private_handles [i]) {
572 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
573 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
575 if (handle_data->type == type &&
576 handle_data->u.shared.offset == offset) {
577 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
578 goto first_pass_done;
585 thr_ret = mono_mutex_unlock (&scan_mutex);
586 g_assert (thr_ret == 0);
587 pthread_cleanup_pop (0);
589 if (handle != INVALID_HANDLE_VALUE) {
590 _wapi_handle_ref (handle);
593 g_message ("%s: Returning old handle %p referencing 0x%x",
594 __func__, handle, offset);
599 /* Prevent entries expiring under us as we search */
600 thr_ret = _wapi_handle_lock_shared_handles ();
601 g_assert (thr_ret == 0);
603 if (shared->type == WAPI_HANDLE_UNUSED) {
604 /* Someone deleted this handle while we were working */
606 g_message ("%s: Handle at 0x%x unused", __func__, offset);
611 if (shared->type != type) {
613 g_message ("%s: Wrong type at %d 0x%x! Found %s wanted %s",
614 __func__, offset, offset,
615 _wapi_handle_typename[shared->type],
616 _wapi_handle_typename[type]);
621 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
622 (void *)&scan_mutex);
623 thr_ret = mono_mutex_lock (&scan_mutex);
624 g_assert (thr_ret == 0);
626 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
627 /* Try and expand the array, and have another go */
628 int idx = SLOT_INDEX (_wapi_private_handle_count);
629 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
630 _WAPI_HANDLE_INITIAL_COUNT);
632 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
633 _wapi_private_handle_slot_count ++;
636 thr_ret = mono_mutex_unlock (&scan_mutex);
637 g_assert (thr_ret == 0);
638 pthread_cleanup_pop (0);
640 /* Make sure we left the space for fd mappings */
641 g_assert (handle_idx >= _wapi_fd_reserve);
643 handle = GUINT_TO_POINTER (handle_idx);
645 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
646 InterlockedIncrement ((gint32 *)&shared->handle_refs);
649 g_message ("%s: Allocated new handle %p referencing 0x%x (shared refs %d)", __func__, handle, offset, shared->handle_refs);
653 _wapi_handle_unlock_shared_handles ();
659 init_handles_slot (int idx)
663 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
664 (void *)&scan_mutex);
665 thr_ret = mono_mutex_lock (&scan_mutex);
666 g_assert (thr_ret == 0);
668 if (_wapi_private_handles [idx] == NULL) {
669 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
670 _WAPI_HANDLE_INITIAL_COUNT);
671 g_assert (_wapi_private_handles [idx]);
674 thr_ret = mono_mutex_unlock (&scan_mutex);
675 g_assert (thr_ret == 0);
676 pthread_cleanup_pop (0);
679 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
680 gpointer handle_specific)
682 struct _WapiHandleUnshared *handle;
685 g_assert (_wapi_has_shut_down == FALSE);
687 mono_once (&shared_init_once, shared_init);
690 g_message ("%s: Creating new handle of type %s", __func__,
691 _wapi_handle_typename[type]);
694 g_assert(_WAPI_FD_HANDLE(type));
695 g_assert(!_WAPI_SHARED_HANDLE(type));
697 if (fd >= _wapi_fd_reserve) {
699 g_message ("%s: fd %d is too big", __func__, fd);
702 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
705 /* Initialize the array entries on demand */
706 if (_wapi_private_handles [SLOT_INDEX (fd)] == NULL)
707 init_handles_slot (SLOT_INDEX (fd));
709 handle = &_WAPI_PRIVATE_HANDLES(fd);
711 if (handle->type != WAPI_HANDLE_UNUSED) {
713 g_message ("%s: fd %d is already in use!", __func__, fd);
715 /* FIXME: clean up this handle? We can't do anything
716 * with the fd, cos thats the new one
721 g_message ("%s: Assigning new fd handle %d", __func__, fd);
724 /* Prevent file share entries racing with us, when the file
725 * handle is only half initialised
727 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
728 g_assert(thr_ret == 0);
730 _wapi_handle_init (handle, type, handle_specific);
732 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
734 return(GUINT_TO_POINTER(fd));
737 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
738 gpointer *handle_specific)
740 struct _WapiHandleUnshared *handle_data;
741 guint32 handle_idx = GPOINTER_TO_UINT(handle);
743 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
747 /* Initialize the array entries on demand */
748 if (_wapi_private_handles [SLOT_INDEX (handle_idx)] == NULL)
749 init_handles_slot (SLOT_INDEX (handle_idx));
751 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
753 if (handle_data->type != type) {
757 if (handle_specific == NULL) {
761 if (_WAPI_SHARED_HANDLE(type)) {
762 struct _WapiHandle_shared_ref *ref;
763 struct _WapiHandleShared *shared_handle_data;
765 ref = &handle_data->u.shared;
766 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
768 if (shared_handle_data->type != type) {
769 /* The handle must have been deleted on us
774 *handle_specific = &shared_handle_data->u;
776 *handle_specific = &handle_data->u;
783 _wapi_handle_foreach (WapiHandleType type,
784 gboolean (*on_each)(gpointer test, gpointer user),
787 struct _WapiHandleUnshared *handle_data = NULL;
792 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
793 (void *)&scan_mutex);
794 thr_ret = mono_mutex_lock (&scan_mutex);
795 g_assert (thr_ret == 0);
797 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
798 if (_wapi_private_handles [i]) {
799 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
800 handle_data = &_wapi_private_handles [i][k];
802 if (handle_data->type == type) {
803 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
804 if (on_each (ret, user_data) == TRUE)
811 thr_ret = mono_mutex_unlock (&scan_mutex);
812 g_assert (thr_ret == 0);
813 pthread_cleanup_pop (0);
816 /* This might list some shared handles twice if they are already
817 * opened by this process, and the check function returns FALSE the
818 * first time. Shared handles that are created during the search are
819 * unreffed if the check function returns FALSE, so callers must not
820 * rely on the handle persisting (unless the check function returns
823 gpointer _wapi_search_handle (WapiHandleType type,
824 gboolean (*check)(gpointer test, gpointer user),
826 gpointer *handle_specific,
827 gboolean search_shared)
829 struct _WapiHandleUnshared *handle_data = NULL;
830 struct _WapiHandleShared *shared = NULL;
833 gboolean found = FALSE;
836 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
837 (void *)&scan_mutex);
838 thr_ret = mono_mutex_lock (&scan_mutex);
839 g_assert (thr_ret == 0);
841 for (i = SLOT_INDEX (0); !found && i < _wapi_private_handle_slot_count; i++) {
842 if (_wapi_private_handles [i]) {
843 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
844 handle_data = &_wapi_private_handles [i][k];
846 if (handle_data->type == type) {
847 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
848 if (check (ret, user_data) == TRUE) {
849 _wapi_handle_ref (ret);
852 if (_WAPI_SHARED_HANDLE (type)) {
853 shared = &_wapi_shared_layout->handles[i];
863 thr_ret = mono_mutex_unlock (&scan_mutex);
864 g_assert (thr_ret == 0);
865 pthread_cleanup_pop (0);
867 if (!found && search_shared && _WAPI_SHARED_HANDLE (type)) {
868 /* Not found yet, so search the shared memory too */
870 g_message ("%s: Looking at other shared handles...", __func__);
873 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
874 shared = &_wapi_shared_layout->handles[i];
876 if (shared->type == type) {
877 /* Tell new_from_offset to not
878 * timestamp this handle, because
879 * otherwise it will ping every handle
880 * in the list and they will never
883 ret = _wapi_handle_new_from_offset (type, i,
885 if (ret == INVALID_HANDLE_VALUE) {
886 /* This handle was deleted
887 * while we were looking at it
893 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
896 /* It's possible that the shared part
897 * of this handle has now been blown
898 * away (after new_from_offset
899 * successfully opened it,) if its
900 * timestamp is too old. The check
901 * function needs to be aware of this,
902 * and cope if the handle has
905 if (check (ret, user_data) == TRUE) {
906 /* Timestamp this handle, but make
907 * sure it still exists first
909 thr_ret = _wapi_handle_lock_shared_handles ();
910 g_assert (thr_ret == 0);
912 if (shared->type == type) {
913 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
914 InterlockedExchange ((gint32 *)&shared->timestamp, now);
917 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
919 _wapi_handle_unlock_shared_handles ();
922 /* It's been deleted,
926 _wapi_handle_unlock_shared_handles ();
930 /* This isn't the handle we're looking
931 * for, so drop the reference we took
932 * in _wapi_handle_new_from_offset ()
934 _wapi_handle_unref (ret);
944 if(handle_specific != NULL) {
945 if (_WAPI_SHARED_HANDLE(type)) {
946 g_assert(shared->type == type);
948 *handle_specific = &shared->u;
950 *handle_specific = &handle_data->u;
958 /* Returns the offset of the metadata array, or -1 on error, or 0 for
959 * not found (0 is not a valid offset)
961 gint32 _wapi_search_handle_namespace (WapiHandleType type,
964 struct _WapiHandleShared *shared_handle_data;
969 g_assert(_WAPI_SHARED_HANDLE(type));
972 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
973 utf8_name, _wapi_handle_typename[type]);
976 /* Do a handle collection before starting to look, so that any
977 * stale cruft gets removed
979 _wapi_handle_collect ();
981 thr_ret = _wapi_handle_lock_shared_handles ();
982 g_assert (thr_ret == 0);
984 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
985 WapiSharedNamespace *sharedns;
987 shared_handle_data = &_wapi_shared_layout->handles[i];
989 /* Check mutex, event, semaphore, timer, job and
990 * file-mapping object names. So far only mutex,
991 * semaphore and event are implemented.
993 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
998 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
1001 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
1004 g_message ("%s: name is [%s]", __func__, sharedns->name);
1007 if (strcmp (sharedns->name, utf8_name) == 0) {
1008 if (shared_handle_data->type != type) {
1009 /* Its the wrong type, so fail now */
1011 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
1017 g_message ("%s: handle 0x%x matches name and type", __func__, i);
1026 _wapi_handle_unlock_shared_handles ();
1031 void _wapi_handle_ref (gpointer handle)
1033 guint32 idx = GPOINTER_TO_UINT(handle);
1034 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1035 struct _WapiHandleUnshared *handle_data;
1037 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1041 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1042 g_warning ("%s: Attempting to ref unused handle %p", __func__,
1047 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
1049 InterlockedIncrement ((gint32 *)&handle_data->ref);
1051 /* It's possible for processes to exit before getting around
1052 * to updating timestamps in the collection thread, so if a
1053 * shared handle is reffed do the timestamp here as well just
1056 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
1057 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
1059 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
1063 g_message ("%s: %s handle %p ref now %d", __func__,
1064 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1066 _WAPI_PRIVATE_HANDLES(idx).ref);
1070 /* The handle must not be locked on entry to this function */
1071 void _wapi_handle_unref (gpointer handle)
1073 guint32 idx = GPOINTER_TO_UINT(handle);
1074 gboolean destroy = FALSE;
1077 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1081 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1082 g_warning ("%s: Attempting to unref unused handle %p",
1087 /* Possible race condition here if another thread refs the
1088 * handle between here and setting the type to UNUSED. I
1089 * could lock a mutex, but I'm not sure that allowing a handle
1090 * reference to reach 0 isn't an application bug anyway.
1092 destroy = (InterlockedDecrement ((gint32 *)&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
1095 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
1096 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1098 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
1102 /* Need to copy the handle info, reset the slot in the
1103 * array, and _only then_ call the close function to
1104 * avoid race conditions (eg file descriptors being
1105 * closed, and another file being opened getting the
1106 * same fd racing the memset())
1108 struct _WapiHandleUnshared handle_data;
1109 struct _WapiHandleShared shared_handle_data;
1110 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
1111 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
1112 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
1115 /* If this is a shared handle we need to take
1116 * the shared lock outside of the scan_mutex
1117 * lock to avoid deadlocks
1119 thr_ret = _wapi_handle_lock_shared_handles ();
1120 g_assert (thr_ret == 0);
1123 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
1124 thr_ret = mono_mutex_lock (&scan_mutex);
1127 g_message ("%s: Destroying handle %p", __func__, handle);
1130 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
1131 sizeof (struct _WapiHandleUnshared));
1133 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
1134 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
1136 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
1139 /* Destroy the mutex and cond var. We hope nobody
1140 * tried to grab them between the handle unlock and
1141 * now, but pthreads doesn't have a
1142 * "unlock_and_destroy" atomic function.
1144 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
1145 g_assert (thr_ret == 0);
1147 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
1148 g_assert (thr_ret == 0);
1150 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
1152 memcpy (&shared_handle_data, shared,
1153 sizeof (struct _WapiHandleShared));
1155 /* It's possible that this handle is already
1156 * pointing at a deleted shared section
1159 g_message ("%s: %s handle %p shared refs before dec %d", __func__, _wapi_handle_typename[type], handle, shared->handle_refs);
1162 if (shared->handle_refs > 0) {
1163 shared->handle_refs--;
1164 if (shared->handle_refs == 0) {
1165 memset (shared, '\0', sizeof (struct _WapiHandleShared));
1170 thr_ret = mono_mutex_unlock (&scan_mutex);
1171 g_assert (thr_ret == 0);
1172 pthread_cleanup_pop (0);
1175 _wapi_handle_unlock_shared_handles ();
1178 if (close_func != NULL) {
1180 close_func (handle, &shared_handle_data.u);
1182 close_func (handle, &handle_data.u);
1188 void _wapi_handle_register_capabilities (WapiHandleType type,
1189 WapiHandleCapability caps)
1191 handle_caps[type] = caps;
1194 gboolean _wapi_handle_test_capabilities (gpointer handle,
1195 WapiHandleCapability caps)
1197 guint32 idx = GPOINTER_TO_UINT(handle);
1198 WapiHandleType type;
1200 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1204 type = _WAPI_PRIVATE_HANDLES(idx).type;
1207 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
1208 handle_caps[type], caps, handle_caps[type] & caps);
1211 return((handle_caps[type] & caps) != 0);
1214 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
1216 if (handle_ops[type] != NULL &&
1217 handle_ops[type]->close != NULL) {
1218 return (handle_ops[type]->close);
1224 void _wapi_handle_ops_close (gpointer handle, gpointer data)
1226 guint32 idx = GPOINTER_TO_UINT(handle);
1227 WapiHandleType type;
1229 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1233 type = _WAPI_PRIVATE_HANDLES(idx).type;
1235 if (handle_ops[type] != NULL &&
1236 handle_ops[type]->close != NULL) {
1237 handle_ops[type]->close (handle, data);
1241 void _wapi_handle_ops_signal (gpointer handle)
1243 guint32 idx = GPOINTER_TO_UINT(handle);
1244 WapiHandleType type;
1246 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1250 type = _WAPI_PRIVATE_HANDLES(idx).type;
1252 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1253 handle_ops[type]->signal (handle);
1257 gboolean _wapi_handle_ops_own (gpointer handle)
1259 guint32 idx = GPOINTER_TO_UINT(handle);
1260 WapiHandleType type;
1262 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1266 type = _WAPI_PRIVATE_HANDLES(idx).type;
1268 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1269 return(handle_ops[type]->own_handle (handle));
1275 gboolean _wapi_handle_ops_isowned (gpointer handle)
1277 guint32 idx = GPOINTER_TO_UINT(handle);
1278 WapiHandleType type;
1280 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1284 type = _WAPI_PRIVATE_HANDLES(idx).type;
1286 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1287 return(handle_ops[type]->is_owned (handle));
1293 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1295 guint32 idx = GPOINTER_TO_UINT(handle);
1296 WapiHandleType type;
1298 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1299 return(WAIT_FAILED);
1302 type = _WAPI_PRIVATE_HANDLES(idx).type;
1304 if (handle_ops[type] != NULL &&
1305 handle_ops[type]->special_wait != NULL) {
1306 return(handle_ops[type]->special_wait (handle, timeout));
1308 return(WAIT_FAILED);
1312 void _wapi_handle_ops_prewait (gpointer handle)
1314 guint32 idx = GPOINTER_TO_UINT (handle);
1315 WapiHandleType type;
1317 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1321 type = _WAPI_PRIVATE_HANDLES (idx).type;
1323 if (handle_ops[type] != NULL &&
1324 handle_ops[type]->prewait != NULL) {
1325 handle_ops[type]->prewait (handle);
1332 * @handle: The handle to release
1334 * Closes and invalidates @handle, releasing any resources it
1335 * consumes. When the last handle to a temporary or non-persistent
1336 * object is closed, that object can be deleted. Closing the same
1337 * handle twice is an error.
1339 * Return value: %TRUE on success, %FALSE otherwise.
1341 gboolean CloseHandle(gpointer handle)
1343 if (handle == NULL) {
1344 /* Problem: because we map file descriptors to the
1345 * same-numbered handle we can't tell the difference
1346 * between a bogus handle and the handle to stdin.
1347 * Assume that it's the console handle if that handle
1350 if (_WAPI_PRIVATE_HANDLES (0).type != WAPI_HANDLE_CONSOLE) {
1351 SetLastError (ERROR_INVALID_PARAMETER);
1355 if (handle == _WAPI_HANDLE_INVALID){
1356 SetLastError (ERROR_INVALID_PARAMETER);
1360 _wapi_handle_unref (handle);
1365 /* Lots more to implement here, but this is all we need at the moment */
1366 gboolean DuplicateHandle (gpointer srcprocess, gpointer src,
1367 gpointer targetprocess, gpointer *target,
1368 guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 options G_GNUC_UNUSED)
1370 if (srcprocess != _WAPI_PROCESS_CURRENT ||
1371 targetprocess != _WAPI_PROCESS_CURRENT) {
1372 /* Duplicating other process's handles is not supported */
1373 SetLastError (ERROR_INVALID_HANDLE);
1377 if (src == _WAPI_PROCESS_CURRENT) {
1378 *target = _wapi_process_duplicate ();
1379 } else if (src == _WAPI_THREAD_CURRENT) {
1380 *target = _wapi_thread_duplicate ();
1382 _wapi_handle_ref (src);
1389 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1395 guint32 count, i, iter=0;
1398 WapiHandleType type;
1400 /* Lock all the handles, with backoff */
1402 thr_ret = _wapi_handle_lock_shared_handles ();
1403 g_assert (thr_ret == 0);
1405 for(i=0; i<numhandles; i++) {
1406 gpointer handle = handles[i];
1407 guint32 idx = GPOINTER_TO_UINT(handle);
1410 g_message ("%s: attempting to lock %p", __func__, handle);
1413 type = _WAPI_PRIVATE_HANDLES(idx).type;
1415 thr_ret = _wapi_handle_trylock_handle (handle);
1421 g_message ("%s: attempt failed for %p: %s", __func__,
1422 handle, strerror (thr_ret));
1425 thr_ret = _wapi_handle_unlock_shared_handles ();
1426 g_assert (thr_ret == 0);
1429 handle = handles[i];
1430 idx = GPOINTER_TO_UINT(handle);
1432 thr_ret = _wapi_handle_unlock_handle (handle);
1433 g_assert (thr_ret == 0);
1436 /* If iter ever reaches 100 the nanosleep will
1437 * return EINVAL immediately, but we have a
1438 * design flaw if that happens.
1442 g_warning ("%s: iteration overflow!",
1448 g_message ("%s: Backing off for %d ms", __func__,
1451 _wapi_handle_spin (10 * iter);
1458 g_message ("%s: Locked all handles", __func__);
1464 for(i=0; i<numhandles; i++) {
1465 gpointer handle = handles[i];
1466 guint32 idx = GPOINTER_TO_UINT(handle);
1468 type = _WAPI_PRIVATE_HANDLES(idx).type;
1471 g_message ("%s: Checking handle %p", __func__, handle);
1474 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1475 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1476 (_WAPI_SHARED_HANDLE(type) &&
1477 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1478 (!_WAPI_SHARED_HANDLE(type) &&
1479 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1483 g_message ("%s: Handle %p signalled", __func__,
1493 g_message ("%s: %d event handles signalled", __func__, count);
1496 if ((waitall == TRUE && count == numhandles) ||
1497 (waitall == FALSE && count > 0)) {
1504 g_message ("%s: Returning %d", __func__, ret);
1512 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1517 thr_ret = _wapi_handle_unlock_shared_handles ();
1518 g_assert (thr_ret == 0);
1520 for(i=0; i<numhandles; i++) {
1521 gpointer handle = handles[i];
1524 g_message ("%s: unlocking handle %p", __func__, handle);
1527 thr_ret = _wapi_handle_unlock_handle (handle);
1528 g_assert (thr_ret == 0);
1532 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout, gboolean alertable)
1534 struct timespec fake_timeout;
1539 ret=mono_cond_timedwait (cond, mutex, timeout);
1541 ret=mono_cond_wait (cond, mutex);
1543 _wapi_calc_timeout (&fake_timeout, 100);
1545 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1546 (fake_timeout.tv_sec == timeout->tv_sec &&
1547 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1548 /* Real timeout is less than 100ms time */
1549 ret=mono_cond_timedwait (cond, mutex, timeout);
1551 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1553 /* Mask the fake timeout, this will cause
1554 * another poll if the cond was not really signaled
1556 if (ret==ETIMEDOUT) {
1565 int _wapi_handle_wait_signal (gboolean poll)
1567 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
1570 int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
1572 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
1575 int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
1578 g_message ("%s: waiting for %p", __func__, handle);
1581 return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
1584 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1585 struct timespec *timeout, gboolean alertable, gboolean poll)
1588 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1589 _wapi_handle_typename[_wapi_handle_type (handle)]);
1592 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1593 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1596 if (timeout != NULL) {
1597 struct timespec fake_timeout;
1598 _wapi_calc_timeout (&fake_timeout, 100);
1600 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1601 (fake_timeout.tv_sec == timeout->tv_sec &&
1602 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1603 /* FIXME: Real timeout is less than
1604 * 100ms time, but is it really worth
1605 * calculating to the exact ms?
1607 _wapi_handle_spin (100);
1609 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1616 _wapi_handle_spin (100);
1620 guint32 idx = GPOINTER_TO_UINT(handle);
1622 pthread_cond_t *cond;
1623 mono_mutex_t *mutex;
1625 if (alertable && !wapi_thread_set_wait_handle (handle))
1628 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1629 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1632 /* This is needed when waiting for process handles */
1633 res = timedwait_signal_poll_cond (cond, mutex, timeout, alertable);
1636 res = mono_cond_timedwait (cond, mutex, timeout);
1638 res = mono_cond_wait (cond, mutex);
1642 wapi_thread_clear_wait_handle (handle);
1649 _wapi_free_share_info (_WapiFileShare *share_info)
1651 if (!_wapi_shm_enabled ()) {
1652 file_share_hash_lock ();
1653 g_hash_table_remove (file_share_hash, share_info);
1654 file_share_hash_unlock ();
1656 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1661 wapi_share_info_equal (gconstpointer ka, gconstpointer kb)
1663 const _WapiFileShare *s1 = ka;
1664 const _WapiFileShare *s2 = kb;
1666 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
1670 wapi_share_info_hash (gconstpointer data)
1672 const _WapiFileShare *s = data;
1677 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1678 guint32 new_sharemode,
1680 guint32 *old_sharemode,
1681 guint32 *old_access,
1682 struct _WapiFileShare **share_info)
1684 struct _WapiFileShare *file_share;
1685 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1686 int thr_ret, i, first_unused = -1;
1687 gboolean exists = FALSE;
1689 /* Prevents entries from expiring under us as we search
1691 thr_ret = _wapi_handle_lock_shared_handles ();
1692 g_assert (thr_ret == 0);
1694 /* Prevent new entries racing with us */
1695 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1696 g_assert (thr_ret == 0);
1698 if (!_wapi_shm_enabled ()) {
1702 * Instead of allocating a 4MB array, we use a hash table to keep track of this
1703 * info. This is needed even if SHM is disabled, to track sharing inside
1704 * the current process.
1706 if (!file_share_hash) {
1707 file_share_hash = g_hash_table_new_full (wapi_share_info_hash, wapi_share_info_equal, NULL, g_free);
1708 InitializeCriticalSection (&file_share_hash_mutex);
1711 tmp.device = device;
1714 file_share_hash_lock ();
1716 file_share = g_hash_table_lookup (file_share_hash, &tmp);
1718 *old_sharemode = file_share->sharemode;
1719 *old_access = file_share->access;
1720 *share_info = file_share;
1722 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1725 file_share = g_new0 (_WapiFileShare, 1);
1727 file_share->device = device;
1728 file_share->inode = inode;
1729 file_share->opened_by_pid = _wapi_getpid ();
1730 file_share->sharemode = new_sharemode;
1731 file_share->access = new_access;
1732 file_share->handle_refs = 1;
1733 *share_info = file_share;
1735 g_hash_table_insert (file_share_hash, file_share, file_share);
1738 file_share_hash_unlock ();
1740 /* If a linear scan gets too slow we'll have to fit a hash
1741 * table onto the shared mem backing store
1744 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1745 file_share = &_wapi_fileshare_layout->share_info[i];
1747 /* Make a note of an unused slot, in case we need to
1750 if (first_unused == -1 && file_share->handle_refs == 0) {
1755 if (file_share->handle_refs == 0) {
1759 if (file_share->device == device &&
1760 file_share->inode == inode) {
1761 *old_sharemode = file_share->sharemode;
1762 *old_access = file_share->access;
1763 *share_info = file_share;
1765 /* Increment the reference count while we
1766 * still have sole access to the shared area.
1767 * This makes the increment atomic wrt
1770 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1778 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1781 if (first_unused == -1) {
1782 file_share = &_wapi_fileshare_layout->share_info[++i];
1783 _wapi_fileshare_layout->hwm = i;
1785 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1788 file_share->device = device;
1789 file_share->inode = inode;
1790 file_share->opened_by_pid = _wapi_getpid ();
1791 file_share->sharemode = new_sharemode;
1792 file_share->access = new_access;
1793 file_share->handle_refs = 1;
1794 *share_info = file_share;
1798 if (*share_info != NULL) {
1799 InterlockedExchange ((gint32 *)&(*share_info)->timestamp, now);
1803 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1805 _wapi_handle_unlock_shared_handles ();
1810 /* If we don't have the info in /proc, check if the process that
1811 * opened this share info is still there (it's not a perfect method,
1814 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1816 if (kill (share_info->opened_by_pid, 0) == -1 &&
1819 /* It's gone completely (or there's a new process
1820 * owned by someone else) so mark this share info as
1824 g_message ("%s: Didn't find it, destroying entry", __func__);
1827 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1832 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1833 * question. If there are none, reset the share info.
1835 * This implementation is Linux-specific; legacy systems will have to
1836 * implement their own ways of finding out if a particular file is
1837 * open by a process.
1839 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1841 gboolean found = FALSE, proc_fds = FALSE;
1842 pid_t self = _wapi_getpid ();
1846 /* Prevents entries from expiring under us if we remove this
1849 thr_ret = _wapi_handle_lock_shared_handles ();
1850 g_assert (thr_ret == 0);
1852 /* Prevent new entries racing with us */
1853 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1854 g_assert (thr_ret == 0);
1856 /* If there is no /proc, there's nothing more we can do here */
1857 if (access ("/proc", F_OK) == -1) {
1858 _wapi_handle_check_share_by_pid (share_info);
1862 /* If there's another handle that thinks it owns this fd, then even
1863 * if the fd has been closed behind our back consider it still owned.
1864 * See bugs 75764 and 75891
1866 for (i = 0; i < _wapi_fd_reserve; i++) {
1867 if (_wapi_private_handles [SLOT_INDEX (i)]) {
1868 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1871 handle->type == WAPI_HANDLE_FILE) {
1872 struct _WapiHandle_file *file_handle = &handle->u.file;
1874 if (file_handle->share_info == share_info) {
1876 g_message ("%s: handle 0x%x has this file open!",
1886 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1887 struct _WapiHandleShared *shared;
1888 struct _WapiHandle_process *process_handle;
1890 shared = &_wapi_shared_layout->handles[i];
1892 if (shared->type == WAPI_HANDLE_PROCESS) {
1894 struct dirent *fd_entry;
1895 char subdir[_POSIX_PATH_MAX];
1897 process_handle = &shared->u.process;
1898 pid = process_handle->id;
1900 /* Look in /proc/<pid>/fd/ but ignore
1901 * /proc/<our pid>/fd/<fd>, as we have the
1904 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1907 fd_dir = opendir (subdir);
1908 if (fd_dir == NULL) {
1913 g_message ("%s: Looking in %s", __func__, subdir);
1918 while ((fd_entry = readdir (fd_dir)) != NULL) {
1919 char path[_POSIX_PATH_MAX];
1920 struct stat link_stat;
1922 if (!strcmp (fd_entry->d_name, ".") ||
1923 !strcmp (fd_entry->d_name, "..") ||
1925 fd == atoi (fd_entry->d_name))) {
1929 g_snprintf (path, _POSIX_PATH_MAX,
1930 "/proc/%d/fd/%s", pid,
1933 stat (path, &link_stat);
1934 if (link_stat.st_dev == share_info->device &&
1935 link_stat.st_ino == share_info->inode) {
1937 g_message ("%s: Found it at %s",
1949 if (proc_fds == FALSE) {
1950 _wapi_handle_check_share_by_pid (share_info);
1951 } else if (found == FALSE) {
1952 /* Blank out this entry, as it is stale */
1954 g_message ("%s: Didn't find it, destroying entry", __func__);
1957 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1961 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1963 _wapi_handle_unlock_shared_handles ();
1967 // Other implementations (non-Linux)
1969 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1973 /* Prevents entries from expiring under us if we remove this
1975 thr_ret = _wapi_handle_lock_shared_handles ();
1976 g_assert (thr_ret == 0);
1978 /* Prevent new entries racing with us */
1979 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1980 g_assert (thr_ret == 0);
1982 _wapi_handle_check_share_by_pid (share_info);
1984 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1985 _wapi_handle_unlock_shared_handles ();
1989 void _wapi_handle_dump (void)
1991 struct _WapiHandleUnshared *handle_data;
1995 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1996 (void *)&scan_mutex);
1997 thr_ret = mono_mutex_lock (&scan_mutex);
1998 g_assert (thr_ret == 0);
2000 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
2001 if (_wapi_private_handles [i]) {
2002 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
2003 handle_data = &_wapi_private_handles [i][k];
2005 if (handle_data->type == WAPI_HANDLE_UNUSED) {
2009 g_print ("%3x [%7s] %s %d ",
2010 i * _WAPI_HANDLE_INITIAL_COUNT + k,
2011 _wapi_handle_typename[handle_data->type],
2012 handle_data->signalled?"Sg":"Un",
2014 handle_details[handle_data->type](&handle_data->u);
2020 thr_ret = mono_mutex_unlock (&scan_mutex);
2021 g_assert (thr_ret == 0);
2022 pthread_cleanup_pop (0);
2025 static void _wapi_shared_details (gpointer handle_info)
2027 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
2029 g_print ("offset: 0x%x", shared->offset);
2032 void _wapi_handle_update_refs (void)
2036 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
2038 thr_ret = _wapi_handle_lock_shared_handles ();
2039 g_assert (thr_ret == 0);
2041 /* Prevent file share entries racing with us */
2042 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
2043 g_assert(thr_ret == 0);
2045 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
2046 (void *)&scan_mutex);
2047 thr_ret = mono_mutex_lock (&scan_mutex);
2049 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
2050 if (_wapi_private_handles [i]) {
2051 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
2052 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
2054 if (_WAPI_SHARED_HANDLE(handle->type)) {
2055 struct _WapiHandleShared *shared_data;
2058 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
2061 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
2064 g_message ("%s: (%d) Updating timestamp of handle 0x%x", __func__, _wapi_getpid (), handle->u.shared.offset);
2067 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
2068 } else if (handle->type == WAPI_HANDLE_FILE) {
2069 struct _WapiHandle_file *file_handle = &handle->u.file;
2072 g_message ("%s: (%d) handle 0x%x is FILE", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
2075 g_assert (file_handle->share_info != NULL);
2078 g_message ("%s: (%d) Inc refs on fileshare 0x%x", __func__, _wapi_getpid (), (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
2081 InterlockedExchange ((gint32 *)&file_handle->share_info->timestamp, now);
2087 thr_ret = mono_mutex_unlock (&scan_mutex);
2088 g_assert (thr_ret == 0);
2089 pthread_cleanup_pop (0);
2091 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
2093 _wapi_handle_unlock_shared_handles ();