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;
189 gpointer handle = GINT_TO_POINTER (i*_WAPI_HANDLE_INITIAL_COUNT+j);
191 if (_WAPI_SHARED_HANDLE (type)) {
192 if (type == WAPI_HANDLE_THREAD) {
193 /* Special-case thread handles
194 * because they need extra
195 * cleanup. This also avoids
196 * a race condition between
197 * the application exit and
198 * the finalizer thread - if
199 * it finishes up between now
200 * and actual app termination
201 * it will find all its handle
202 * details have been blown
203 * away, so this sets those
206 _wapi_thread_set_termination_details (handle, 0);
210 for(k = handle_data->ref; k > 0; k--) {
212 g_message ("%s: unreffing %s handle %p", __func__, _wapi_handle_typename[type], handle);
215 _wapi_handle_unref (handle);
220 _wapi_shm_semaphores_remove ();
222 _wapi_shm_detach (WAPI_SHM_DATA);
223 _wapi_shm_detach (WAPI_SHM_FILESHARE);
225 if (file_share_hash) {
226 g_hash_table_destroy (file_share_hash);
227 DeleteCriticalSection (&file_share_hash_mutex);
230 for (i = 0; i < _WAPI_PRIVATE_MAX_SLOTS; ++i)
231 g_free (_wapi_private_handles [i]);
234 void _wapi_cleanup ()
236 g_assert (_wapi_has_shut_down == FALSE);
238 _wapi_has_shut_down = TRUE;
240 _wapi_critical_section_cleanup ();
241 _wapi_error_cleanup ();
242 _wapi_thread_cleanup ();
245 static mono_once_t shared_init_once = MONO_ONCE_INIT;
246 static void shared_init (void)
248 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
249 == WAPI_HANDLE_COUNT);
251 _wapi_fd_reserve = getdtablesize();
253 /* This is needed by the code in _wapi_handle_new_internal */
254 _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
258 * The entries in _wapi_private_handles reserved for fds are allocated lazily to
262 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
263 _WAPI_HANDLE_INITIAL_COUNT);
266 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
267 _wapi_private_handle_slot_count ++;
268 } while(_wapi_fd_reserve > _wapi_private_handle_count);
270 _wapi_shm_semaphores_init ();
272 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
273 g_assert (_wapi_shared_layout != NULL);
275 if (_wapi_shm_enabled ()) {
276 /* This allocates a 4mb array, so do it only if SHM is enabled */
277 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
278 g_assert (_wapi_fileshare_layout != NULL);
281 #if !defined (DISABLE_SHARED_HANDLES)
282 if (_wapi_shm_enabled ())
283 _wapi_collection_init ();
286 /* Can't call wapi_handle_new as it calls us recursively */
287 _wapi_global_signal_handle = _wapi_handle_real_new (WAPI_HANDLE_EVENT, NULL);
289 _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
290 _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
292 /* Using g_atexit here instead of an explicit function call in
293 * a cleanup routine lets us cope when a third-party library
294 * calls exit (eg if an X client loses the connection to its
297 g_atexit (handle_cleanup);
300 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
302 gpointer handle_specific)
304 g_assert (_wapi_has_shut_down == FALSE);
307 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
308 handle->signalled = FALSE;
309 handle->handle_refs = 1;
311 if (handle_specific != NULL) {
312 memcpy (&handle->u, handle_specific, sizeof (handle->u));
316 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
317 WapiHandleType type, gpointer handle_specific)
321 g_assert (_wapi_has_shut_down == FALSE);
324 handle->signalled = FALSE;
327 if (!_WAPI_SHARED_HANDLE(type)) {
328 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
329 g_assert (thr_ret == 0);
331 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
332 g_assert (thr_ret == 0);
334 if (handle_specific != NULL) {
335 memcpy (&handle->u, handle_specific,
341 static guint32 _wapi_handle_new_shared (WapiHandleType type,
342 gpointer handle_specific)
345 static guint32 last = 1;
348 g_assert (_wapi_has_shut_down == FALSE);
350 /* Leave the first slot empty as a guard */
352 /* FIXME: expandable array */
353 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
354 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
356 if(handle->type == WAPI_HANDLE_UNUSED) {
357 thr_ret = _wapi_handle_lock_shared_handles ();
358 g_assert (thr_ret == 0);
360 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
363 _wapi_handle_init_shared (handle, type,
366 _wapi_handle_unlock_shared_handles ();
370 /* Someone else beat us to it, just
375 _wapi_handle_unlock_shared_handles ();
380 /* Try again from the beginning */
385 /* Will need to expand the array. The caller will sort it out */
391 * _wapi_handle_new_internal:
392 * @type: Init handle to this type
394 * Search for a free handle and initialize it. Return the handle on
395 * success and 0 on failure. This is only called from
396 * _wapi_handle_new, and scan_mutex must be held.
398 static guint32 _wapi_handle_new_internal (WapiHandleType type,
399 gpointer handle_specific)
402 static guint32 last = 0;
403 gboolean retry = FALSE;
405 g_assert (_wapi_has_shut_down == FALSE);
407 /* A linear scan should be fast enough. Start from the last
408 * allocation, assuming that handles are allocated more often
409 * than they're freed. Leave the space reserved for file
413 if (last < _wapi_fd_reserve) {
414 last = _wapi_fd_reserve;
421 for(i = SLOT_INDEX (count); i < _wapi_private_handle_slot_count; i++) {
422 if (_wapi_private_handles [i]) {
423 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
424 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
426 if(handle->type == WAPI_HANDLE_UNUSED) {
429 _wapi_handle_init (handle, type, handle_specific);
437 if(retry && last > _wapi_fd_reserve) {
438 /* Try again from the beginning */
439 last = _wapi_fd_reserve;
443 /* Will need to expand the array. The caller will sort it out */
448 static gpointer _wapi_handle_real_new (WapiHandleType type, gpointer handle_specific)
450 guint32 handle_idx = 0;
455 g_message ("%s: Creating new handle of type %s", __func__,
456 _wapi_handle_typename[type]);
459 g_assert(!_WAPI_FD_HANDLE(type));
461 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
462 (void *)&scan_mutex);
463 thr_ret = mono_mutex_lock (&scan_mutex);
464 g_assert (thr_ret == 0);
466 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
467 /* Try and expand the array, and have another go */
468 int idx = SLOT_INDEX (_wapi_private_handle_count);
469 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
473 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
474 _WAPI_HANDLE_INITIAL_COUNT);
476 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
477 _wapi_private_handle_slot_count ++;
480 thr_ret = mono_mutex_unlock (&scan_mutex);
481 g_assert (thr_ret == 0);
482 pthread_cleanup_pop (0);
484 if (handle_idx == 0) {
485 /* We ran out of slots */
486 handle = _WAPI_HANDLE_INVALID;
490 /* Make sure we left the space for fd mappings */
491 g_assert (handle_idx >= _wapi_fd_reserve);
493 handle = GUINT_TO_POINTER (handle_idx);
496 g_message ("%s: Allocated new handle %p", __func__, handle);
499 if (_WAPI_SHARED_HANDLE(type)) {
500 /* Add the shared section too */
503 ref = _wapi_handle_new_shared (type, handle_specific);
505 _wapi_handle_collect ();
506 _wapi_process_reap ();
507 ref = _wapi_handle_new_shared (type, handle_specific);
509 /* FIXME: grow the arrays */
510 handle = _WAPI_HANDLE_INVALID;
515 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
517 g_message ("%s: New shared handle at offset 0x%x", __func__,
526 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
528 g_assert (_wapi_has_shut_down == FALSE);
530 mono_once (&shared_init_once, shared_init);
532 return _wapi_handle_real_new (type, handle_specific);
535 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
538 guint32 handle_idx = 0;
539 gpointer handle = INVALID_HANDLE_VALUE;
541 struct _WapiHandleShared *shared;
543 g_assert (_wapi_has_shut_down == FALSE);
545 mono_once (&shared_init_once, shared_init);
548 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
549 _wapi_handle_typename[type], offset);
552 g_assert(!_WAPI_FD_HANDLE(type));
553 g_assert(_WAPI_SHARED_HANDLE(type));
554 g_assert(offset != 0);
556 shared = &_wapi_shared_layout->handles[offset];
558 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
559 /* Bump up the timestamp for this offset */
560 InterlockedExchange ((gint32 *)&shared->timestamp, now);
563 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
564 (void *)&scan_mutex);
565 thr_ret = mono_mutex_lock (&scan_mutex);
566 g_assert (thr_ret == 0);
568 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
569 if (_wapi_private_handles [i]) {
570 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
571 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
573 if (handle_data->type == type &&
574 handle_data->u.shared.offset == offset) {
575 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
576 goto first_pass_done;
583 thr_ret = mono_mutex_unlock (&scan_mutex);
584 g_assert (thr_ret == 0);
585 pthread_cleanup_pop (0);
587 if (handle != INVALID_HANDLE_VALUE) {
588 _wapi_handle_ref (handle);
591 g_message ("%s: Returning old handle %p referencing 0x%x",
592 __func__, handle, offset);
597 /* Prevent entries expiring under us as we search */
598 thr_ret = _wapi_handle_lock_shared_handles ();
599 g_assert (thr_ret == 0);
601 if (shared->type == WAPI_HANDLE_UNUSED) {
602 /* Someone deleted this handle while we were working */
604 g_message ("%s: Handle at 0x%x unused", __func__, offset);
609 if (shared->type != type) {
611 g_message ("%s: Wrong type at %d 0x%x! Found %s wanted %s",
612 __func__, offset, offset,
613 _wapi_handle_typename[shared->type],
614 _wapi_handle_typename[type]);
619 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
620 (void *)&scan_mutex);
621 thr_ret = mono_mutex_lock (&scan_mutex);
622 g_assert (thr_ret == 0);
624 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
625 /* Try and expand the array, and have another go */
626 int idx = SLOT_INDEX (_wapi_private_handle_count);
627 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
628 _WAPI_HANDLE_INITIAL_COUNT);
630 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
631 _wapi_private_handle_slot_count ++;
634 thr_ret = mono_mutex_unlock (&scan_mutex);
635 g_assert (thr_ret == 0);
636 pthread_cleanup_pop (0);
638 /* Make sure we left the space for fd mappings */
639 g_assert (handle_idx >= _wapi_fd_reserve);
641 handle = GUINT_TO_POINTER (handle_idx);
643 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
644 InterlockedIncrement ((gint32 *)&shared->handle_refs);
647 g_message ("%s: Allocated new handle %p referencing 0x%x (shared refs %d)", __func__, handle, offset, shared->handle_refs);
651 _wapi_handle_unlock_shared_handles ();
657 init_handles_slot (int idx)
661 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
662 (void *)&scan_mutex);
663 thr_ret = mono_mutex_lock (&scan_mutex);
664 g_assert (thr_ret == 0);
666 if (_wapi_private_handles [idx] == NULL) {
667 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
668 _WAPI_HANDLE_INITIAL_COUNT);
669 g_assert (_wapi_private_handles [idx]);
672 thr_ret = mono_mutex_unlock (&scan_mutex);
673 g_assert (thr_ret == 0);
674 pthread_cleanup_pop (0);
677 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
678 gpointer handle_specific)
680 struct _WapiHandleUnshared *handle;
683 g_assert (_wapi_has_shut_down == FALSE);
685 mono_once (&shared_init_once, shared_init);
688 g_message ("%s: Creating new handle of type %s", __func__,
689 _wapi_handle_typename[type]);
692 g_assert(_WAPI_FD_HANDLE(type));
693 g_assert(!_WAPI_SHARED_HANDLE(type));
695 if (fd >= _wapi_fd_reserve) {
697 g_message ("%s: fd %d is too big", __func__, fd);
700 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
703 /* Initialize the array entries on demand */
704 if (_wapi_private_handles [SLOT_INDEX (fd)] == NULL)
705 init_handles_slot (SLOT_INDEX (fd));
707 handle = &_WAPI_PRIVATE_HANDLES(fd);
709 if (handle->type != WAPI_HANDLE_UNUSED) {
711 g_message ("%s: fd %d is already in use!", __func__, fd);
713 /* FIXME: clean up this handle? We can't do anything
714 * with the fd, cos thats the new one
719 g_message ("%s: Assigning new fd handle %d", __func__, fd);
722 /* Prevent file share entries racing with us, when the file
723 * handle is only half initialised
725 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
726 g_assert(thr_ret == 0);
728 _wapi_handle_init (handle, type, handle_specific);
730 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
732 return(GUINT_TO_POINTER(fd));
735 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
736 gpointer *handle_specific)
738 struct _WapiHandleUnshared *handle_data;
739 guint32 handle_idx = GPOINTER_TO_UINT(handle);
741 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
745 /* Initialize the array entries on demand */
746 if (_wapi_private_handles [SLOT_INDEX (handle_idx)] == NULL)
747 init_handles_slot (SLOT_INDEX (handle_idx));
749 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
751 if (handle_data->type != type) {
755 if (handle_specific == NULL) {
759 if (_WAPI_SHARED_HANDLE(type)) {
760 struct _WapiHandle_shared_ref *ref;
761 struct _WapiHandleShared *shared_handle_data;
763 ref = &handle_data->u.shared;
764 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
766 if (shared_handle_data->type != type) {
767 /* The handle must have been deleted on us
772 *handle_specific = &shared_handle_data->u;
774 *handle_specific = &handle_data->u;
781 _wapi_handle_foreach (WapiHandleType type,
782 gboolean (*on_each)(gpointer test, gpointer user),
785 struct _WapiHandleUnshared *handle_data = NULL;
790 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
791 (void *)&scan_mutex);
792 thr_ret = mono_mutex_lock (&scan_mutex);
793 g_assert (thr_ret == 0);
795 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
796 if (_wapi_private_handles [i]) {
797 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
798 handle_data = &_wapi_private_handles [i][k];
800 if (handle_data->type == type) {
801 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
802 if (on_each (ret, user_data) == TRUE)
809 thr_ret = mono_mutex_unlock (&scan_mutex);
810 g_assert (thr_ret == 0);
811 pthread_cleanup_pop (0);
814 /* This might list some shared handles twice if they are already
815 * opened by this process, and the check function returns FALSE the
816 * first time. Shared handles that are created during the search are
817 * unreffed if the check function returns FALSE, so callers must not
818 * rely on the handle persisting (unless the check function returns
821 gpointer _wapi_search_handle (WapiHandleType type,
822 gboolean (*check)(gpointer test, gpointer user),
824 gpointer *handle_specific,
825 gboolean search_shared)
827 struct _WapiHandleUnshared *handle_data = NULL;
828 struct _WapiHandleShared *shared = NULL;
831 gboolean found = FALSE;
834 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
835 (void *)&scan_mutex);
836 thr_ret = mono_mutex_lock (&scan_mutex);
837 g_assert (thr_ret == 0);
839 for (i = SLOT_INDEX (0); !found && i < _wapi_private_handle_slot_count; i++) {
840 if (_wapi_private_handles [i]) {
841 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
842 handle_data = &_wapi_private_handles [i][k];
844 if (handle_data->type == type) {
845 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
846 if (check (ret, user_data) == TRUE) {
847 _wapi_handle_ref (ret);
850 if (_WAPI_SHARED_HANDLE (type)) {
851 shared = &_wapi_shared_layout->handles[i];
861 thr_ret = mono_mutex_unlock (&scan_mutex);
862 g_assert (thr_ret == 0);
863 pthread_cleanup_pop (0);
865 if (!found && search_shared && _WAPI_SHARED_HANDLE (type)) {
866 /* Not found yet, so search the shared memory too */
868 g_message ("%s: Looking at other shared handles...", __func__);
871 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
872 shared = &_wapi_shared_layout->handles[i];
874 if (shared->type == type) {
875 /* Tell new_from_offset to not
876 * timestamp this handle, because
877 * otherwise it will ping every handle
878 * in the list and they will never
881 ret = _wapi_handle_new_from_offset (type, i,
883 if (ret == INVALID_HANDLE_VALUE) {
884 /* This handle was deleted
885 * while we were looking at it
891 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
894 /* It's possible that the shared part
895 * of this handle has now been blown
896 * away (after new_from_offset
897 * successfully opened it,) if its
898 * timestamp is too old. The check
899 * function needs to be aware of this,
900 * and cope if the handle has
903 if (check (ret, user_data) == TRUE) {
904 /* Timestamp this handle, but make
905 * sure it still exists first
907 thr_ret = _wapi_handle_lock_shared_handles ();
908 g_assert (thr_ret == 0);
910 if (shared->type == type) {
911 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
912 InterlockedExchange ((gint32 *)&shared->timestamp, now);
915 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
917 _wapi_handle_unlock_shared_handles ();
920 /* It's been deleted,
924 _wapi_handle_unlock_shared_handles ();
928 /* This isn't the handle we're looking
929 * for, so drop the reference we took
930 * in _wapi_handle_new_from_offset ()
932 _wapi_handle_unref (ret);
942 if(handle_specific != NULL) {
943 if (_WAPI_SHARED_HANDLE(type)) {
944 g_assert(shared->type == type);
946 *handle_specific = &shared->u;
948 *handle_specific = &handle_data->u;
956 /* Returns the offset of the metadata array, or -1 on error, or 0 for
957 * not found (0 is not a valid offset)
959 gint32 _wapi_search_handle_namespace (WapiHandleType type,
962 struct _WapiHandleShared *shared_handle_data;
967 g_assert(_WAPI_SHARED_HANDLE(type));
970 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
971 utf8_name, _wapi_handle_typename[type]);
974 /* Do a handle collection before starting to look, so that any
975 * stale cruft gets removed
977 _wapi_handle_collect ();
979 thr_ret = _wapi_handle_lock_shared_handles ();
980 g_assert (thr_ret == 0);
982 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
983 WapiSharedNamespace *sharedns;
985 shared_handle_data = &_wapi_shared_layout->handles[i];
987 /* Check mutex, event, semaphore, timer, job and
988 * file-mapping object names. So far only mutex,
989 * semaphore and event are implemented.
991 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
996 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
999 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
1002 g_message ("%s: name is [%s]", __func__, sharedns->name);
1005 if (strcmp (sharedns->name, utf8_name) == 0) {
1006 if (shared_handle_data->type != type) {
1007 /* Its the wrong type, so fail now */
1009 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
1015 g_message ("%s: handle 0x%x matches name and type", __func__, i);
1024 _wapi_handle_unlock_shared_handles ();
1029 void _wapi_handle_ref (gpointer handle)
1031 guint32 idx = GPOINTER_TO_UINT(handle);
1032 struct _WapiHandleUnshared *handle_data;
1034 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1038 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1039 g_warning ("%s: Attempting to ref unused handle %p", __func__,
1044 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
1046 InterlockedIncrement ((gint32 *)&handle_data->ref);
1048 /* It's possible for processes to exit before getting around
1049 * to updating timestamps in the collection thread, so if a
1050 * shared handle is reffed do the timestamp here as well just
1053 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
1054 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
1055 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1056 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
1060 g_message ("%s: %s handle %p ref now %d", __func__,
1061 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1063 _WAPI_PRIVATE_HANDLES(idx).ref);
1067 /* The handle must not be locked on entry to this function */
1068 void _wapi_handle_unref (gpointer handle)
1070 guint32 idx = GPOINTER_TO_UINT(handle);
1071 gboolean destroy = FALSE;
1074 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1078 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1079 g_warning ("%s: Attempting to unref unused handle %p",
1084 /* Possible race condition here if another thread refs the
1085 * handle between here and setting the type to UNUSED. I
1086 * could lock a mutex, but I'm not sure that allowing a handle
1087 * reference to reach 0 isn't an application bug anyway.
1089 destroy = (InterlockedDecrement ((gint32 *)&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
1092 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
1093 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1095 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
1099 /* Need to copy the handle info, reset the slot in the
1100 * array, and _only then_ call the close function to
1101 * avoid race conditions (eg file descriptors being
1102 * closed, and another file being opened getting the
1103 * same fd racing the memset())
1105 struct _WapiHandleUnshared handle_data;
1106 struct _WapiHandleShared shared_handle_data;
1107 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
1108 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
1109 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
1112 /* If this is a shared handle we need to take
1113 * the shared lock outside of the scan_mutex
1114 * lock to avoid deadlocks
1116 thr_ret = _wapi_handle_lock_shared_handles ();
1117 g_assert (thr_ret == 0);
1120 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
1121 thr_ret = mono_mutex_lock (&scan_mutex);
1124 g_message ("%s: Destroying handle %p", __func__, handle);
1127 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
1128 sizeof (struct _WapiHandleUnshared));
1130 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
1131 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
1133 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
1136 /* Destroy the mutex and cond var. We hope nobody
1137 * tried to grab them between the handle unlock and
1138 * now, but pthreads doesn't have a
1139 * "unlock_and_destroy" atomic function.
1141 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
1142 g_assert (thr_ret == 0);
1144 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
1145 g_assert (thr_ret == 0);
1147 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
1149 memcpy (&shared_handle_data, shared,
1150 sizeof (struct _WapiHandleShared));
1152 /* It's possible that this handle is already
1153 * pointing at a deleted shared section
1156 g_message ("%s: %s handle %p shared refs before dec %d", __func__, _wapi_handle_typename[type], handle, shared->handle_refs);
1159 if (shared->handle_refs > 0) {
1160 shared->handle_refs--;
1161 if (shared->handle_refs == 0) {
1162 memset (shared, '\0', sizeof (struct _WapiHandleShared));
1167 thr_ret = mono_mutex_unlock (&scan_mutex);
1168 g_assert (thr_ret == 0);
1169 pthread_cleanup_pop (0);
1172 _wapi_handle_unlock_shared_handles ();
1175 if (close_func != NULL) {
1177 close_func (handle, &shared_handle_data.u);
1179 close_func (handle, &handle_data.u);
1185 void _wapi_handle_register_capabilities (WapiHandleType type,
1186 WapiHandleCapability caps)
1188 handle_caps[type] = caps;
1191 gboolean _wapi_handle_test_capabilities (gpointer handle,
1192 WapiHandleCapability caps)
1194 guint32 idx = GPOINTER_TO_UINT(handle);
1195 WapiHandleType type;
1197 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1201 type = _WAPI_PRIVATE_HANDLES(idx).type;
1204 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
1205 handle_caps[type], caps, handle_caps[type] & caps);
1208 return((handle_caps[type] & caps) != 0);
1211 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
1213 if (handle_ops[type] != NULL &&
1214 handle_ops[type]->close != NULL) {
1215 return (handle_ops[type]->close);
1221 void _wapi_handle_ops_close (gpointer handle, gpointer data)
1223 guint32 idx = GPOINTER_TO_UINT(handle);
1224 WapiHandleType type;
1226 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1230 type = _WAPI_PRIVATE_HANDLES(idx).type;
1232 if (handle_ops[type] != NULL &&
1233 handle_ops[type]->close != NULL) {
1234 handle_ops[type]->close (handle, data);
1238 void _wapi_handle_ops_signal (gpointer handle)
1240 guint32 idx = GPOINTER_TO_UINT(handle);
1241 WapiHandleType type;
1243 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1247 type = _WAPI_PRIVATE_HANDLES(idx).type;
1249 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1250 handle_ops[type]->signal (handle);
1254 gboolean _wapi_handle_ops_own (gpointer handle)
1256 guint32 idx = GPOINTER_TO_UINT(handle);
1257 WapiHandleType type;
1259 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1263 type = _WAPI_PRIVATE_HANDLES(idx).type;
1265 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1266 return(handle_ops[type]->own_handle (handle));
1272 gboolean _wapi_handle_ops_isowned (gpointer handle)
1274 guint32 idx = GPOINTER_TO_UINT(handle);
1275 WapiHandleType type;
1277 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1281 type = _WAPI_PRIVATE_HANDLES(idx).type;
1283 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1284 return(handle_ops[type]->is_owned (handle));
1290 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1292 guint32 idx = GPOINTER_TO_UINT(handle);
1293 WapiHandleType type;
1295 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1296 return(WAIT_FAILED);
1299 type = _WAPI_PRIVATE_HANDLES(idx).type;
1301 if (handle_ops[type] != NULL &&
1302 handle_ops[type]->special_wait != NULL) {
1303 return(handle_ops[type]->special_wait (handle, timeout));
1305 return(WAIT_FAILED);
1309 void _wapi_handle_ops_prewait (gpointer handle)
1311 guint32 idx = GPOINTER_TO_UINT (handle);
1312 WapiHandleType type;
1314 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1318 type = _WAPI_PRIVATE_HANDLES (idx).type;
1320 if (handle_ops[type] != NULL &&
1321 handle_ops[type]->prewait != NULL) {
1322 handle_ops[type]->prewait (handle);
1329 * @handle: The handle to release
1331 * Closes and invalidates @handle, releasing any resources it
1332 * consumes. When the last handle to a temporary or non-persistent
1333 * object is closed, that object can be deleted. Closing the same
1334 * handle twice is an error.
1336 * Return value: %TRUE on success, %FALSE otherwise.
1338 gboolean CloseHandle(gpointer handle)
1340 if (handle == NULL) {
1341 /* Problem: because we map file descriptors to the
1342 * same-numbered handle we can't tell the difference
1343 * between a bogus handle and the handle to stdin.
1344 * Assume that it's the console handle if that handle
1347 if (_WAPI_PRIVATE_HANDLES (0).type != WAPI_HANDLE_CONSOLE) {
1348 SetLastError (ERROR_INVALID_PARAMETER);
1352 if (handle == _WAPI_HANDLE_INVALID){
1353 SetLastError (ERROR_INVALID_PARAMETER);
1357 _wapi_handle_unref (handle);
1362 /* Lots more to implement here, but this is all we need at the moment */
1363 gboolean DuplicateHandle (gpointer srcprocess, gpointer src,
1364 gpointer targetprocess, gpointer *target,
1365 guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 options G_GNUC_UNUSED)
1367 if (srcprocess != _WAPI_PROCESS_CURRENT ||
1368 targetprocess != _WAPI_PROCESS_CURRENT) {
1369 /* Duplicating other process's handles is not supported */
1370 SetLastError (ERROR_INVALID_HANDLE);
1374 if (src == _WAPI_PROCESS_CURRENT) {
1375 *target = _wapi_process_duplicate ();
1376 } else if (src == _WAPI_THREAD_CURRENT) {
1377 *target = _wapi_thread_duplicate ();
1379 _wapi_handle_ref (src);
1386 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1392 guint32 count, i, iter=0;
1395 WapiHandleType type;
1397 /* Lock all the handles, with backoff */
1399 thr_ret = _wapi_handle_lock_shared_handles ();
1400 g_assert (thr_ret == 0);
1402 for(i=0; i<numhandles; i++) {
1403 gpointer handle = handles[i];
1404 guint32 idx = GPOINTER_TO_UINT(handle);
1407 g_message ("%s: attempting to lock %p", __func__, handle);
1410 type = _WAPI_PRIVATE_HANDLES(idx).type;
1412 thr_ret = _wapi_handle_trylock_handle (handle);
1418 g_message ("%s: attempt failed for %p: %s", __func__,
1419 handle, strerror (thr_ret));
1422 thr_ret = _wapi_handle_unlock_shared_handles ();
1423 g_assert (thr_ret == 0);
1426 handle = handles[i];
1427 idx = GPOINTER_TO_UINT(handle);
1429 thr_ret = _wapi_handle_unlock_handle (handle);
1430 g_assert (thr_ret == 0);
1433 /* If iter ever reaches 100 the nanosleep will
1434 * return EINVAL immediately, but we have a
1435 * design flaw if that happens.
1439 g_warning ("%s: iteration overflow!",
1445 g_message ("%s: Backing off for %d ms", __func__,
1448 _wapi_handle_spin (10 * iter);
1455 g_message ("%s: Locked all handles", __func__);
1461 for(i=0; i<numhandles; i++) {
1462 gpointer handle = handles[i];
1463 guint32 idx = GPOINTER_TO_UINT(handle);
1465 type = _WAPI_PRIVATE_HANDLES(idx).type;
1468 g_message ("%s: Checking handle %p", __func__, handle);
1471 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1472 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1473 (_WAPI_SHARED_HANDLE(type) &&
1474 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1475 (!_WAPI_SHARED_HANDLE(type) &&
1476 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1480 g_message ("%s: Handle %p signalled", __func__,
1490 g_message ("%s: %d event handles signalled", __func__, count);
1493 if ((waitall == TRUE && count == numhandles) ||
1494 (waitall == FALSE && count > 0)) {
1501 g_message ("%s: Returning %d", __func__, ret);
1509 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1514 thr_ret = _wapi_handle_unlock_shared_handles ();
1515 g_assert (thr_ret == 0);
1517 for(i=0; i<numhandles; i++) {
1518 gpointer handle = handles[i];
1521 g_message ("%s: unlocking handle %p", __func__, handle);
1524 thr_ret = _wapi_handle_unlock_handle (handle);
1525 g_assert (thr_ret == 0);
1529 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout, gboolean alertable)
1531 struct timespec fake_timeout;
1536 ret=mono_cond_timedwait (cond, mutex, timeout);
1538 ret=mono_cond_wait (cond, mutex);
1540 _wapi_calc_timeout (&fake_timeout, 100);
1542 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1543 (fake_timeout.tv_sec == timeout->tv_sec &&
1544 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1545 /* Real timeout is less than 100ms time */
1546 ret=mono_cond_timedwait (cond, mutex, timeout);
1548 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1550 /* Mask the fake timeout, this will cause
1551 * another poll if the cond was not really signaled
1553 if (ret==ETIMEDOUT) {
1562 int _wapi_handle_wait_signal (gboolean poll)
1564 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
1567 int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
1569 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
1572 int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
1575 g_message ("%s: waiting for %p", __func__, handle);
1578 return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
1581 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1582 struct timespec *timeout, gboolean alertable, gboolean poll)
1585 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1586 _wapi_handle_typename[_wapi_handle_type (handle)]);
1589 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1590 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1593 if (timeout != NULL) {
1594 struct timespec fake_timeout;
1595 _wapi_calc_timeout (&fake_timeout, 100);
1597 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1598 (fake_timeout.tv_sec == timeout->tv_sec &&
1599 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1600 /* FIXME: Real timeout is less than
1601 * 100ms time, but is it really worth
1602 * calculating to the exact ms?
1604 _wapi_handle_spin (100);
1606 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1613 _wapi_handle_spin (100);
1617 guint32 idx = GPOINTER_TO_UINT(handle);
1619 pthread_cond_t *cond;
1620 mono_mutex_t *mutex;
1622 if (alertable && !wapi_thread_set_wait_handle (handle))
1625 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1626 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1629 /* This is needed when waiting for process handles */
1630 res = timedwait_signal_poll_cond (cond, mutex, timeout, alertable);
1633 res = mono_cond_timedwait (cond, mutex, timeout);
1635 res = mono_cond_wait (cond, mutex);
1639 wapi_thread_clear_wait_handle (handle);
1646 _wapi_free_share_info (_WapiFileShare *share_info)
1648 if (!_wapi_shm_enabled ()) {
1649 file_share_hash_lock ();
1650 g_hash_table_remove (file_share_hash, share_info);
1651 file_share_hash_unlock ();
1653 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1658 wapi_share_info_equal (gconstpointer ka, gconstpointer kb)
1660 const _WapiFileShare *s1 = ka;
1661 const _WapiFileShare *s2 = kb;
1663 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
1667 wapi_share_info_hash (gconstpointer data)
1669 const _WapiFileShare *s = data;
1674 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1675 guint32 new_sharemode,
1677 guint32 *old_sharemode,
1678 guint32 *old_access,
1679 struct _WapiFileShare **share_info)
1681 struct _WapiFileShare *file_share;
1682 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1683 int thr_ret, i, first_unused = -1;
1684 gboolean exists = FALSE;
1686 /* Prevents entries from expiring under us as we search
1688 thr_ret = _wapi_handle_lock_shared_handles ();
1689 g_assert (thr_ret == 0);
1691 /* Prevent new entries racing with us */
1692 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1693 g_assert (thr_ret == 0);
1695 if (!_wapi_shm_enabled ()) {
1699 * Instead of allocating a 4MB array, we use a hash table to keep track of this
1700 * info. This is needed even if SHM is disabled, to track sharing inside
1701 * the current process.
1703 if (!file_share_hash) {
1704 file_share_hash = g_hash_table_new_full (wapi_share_info_hash, wapi_share_info_equal, NULL, g_free);
1705 InitializeCriticalSection (&file_share_hash_mutex);
1708 tmp.device = device;
1711 file_share_hash_lock ();
1713 file_share = g_hash_table_lookup (file_share_hash, &tmp);
1715 *old_sharemode = file_share->sharemode;
1716 *old_access = file_share->access;
1717 *share_info = file_share;
1719 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1722 file_share = g_new0 (_WapiFileShare, 1);
1724 file_share->device = device;
1725 file_share->inode = inode;
1726 file_share->opened_by_pid = _wapi_getpid ();
1727 file_share->sharemode = new_sharemode;
1728 file_share->access = new_access;
1729 file_share->handle_refs = 1;
1730 *share_info = file_share;
1732 g_hash_table_insert (file_share_hash, file_share, file_share);
1735 file_share_hash_unlock ();
1737 /* If a linear scan gets too slow we'll have to fit a hash
1738 * table onto the shared mem backing store
1741 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1742 file_share = &_wapi_fileshare_layout->share_info[i];
1744 /* Make a note of an unused slot, in case we need to
1747 if (first_unused == -1 && file_share->handle_refs == 0) {
1752 if (file_share->handle_refs == 0) {
1756 if (file_share->device == device &&
1757 file_share->inode == inode) {
1758 *old_sharemode = file_share->sharemode;
1759 *old_access = file_share->access;
1760 *share_info = file_share;
1762 /* Increment the reference count while we
1763 * still have sole access to the shared area.
1764 * This makes the increment atomic wrt
1767 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1775 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1778 if (first_unused == -1) {
1779 file_share = &_wapi_fileshare_layout->share_info[++i];
1780 _wapi_fileshare_layout->hwm = i;
1782 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1785 file_share->device = device;
1786 file_share->inode = inode;
1787 file_share->opened_by_pid = _wapi_getpid ();
1788 file_share->sharemode = new_sharemode;
1789 file_share->access = new_access;
1790 file_share->handle_refs = 1;
1791 *share_info = file_share;
1795 if (*share_info != NULL) {
1796 InterlockedExchange ((gint32 *)&(*share_info)->timestamp, now);
1800 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1802 _wapi_handle_unlock_shared_handles ();
1807 /* If we don't have the info in /proc, check if the process that
1808 * opened this share info is still there (it's not a perfect method,
1811 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1813 if (kill (share_info->opened_by_pid, 0) == -1 &&
1816 /* It's gone completely (or there's a new process
1817 * owned by someone else) so mark this share info as
1821 g_message ("%s: Didn't find it, destroying entry", __func__);
1824 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1829 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1830 * question. If there are none, reset the share info.
1832 * This implementation is Linux-specific; legacy systems will have to
1833 * implement their own ways of finding out if a particular file is
1834 * open by a process.
1836 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1838 gboolean found = FALSE, proc_fds = FALSE;
1839 pid_t self = _wapi_getpid ();
1843 /* Prevents entries from expiring under us if we remove this
1846 thr_ret = _wapi_handle_lock_shared_handles ();
1847 g_assert (thr_ret == 0);
1849 /* Prevent new entries racing with us */
1850 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1851 g_assert (thr_ret == 0);
1853 /* If there is no /proc, there's nothing more we can do here */
1854 if (access ("/proc", F_OK) == -1) {
1855 _wapi_handle_check_share_by_pid (share_info);
1859 /* If there's another handle that thinks it owns this fd, then even
1860 * if the fd has been closed behind our back consider it still owned.
1861 * See bugs 75764 and 75891
1863 for (i = 0; i < _wapi_fd_reserve; i++) {
1864 if (_wapi_private_handles [SLOT_INDEX (i)]) {
1865 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1868 handle->type == WAPI_HANDLE_FILE) {
1869 struct _WapiHandle_file *file_handle = &handle->u.file;
1871 if (file_handle->share_info == share_info) {
1873 g_message ("%s: handle 0x%x has this file open!",
1883 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1884 struct _WapiHandleShared *shared;
1885 struct _WapiHandle_process *process_handle;
1887 shared = &_wapi_shared_layout->handles[i];
1889 if (shared->type == WAPI_HANDLE_PROCESS) {
1891 struct dirent *fd_entry;
1892 char subdir[_POSIX_PATH_MAX];
1894 process_handle = &shared->u.process;
1895 pid = process_handle->id;
1897 /* Look in /proc/<pid>/fd/ but ignore
1898 * /proc/<our pid>/fd/<fd>, as we have the
1901 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1904 fd_dir = opendir (subdir);
1905 if (fd_dir == NULL) {
1910 g_message ("%s: Looking in %s", __func__, subdir);
1915 while ((fd_entry = readdir (fd_dir)) != NULL) {
1916 char path[_POSIX_PATH_MAX];
1917 struct stat link_stat;
1919 if (!strcmp (fd_entry->d_name, ".") ||
1920 !strcmp (fd_entry->d_name, "..") ||
1922 fd == atoi (fd_entry->d_name))) {
1926 g_snprintf (path, _POSIX_PATH_MAX,
1927 "/proc/%d/fd/%s", pid,
1930 stat (path, &link_stat);
1931 if (link_stat.st_dev == share_info->device &&
1932 link_stat.st_ino == share_info->inode) {
1934 g_message ("%s: Found it at %s",
1946 if (proc_fds == FALSE) {
1947 _wapi_handle_check_share_by_pid (share_info);
1948 } else if (found == FALSE) {
1949 /* Blank out this entry, as it is stale */
1951 g_message ("%s: Didn't find it, destroying entry", __func__);
1954 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1958 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1960 _wapi_handle_unlock_shared_handles ();
1964 // Other implementations (non-Linux)
1966 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1970 /* Prevents entries from expiring under us if we remove this
1972 thr_ret = _wapi_handle_lock_shared_handles ();
1973 g_assert (thr_ret == 0);
1975 /* Prevent new entries racing with us */
1976 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1977 g_assert (thr_ret == 0);
1979 _wapi_handle_check_share_by_pid (share_info);
1981 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1982 _wapi_handle_unlock_shared_handles ();
1986 void _wapi_handle_dump (void)
1988 struct _WapiHandleUnshared *handle_data;
1992 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1993 (void *)&scan_mutex);
1994 thr_ret = mono_mutex_lock (&scan_mutex);
1995 g_assert (thr_ret == 0);
1997 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1998 if (_wapi_private_handles [i]) {
1999 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
2000 handle_data = &_wapi_private_handles [i][k];
2002 if (handle_data->type == WAPI_HANDLE_UNUSED) {
2006 g_print ("%3x [%7s] %s %d ",
2007 i * _WAPI_HANDLE_INITIAL_COUNT + k,
2008 _wapi_handle_typename[handle_data->type],
2009 handle_data->signalled?"Sg":"Un",
2011 handle_details[handle_data->type](&handle_data->u);
2017 thr_ret = mono_mutex_unlock (&scan_mutex);
2018 g_assert (thr_ret == 0);
2019 pthread_cleanup_pop (0);
2022 static void _wapi_shared_details (gpointer handle_info)
2024 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
2026 g_print ("offset: 0x%x", shared->offset);
2029 void _wapi_handle_update_refs (void)
2033 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
2035 thr_ret = _wapi_handle_lock_shared_handles ();
2036 g_assert (thr_ret == 0);
2038 /* Prevent file share entries racing with us */
2039 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
2040 g_assert(thr_ret == 0);
2042 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
2043 (void *)&scan_mutex);
2044 thr_ret = mono_mutex_lock (&scan_mutex);
2046 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
2047 if (_wapi_private_handles [i]) {
2048 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
2049 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
2051 if (_WAPI_SHARED_HANDLE(handle->type)) {
2052 struct _WapiHandleShared *shared_data;
2055 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
2058 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
2061 g_message ("%s: (%d) Updating timestamp of handle 0x%x", __func__, _wapi_getpid (), handle->u.shared.offset);
2064 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
2065 } else if (handle->type == WAPI_HANDLE_FILE) {
2066 struct _WapiHandle_file *file_handle = &handle->u.file;
2069 g_message ("%s: (%d) handle 0x%x is FILE", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
2072 g_assert (file_handle->share_info != NULL);
2075 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));
2078 InterlockedExchange ((gint32 *)&file_handle->share_info->timestamp, now);
2084 thr_ret = mono_mutex_unlock (&scan_mutex);
2085 g_assert (thr_ret == 0);
2086 pthread_cleanup_pop (0);
2088 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
2090 _wapi_handle_unlock_shared_handles ();