2 * handles.c: Generic and internal operations on handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
16 #include <sys/types.h>
17 #include <sys/socket.h>
23 #include <mono/os/gc_wrapper.h>
25 #include <mono/io-layer/wapi.h>
26 #include <mono/io-layer/wapi-private.h>
27 #include <mono/io-layer/handles-private.h>
28 #include <mono/io-layer/mono-mutex.h>
29 #include <mono/io-layer/misc-private.h>
30 #include <mono/io-layer/shared.h>
31 #include <mono/io-layer/collection.h>
36 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
38 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
39 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
51 &_wapi_namedmutex_ops,
54 static void _wapi_shared_details (gpointer handle_info);
56 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
59 _wapi_console_details,
60 _wapi_shared_details, /* thread */
64 NULL, /* Nothing useful to see in a socket handle */
65 NULL, /* Nothing useful to see in a find handle */
66 _wapi_shared_details, /* process */
68 _wapi_shared_details, /* namedmutex */
71 const char *_wapi_handle_typename[] = {
88 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
89 * If 4M handles are not enough... Oh, well... we will crash.
91 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
92 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
94 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
95 static guint32 _wapi_private_handle_count = 0;
97 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
98 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
100 guint32 _wapi_fd_reserve;
102 mono_mutex_t _wapi_global_signal_mutex;
103 pthread_cond_t _wapi_global_signal_cond;
107 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
109 static mono_once_t shared_init_once = MONO_ONCE_INIT;
110 static void shared_init (void)
115 _wapi_fd_reserve = getdtablesize();
118 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
119 _WAPI_HANDLE_INITIAL_COUNT);
121 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
122 } while(_wapi_fd_reserve > _wapi_private_handle_count);
124 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
125 g_assert (_wapi_shared_layout != NULL);
127 _wapi_shm_semaphores_init ();
129 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
130 g_assert (_wapi_fileshare_layout != NULL);
132 _wapi_collection_init ();
134 thr_ret = pthread_cond_init(&_wapi_global_signal_cond, NULL);
135 g_assert (thr_ret == 0);
137 thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL);
138 g_assert (thr_ret == 0);
141 static void _wapi_handle_init_shared_metadata (struct _WapiHandleSharedMetadata *meta)
143 meta->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
144 meta->signalled = FALSE;
147 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
149 gpointer handle_specific)
152 handle->stale = FALSE;
154 if (handle_specific != NULL) {
155 memcpy (&handle->u, handle_specific, sizeof (handle->u));
159 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
160 WapiHandleType type, gpointer handle_specific)
165 handle->signalled = FALSE;
168 if (!_WAPI_SHARED_HANDLE(type)) {
169 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
170 g_assert (thr_ret == 0);
172 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
173 g_assert (thr_ret == 0);
175 if (handle_specific != NULL) {
176 memcpy (&handle->u, handle_specific,
182 static guint32 _wapi_handle_new_shared_offset (guint32 offset)
185 static guint32 last = 1;
188 /* FIXME: expandable array */
189 /* leave a few slots at the end so that there's always space
190 * to move a handle. (We leave the space in the offset table
191 * too, so we don't have to keep track of inter-segment
194 for(i = last; i <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM; i++) {
195 struct _WapiHandleSharedMetadata *meta = &_wapi_shared_layout->metadata[i];
197 if(meta->offset == 0) {
198 if (InterlockedCompareExchange (&meta->offset, offset,
202 _wapi_handle_init_shared_metadata (meta);
205 /* Someone else beat us to it, just
213 /* Try again from the beginning */
218 /* Will need to expand the array. The caller will sort it out */
223 static guint32 _wapi_handle_new_shared (WapiHandleType type,
224 gpointer handle_specific)
227 static guint32 last = 1;
229 /* The shared memory holds an offset to the real data, so we
230 * can update the handle RCU-style without taking a lock.
231 * This function just allocates the next available data slot,
232 * use _wapi_handle_new_shared_offset to get the offset entry.
235 /* Leave the first slot empty as a guard */
237 /* FIXME: expandable array */
238 /* Leave a few slots at the end so that there's always space
241 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM;
243 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
245 if(handle->type == WAPI_HANDLE_UNUSED) {
246 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
249 _wapi_handle_init_shared (handle, type,
253 /* Someone else beat us to it, just
261 /* Try again from the beginning */
266 /* Will need to expand the array. The caller will sort it out */
272 * _wapi_handle_new_internal:
273 * @type: Init handle to this type
275 * Search for a free handle and initialize it. Return the handle on
276 * success and 0 on failure. This is only called from
277 * _wapi_handle_new, and scan_mutex must be held.
279 static guint32 _wapi_handle_new_internal (WapiHandleType type,
280 gpointer handle_specific)
283 static guint32 last = 0;
284 gboolean retry = FALSE;
286 /* A linear scan should be fast enough. Start from the last
287 * allocation, assuming that handles are allocated more often
288 * than they're freed. Leave the space reserved for file
292 if (last < _wapi_fd_reserve) {
293 last = _wapi_fd_reserve;
300 for(i = SLOT_INDEX (count); _wapi_private_handles [i] != NULL; i++) {
301 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
302 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
304 if(handle->type == WAPI_HANDLE_UNUSED) {
307 _wapi_handle_init (handle, type, handle_specific);
314 if(retry && last > _wapi_fd_reserve) {
315 /* Try again from the beginning */
316 last = _wapi_fd_reserve;
320 /* Will need to expand the array. The caller will sort it out */
325 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
327 guint32 handle_idx = 0;
331 mono_once (&shared_init_once, shared_init);
334 g_message ("%s: Creating new handle of type %s", __func__,
335 _wapi_handle_typename[type]);
338 g_assert(!_WAPI_FD_HANDLE(type));
340 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
341 (void *)&scan_mutex);
342 thr_ret = mono_mutex_lock (&scan_mutex);
343 g_assert (thr_ret == 0);
345 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
346 /* Try and expand the array, and have another go */
347 int idx = SLOT_INDEX (_wapi_private_handle_count);
348 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
349 _WAPI_HANDLE_INITIAL_COUNT);
351 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
354 /* Make sure we left the space for fd mappings */
355 g_assert (handle_idx >= _wapi_fd_reserve);
357 handle = GUINT_TO_POINTER (handle_idx);
360 g_message ("%s: Allocated new handle %p", __func__, handle);
363 if (_WAPI_SHARED_HANDLE(type)) {
364 /* Add the shared section too */
367 offset = _wapi_handle_new_shared (type, handle_specific);
369 _wapi_handle_collect ();
370 offset = _wapi_handle_new_shared (type,
373 /* FIXME: grow the arrays */
374 handle = _WAPI_HANDLE_INVALID;
379 ref = _wapi_handle_new_shared_offset (offset);
381 _wapi_handle_collect ();
382 ref = _wapi_handle_new_shared_offset (offset);
385 /* FIXME: grow the arrays */
386 handle = _WAPI_HANDLE_INVALID;
391 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
393 g_message ("%s: New shared handle at offset 0x%x", __func__,
399 thr_ret = mono_mutex_unlock (&scan_mutex);
400 g_assert (thr_ret == 0);
401 pthread_cleanup_pop (0);
406 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset)
408 guint32 handle_idx = 0;
409 gpointer handle = INVALID_HANDLE_VALUE;
412 mono_once (&shared_init_once, shared_init);
415 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
416 _wapi_handle_typename[type], offset);
419 g_assert(!_WAPI_FD_HANDLE(type));
420 g_assert(_WAPI_SHARED_HANDLE(type));
421 g_assert(offset != 0);
423 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
424 (void *)&scan_mutex);
425 thr_ret = mono_mutex_lock (&scan_mutex);
426 g_assert (thr_ret == 0);
428 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
429 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
430 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
432 if (handle_data->type == type &&
433 handle_data->u.shared.offset == offset) {
434 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
435 goto first_pass_done;
441 thr_ret = mono_mutex_unlock (&scan_mutex);
442 g_assert (thr_ret == 0);
443 pthread_cleanup_pop (0);
445 if (handle != INVALID_HANDLE_VALUE) {
446 _wapi_handle_ref (handle);
449 g_message ("%s: Returning old handle %p referencing 0x%x",
450 __func__, handle, offset);
455 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
456 (void *)&scan_mutex);
457 thr_ret = mono_mutex_lock (&scan_mutex);
458 g_assert (thr_ret == 0);
460 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
461 /* Try and expand the array, and have another go */
462 int idx = SLOT_INDEX (_wapi_private_handle_count);
463 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
464 _WAPI_HANDLE_INITIAL_COUNT);
466 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
469 thr_ret = mono_mutex_unlock (&scan_mutex);
470 g_assert (thr_ret == 0);
471 pthread_cleanup_pop (0);
473 /* Make sure we left the space for fd mappings */
474 g_assert (handle_idx >= _wapi_fd_reserve);
476 handle = GUINT_TO_POINTER (handle_idx);
478 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
481 g_message ("%s: Allocated new handle %p referencing 0x%x", __func__,
488 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
489 gpointer handle_specific)
491 struct _WapiHandleUnshared *handle;
493 mono_once (&shared_init_once, shared_init);
496 g_message ("%s: Creating new handle of type %s", __func__,
497 _wapi_handle_typename[type]);
500 g_assert(_WAPI_FD_HANDLE(type));
501 g_assert(!_WAPI_SHARED_HANDLE(type));
503 if (fd >= _wapi_fd_reserve) {
505 g_message ("%s: fd %d is too big", __func__, fd);
508 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
511 handle = &_WAPI_PRIVATE_HANDLES(fd);
513 if (handle->type != WAPI_HANDLE_UNUSED) {
515 g_message ("%s: fd %d is already in use!", __func__, fd);
517 /* FIXME: clean up this handle? We can't do anything
518 * with the fd, cos thats the new one
523 g_message ("%s: Assigning new fd handle %d", __func__, fd);
526 _wapi_handle_init (handle, type, handle_specific);
528 return(GUINT_TO_POINTER(fd));
531 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
532 gpointer *handle_specific)
534 struct _WapiHandleUnshared *handle_data;
535 guint32 handle_idx = GPOINTER_TO_UINT(handle);
537 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
539 if (handle_data->type != type) {
543 if (handle_specific == NULL) {
547 if (_WAPI_SHARED_HANDLE(type)) {
548 struct _WapiHandle_shared_ref *ref;
549 struct _WapiHandleShared *shared_handle_data;
550 struct _WapiHandleSharedMetadata *shared_meta;
553 /* Unsafe, because we don't want the handle to vanish
554 * while we're checking it
556 _WAPI_HANDLE_COLLECTION_UNSAFE;
559 ref = &handle_data->u.shared;
560 shared_meta = &_wapi_shared_layout->metadata[ref->offset];
561 offset = shared_meta->offset;
562 shared_handle_data = &_wapi_shared_layout->handles[offset];
564 g_assert(shared_handle_data->type == type);
566 *handle_specific = &shared_handle_data->u;
567 } while (offset != shared_meta->offset);
569 _WAPI_HANDLE_COLLECTION_SAFE;
571 *handle_specific = &handle_data->u;
577 gboolean _wapi_copy_handle (gpointer handle, WapiHandleType type,
578 struct _WapiHandleShared *handle_specific)
580 struct _WapiHandleUnshared *handle_data;
581 guint32 handle_idx = GPOINTER_TO_UINT(handle);
582 struct _WapiHandle_shared_ref *ref;
583 struct _WapiHandleShared *shared_handle_data;
584 struct _WapiHandleSharedMetadata *shared_meta;
587 g_assert(_WAPI_SHARED_HANDLE(type));
590 g_message ("%s: copying handle %p type %s", __func__, handle,
591 _wapi_handle_typename[type]);
594 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
596 if(handle_data->type != type) {
598 g_message ("%s: incorrect type, %p has type %s", __func__,
599 handle, _wapi_handle_typename[handle_data->type]);
605 if(handle_specific == NULL) {
607 g_message ("%s: Nowhere to store data", __func__);
614 ref = &handle_data->u.shared;
615 shared_meta = &_wapi_shared_layout->metadata[ref->offset];
616 offset = shared_meta->offset;
617 shared_handle_data = &_wapi_shared_layout->handles[offset];
619 g_assert(shared_handle_data->type == type);
621 memcpy(handle_specific, shared_handle_data,
622 sizeof(struct _WapiHandleShared));
623 } while (offset != shared_meta->offset);
626 g_message ("%s: OK", __func__);
632 gboolean _wapi_replace_handle (gpointer handle, WapiHandleType type,
633 struct _WapiHandleShared *handle_specific)
635 struct _WapiHandleShared *shared_handle_data;
636 struct _WapiHandleSharedMetadata *shared_meta;
637 guint32 handle_idx = GPOINTER_TO_UINT(handle);
638 guint32 old_off, new_off, ref;
641 g_message ("%s: Replacing handle %p of type %s", __func__, handle,
642 _wapi_handle_typename[type]);
645 g_assert(_WAPI_SHARED_HANDLE(type));
646 g_assert(_WAPI_PRIVATE_HANDLES(handle_idx).type == type);
648 ref = _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset;
649 shared_meta = &_wapi_shared_layout->metadata[ref];
652 old_off = shared_meta->offset;
653 new_off = _wapi_handle_new_shared (type, handle_specific);
655 _wapi_handle_collect ();
656 new_off = _wapi_handle_new_shared (type,
660 /* FIXME: grow the arrays */
665 shared_handle_data = &_wapi_shared_layout->handles[new_off];
667 memcpy (shared_handle_data, handle_specific,
668 sizeof(struct _WapiHandleShared));
670 /* An entry can't become fresh again (its going to be
671 * collected eventually), so no need for atomic ops
674 _wapi_shared_layout->handles[old_off].stale = TRUE;
675 } while(InterlockedCompareExchange (&shared_meta->offset, new_off,
676 old_off) != old_off);
679 g_message ("%s: handle at 0x%x is now found at 0x%x", __func__, ref,
687 _wapi_handle_foreach (WapiHandleType type,
688 gboolean (*on_each)(gpointer test, gpointer user),
691 struct _WapiHandleUnshared *handle_data = NULL;
696 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
697 (void *)&scan_mutex);
698 thr_ret = mono_mutex_lock (&scan_mutex);
699 g_assert (thr_ret == 0);
701 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
702 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
703 handle_data = &_wapi_private_handles [i][k];
705 if (handle_data->type == type) {
706 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
707 if (on_each (ret, user_data) == TRUE)
713 thr_ret = mono_mutex_unlock (&scan_mutex);
714 g_assert (thr_ret == 0);
715 pthread_cleanup_pop (0);
718 /* This might list some shared handles twice if they are already
719 * opened by this process, and the check function returns FALSE the
720 * first time. Shared handles that are created during the search are
721 * unreffed if the check function returns FALSE, so callers must not
722 * rely on the handle persisting (unless the check function returns
725 gpointer _wapi_search_handle (WapiHandleType type,
726 gboolean (*check)(gpointer test, gpointer user),
728 gpointer *handle_specific)
730 struct _WapiHandleUnshared *handle_data = NULL;
733 gboolean found = FALSE;
736 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
737 (void *)&scan_mutex);
738 thr_ret = mono_mutex_lock (&scan_mutex);
739 g_assert (thr_ret == 0);
741 for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) {
742 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
743 handle_data = &_wapi_private_handles [i][k];
745 if (handle_data->type == type) {
746 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
747 if (check (ret, user_data) == TRUE) {
755 thr_ret = mono_mutex_unlock (&scan_mutex);
756 g_assert (thr_ret == 0);
757 pthread_cleanup_pop (0);
759 if (!found && _WAPI_SHARED_HANDLE (type)) {
760 /* Not found yet, so search the shared memory too */
762 g_message ("%s: Looking at other shared handles...", __func__);
765 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
766 struct _WapiHandleShared *shared;
767 struct _WapiHandleSharedMetadata *meta;
768 WapiHandleType shared_type;
770 _WAPI_HANDLE_COLLECTION_UNSAFE;
772 meta = &_wapi_shared_layout->metadata[i];
773 shared = &_wapi_shared_layout->handles[meta->offset];
774 shared_type = shared->type;
776 _WAPI_HANDLE_COLLECTION_SAFE;
778 if (shared_type == type) {
779 ret = _wapi_handle_new_from_offset (type, i);
782 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], meta->offset);
785 if (check (ret, user_data) == TRUE) {
787 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
792 /* This isn't the handle we're looking
793 * for, so drop the reference we took
794 * in _wapi_handle_new_from_offset ()
796 _wapi_handle_unref (ret);
805 if(handle_specific != NULL) {
806 if (_WAPI_SHARED_HANDLE(type)) {
807 struct _WapiHandle_shared_ref *ref ;
808 struct _WapiHandleShared *shared_handle_data;
809 struct _WapiHandleSharedMetadata *shared_meta;
812 /* Unsafe, because we don't want the handle to
813 * vanish while we're checking it
815 _WAPI_HANDLE_COLLECTION_UNSAFE;
818 ref = &handle_data->u.shared;
819 shared_meta = &_wapi_shared_layout->metadata[ref->offset];
820 offset = shared_meta->offset;
821 shared_handle_data = &_wapi_shared_layout->handles[offset];
823 g_assert(shared_handle_data->type == type);
825 *handle_specific = &shared_handle_data->u;
826 } while (offset != shared_meta->offset);
828 /* Make sure this handle doesn't vanish in the
831 now = (guint32)(time (NULL) & 0xFFFFFFFF);
832 InterlockedExchange (&shared_meta->timestamp, now);
834 _WAPI_HANDLE_COLLECTION_SAFE;
836 *handle_specific = &handle_data->u;
844 /* Returns the offset of the metadata array, or -1 on error, or 0 for
845 * not found (0 is not a valid offset)
847 gint32 _wapi_search_handle_namespace (WapiHandleType type,
850 struct _WapiHandleShared *shared_handle_data;
851 struct _WapiHandleSharedMetadata *shared_meta;
855 g_assert(_WAPI_SHARED_HANDLE(type));
858 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
859 utf8_name, _wapi_handle_typename[type]);
862 _WAPI_HANDLE_COLLECTION_UNSAFE;
864 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
865 WapiSharedNamespace *sharedns;
867 shared_meta = &_wapi_shared_layout->metadata[i];
868 shared_handle_data = &_wapi_shared_layout->handles[shared_meta->offset];
870 /* Check mutex, event, semaphore, timer, job and file-mapping
871 * object names. So far only mutex is implemented.
873 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
878 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
881 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
884 g_message ("%s: name is [%s]", __func__, sharedns->name);
887 if (strcmp (sharedns->name, utf8_name) == 0) {
888 if (shared_handle_data->type != type) {
889 /* Its the wrong type, so fail now */
891 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
897 g_message ("%s: handle 0x%x matches name and type", __func__, i);
906 _WAPI_HANDLE_COLLECTION_SAFE;
911 void _wapi_handle_ref (gpointer handle)
913 guint32 idx = GPOINTER_TO_UINT(handle);
914 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
915 struct _WapiHandleUnshared *handle_data = &_WAPI_PRIVATE_HANDLES(idx);
917 InterlockedIncrement (&handle_data->ref);
919 /* It's possible for processes to exit before getting around
920 * to updating timestamps in the collection thread, so if a
921 * shared handle is reffed do the timestamp here as well just
924 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
925 struct _WapiHandleSharedMetadata *shared_meta = &_wapi_shared_layout->metadata[handle_data->u.shared.offset];
927 InterlockedExchange (&shared_meta->timestamp, now);
931 g_message ("%s: handle %p ref now %d", __func__, handle,
932 _WAPI_PRIVATE_HANDLES(idx).ref);
936 /* The handle must not be locked on entry to this function */
937 void _wapi_handle_unref (gpointer handle)
939 guint32 idx = GPOINTER_TO_UINT(handle);
940 gboolean destroy = FALSE;
943 /* Possible race condition here if another thread refs the
944 * handle between here and setting the type to UNUSED. I
945 * could lock a mutex, but I'm not sure that allowing a handle
946 * reference to reach 0 isn't an application bug anyway.
948 destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
951 g_message ("%s: handle %p ref now %d (destroy %s)", __func__, handle,
952 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
956 /* Need to copy the handle info, reset the slot in the
957 * array, and _only then_ call the close function to
958 * avoid race conditions (eg file descriptors being
959 * closed, and another file being opened getting the
960 * same fd racing the memset())
962 struct _WapiHandleUnshared handle_data;
963 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
964 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
967 g_message ("%s: Destroying handle %p", __func__, handle);
970 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
971 sizeof (struct _WapiHandleUnshared));
973 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
974 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
976 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
978 if (!_WAPI_SHARED_HANDLE(type)) {
979 /* Destroy the mutex and cond var. We hope nobody
980 * tried to grab them between the handle unlock and
981 * now, but pthreads doesn't have a
982 * "unlock_and_destroy" atomic function.
984 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
985 g_assert (thr_ret == 0);
987 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
988 g_assert (thr_ret == 0);
991 /* The garbage collector will take care of shared data
992 * if this is a shared handle
995 if (close_func != NULL) {
996 close_func (handle, &handle_data.u);
1001 void _wapi_handle_register_capabilities (WapiHandleType type,
1002 WapiHandleCapability caps)
1004 handle_caps[type] = caps;
1007 gboolean _wapi_handle_test_capabilities (gpointer handle,
1008 WapiHandleCapability caps)
1010 guint32 idx = GPOINTER_TO_UINT(handle);
1011 WapiHandleType type;
1013 type = _WAPI_PRIVATE_HANDLES(idx).type;
1016 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
1017 handle_caps[type], caps, handle_caps[type] & caps);
1020 return((handle_caps[type] & caps) != 0);
1023 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
1025 if (handle_ops[type] != NULL &&
1026 handle_ops[type]->close != NULL) {
1027 return (handle_ops[type]->close);
1033 void _wapi_handle_ops_close (gpointer handle, gpointer data)
1035 guint32 idx = GPOINTER_TO_UINT(handle);
1036 WapiHandleType type;
1038 type = _WAPI_PRIVATE_HANDLES(idx).type;
1040 if (handle_ops[type] != NULL &&
1041 handle_ops[type]->close != NULL) {
1042 handle_ops[type]->close (handle, data);
1046 void _wapi_handle_ops_signal (gpointer handle)
1048 guint32 idx = GPOINTER_TO_UINT(handle);
1049 WapiHandleType type;
1051 type = _WAPI_PRIVATE_HANDLES(idx).type;
1053 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1054 handle_ops[type]->signal (handle);
1058 gboolean _wapi_handle_ops_own (gpointer handle)
1060 guint32 idx = GPOINTER_TO_UINT(handle);
1061 WapiHandleType type;
1063 type = _WAPI_PRIVATE_HANDLES(idx).type;
1065 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1066 return(handle_ops[type]->own_handle (handle));
1072 gboolean _wapi_handle_ops_isowned (gpointer handle)
1074 guint32 idx = GPOINTER_TO_UINT(handle);
1075 WapiHandleType type;
1077 type = _WAPI_PRIVATE_HANDLES(idx).type;
1079 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1080 return(handle_ops[type]->is_owned (handle));
1086 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1088 guint32 idx = GPOINTER_TO_UINT(handle);
1089 WapiHandleType type;
1091 type = _WAPI_PRIVATE_HANDLES(idx).type;
1093 if (handle_ops[type] != NULL &&
1094 handle_ops[type]->special_wait != NULL) {
1095 return(handle_ops[type]->special_wait (handle, timeout));
1097 return(WAIT_FAILED);
1104 * @handle: The handle to release
1106 * Closes and invalidates @handle, releasing any resources it
1107 * consumes. When the last handle to a temporary or non-persistent
1108 * object is closed, that object can be deleted. Closing the same
1109 * handle twice is an error.
1111 * Return value: %TRUE on success, %FALSE otherwise.
1113 gboolean CloseHandle(gpointer handle)
1115 _wapi_handle_unref (handle);
1120 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1126 guint32 count, i, iter=0;
1129 WapiHandleType type;
1131 /* Lock all the handles, with backoff */
1133 thr_ret = _wapi_handle_lock_shared_handles ();
1134 g_assert (thr_ret == 0);
1136 for(i=0; i<numhandles; i++) {
1137 gpointer handle = handles[i];
1138 guint32 idx = GPOINTER_TO_UINT(handle);
1141 g_message ("%s: attempting to lock %p", __func__, handle);
1144 type = _WAPI_PRIVATE_HANDLES(idx).type;
1146 thr_ret = _wapi_handle_trylock_handle (handle);
1152 g_message ("%s: attempt failed for %p: %s", __func__,
1153 handle, strerror (thr_ret));
1156 thr_ret = _wapi_handle_unlock_shared_handles ();
1157 g_assert (thr_ret == 0);
1160 handle = handles[i];
1161 idx = GPOINTER_TO_UINT(handle);
1163 thr_ret = _wapi_handle_unlock_handle (handle);
1164 g_assert (thr_ret == 0);
1167 /* If iter ever reaches 100 the nanosleep will
1168 * return EINVAL immediately, but we have a
1169 * design flaw if that happens.
1173 g_warning ("%s: iteration overflow!",
1179 g_message ("%s: Backing off for %d ms", __func__,
1182 _wapi_handle_spin (10 * iter);
1189 g_message ("%s: Locked all handles", __func__);
1195 for(i=0; i<numhandles; i++) {
1196 gpointer handle = handles[i];
1197 guint32 idx = GPOINTER_TO_UINT(handle);
1199 type = _WAPI_PRIVATE_HANDLES(idx).type;
1201 _wapi_handle_ref (handle);
1204 g_message ("%s: Checking handle %p", __func__, handle);
1207 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1208 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1209 (_WAPI_SHARED_HANDLE(type) &&
1210 WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) ||
1211 (!_WAPI_SHARED_HANDLE(type) &&
1212 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1216 g_message ("%s: Handle %p signalled", __func__,
1226 g_message ("%s: %d event handles signalled", __func__, count);
1229 if ((waitall == TRUE && count == numhandles) ||
1230 (waitall == FALSE && count > 0)) {
1237 g_message ("%s: Returning %d", __func__, ret);
1245 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1250 thr_ret = _wapi_handle_unlock_shared_handles ();
1251 g_assert (thr_ret == 0);
1253 for(i=0; i<numhandles; i++) {
1254 gpointer handle = handles[i];
1257 g_message ("%s: unlocking handle %p", __func__, handle);
1260 thr_ret = _wapi_handle_unlock_handle (handle);
1261 g_assert (thr_ret == 0);
1265 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout)
1267 struct timespec fake_timeout;
1270 _wapi_calc_timeout (&fake_timeout, 100);
1272 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1273 (fake_timeout.tv_sec == timeout->tv_sec &&
1274 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1275 /* Real timeout is less than 100ms time */
1276 ret=mono_cond_timedwait (cond, mutex, timeout);
1278 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1280 /* Mask the fake timeout, this will cause
1281 * another poll if the cond was not really signaled
1283 if (ret==ETIMEDOUT) {
1291 int _wapi_handle_wait_signal (void)
1293 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL);
1296 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1298 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout);
1301 int _wapi_handle_wait_signal_poll_share (void)
1304 g_message ("%s: poll private and shared handles", __func__);
1307 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL);
1310 int _wapi_handle_timedwait_signal_poll_share (struct timespec *timeout)
1313 g_message ("%s: poll private and shared handles", __func__);
1316 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout);
1319 int _wapi_handle_wait_signal_handle (gpointer handle)
1322 g_message ("%s: waiting for %p", __func__, handle);
1325 return _wapi_handle_timedwait_signal_handle (handle, NULL);
1328 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1329 struct timespec *timeout)
1332 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1333 _wapi_handle_typename[_wapi_handle_type (handle)]);
1336 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1337 if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) {
1340 if (timeout != NULL) {
1341 struct timespec fake_timeout;
1342 _wapi_calc_timeout (&fake_timeout, 100);
1344 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1345 (fake_timeout.tv_sec == timeout->tv_sec &&
1346 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1347 /* FIXME: Real timeout is less than
1348 * 100ms time, but is it really worth
1349 * calculating to the exact ms?
1351 _wapi_handle_spin (100);
1353 if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) {
1360 _wapi_handle_spin (100);
1364 guint32 idx = GPOINTER_TO_UINT(handle);
1365 return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout);
1369 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1370 guint32 new_sharemode,
1372 guint32 *old_sharemode,
1373 guint32 *old_access,
1374 struct _WapiFileShare **share_info)
1376 struct _WapiFileShare *file_share;
1377 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1378 int thr_ret, i, first_unused = -1;
1379 gboolean exists = FALSE;
1381 /* Marking this as COLLECTION_UNSAFE prevents entries from
1382 * expiring under us as we search
1384 _WAPI_HANDLE_COLLECTION_UNSAFE;
1386 /* Prevent new entries racing with us */
1387 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE);
1388 g_assert (thr_ret == 0);
1390 /* If a linear scan gets too slow we'll have to fit a hash
1391 * table onto the shared mem backing store
1394 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1395 file_share = &_wapi_fileshare_layout->share_info[i];
1397 /* Make a note of an unused slot, in case we need to
1400 if (first_unused == -1 && file_share->handle_refs == 0) {
1405 if (file_share->handle_refs == 0) {
1409 if (file_share->device == device &&
1410 file_share->inode == inode) {
1411 *old_sharemode = file_share->sharemode;
1412 *old_access = file_share->access;
1413 *share_info = file_share;
1415 /* Increment the reference count while we
1416 * still have sole access to the shared area.
1417 * This makes the increment atomic wrt
1420 InterlockedIncrement (&file_share->handle_refs);
1428 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1431 if (first_unused == -1) {
1432 file_share = &_wapi_fileshare_layout->share_info[++i];
1433 _wapi_fileshare_layout->hwm = i;
1435 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1438 file_share->device = device;
1439 file_share->inode = inode;
1440 file_share->opened_by_pid = getpid ();
1441 file_share->sharemode = new_sharemode;
1442 file_share->access = new_access;
1443 file_share->handle_refs = 1;
1444 *share_info = file_share;
1448 if (*share_info != NULL) {
1449 InterlockedExchange (&(*share_info)->timestamp, now);
1452 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE);
1454 _WAPI_HANDLE_COLLECTION_SAFE;
1459 /* If we don't have the info in /proc, check if the process that
1460 * opened this share info is still there (it's not a perfect method,
1463 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1465 if (kill (share_info->opened_by_pid, 0) == -1 &&
1468 /* It's gone completely (or there's a new process
1469 * owned by someone else) so mark this share info as
1473 g_message ("%s: Didn't find it, destroying entry", __func__);
1476 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1480 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1481 * question. If there are none, reset the share info.
1483 * This implementation is Linux-specific; legacy systems will have to
1484 * implement their own ways of finding out if a particular file is
1485 * open by a process.
1487 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1489 gboolean found = FALSE, proc_fds = FALSE;
1490 pid_t self = getpid();
1494 /* If there is no /proc, there's nothing more we can do here */
1495 if (access ("/proc", F_OK) == -1) {
1496 _wapi_handle_check_share_by_pid (share_info);
1500 /* Marking this as COLLECTION_UNSAFE prevents entries from
1501 * expiring under us if we remove this one
1503 _WAPI_HANDLE_COLLECTION_UNSAFE;
1505 /* Prevent new entries racing with us */
1506 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE);
1507 g_assert (thr_ret == 0);
1509 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1510 struct _WapiHandleShared *shared;
1511 struct _WapiHandleSharedMetadata *meta;
1512 struct _WapiHandle_process *process_handle;
1514 meta = &_wapi_shared_layout->metadata[i];
1515 shared = &_wapi_shared_layout->handles[meta->offset];
1517 if (shared->type == WAPI_HANDLE_PROCESS) {
1519 struct dirent *fd_entry;
1520 char subdir[_POSIX_PATH_MAX];
1522 process_handle = &shared->u.process;
1523 pid = process_handle->id;
1525 /* Look in /proc/<pid>/fd/ but ignore
1526 * /proc/<our pid>/fd/<fd>, as we have the
1529 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1532 fd_dir = opendir (subdir);
1533 if (fd_dir == NULL) {
1538 g_message ("%s: Looking in %s", __func__, subdir);
1543 while ((fd_entry = readdir (fd_dir)) != NULL) {
1544 char path[_POSIX_PATH_MAX];
1545 struct stat link_stat;
1547 if (!strcmp (fd_entry->d_name, ".") ||
1548 !strcmp (fd_entry->d_name, "..") ||
1550 fd == atoi (fd_entry->d_name))) {
1554 g_snprintf (path, _POSIX_PATH_MAX,
1555 "/proc/%d/fd/%s", pid,
1558 stat (path, &link_stat);
1559 if (link_stat.st_dev == share_info->device &&
1560 link_stat.st_ino == share_info->inode) {
1562 g_message ("%s: Found it at %s",
1574 if (proc_fds == FALSE) {
1575 _wapi_handle_check_share_by_pid (share_info);
1576 } else if (found == FALSE) {
1577 /* Blank out this entry, as it is stale */
1579 g_message ("%s: Didn't find it, destroying entry", __func__);
1582 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1585 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE);
1587 _WAPI_HANDLE_COLLECTION_SAFE;
1590 void _wapi_handle_dump (void)
1592 struct _WapiHandleUnshared *handle_data;
1596 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1597 (void *)&scan_mutex);
1598 thr_ret = mono_mutex_lock (&scan_mutex);
1599 g_assert (thr_ret == 0);
1601 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1602 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1603 handle_data = &_wapi_private_handles [i][k];
1605 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1609 g_print ("%3x [%7s] %s %d ",
1610 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1611 _wapi_handle_typename[handle_data->type],
1612 handle_data->signalled?"Sg":"Un",
1614 handle_details[handle_data->type](&handle_data->u);
1619 thr_ret = mono_mutex_unlock (&scan_mutex);
1620 g_assert (thr_ret == 0);
1621 pthread_cleanup_pop (0);
1624 static void _wapi_shared_details (gpointer handle_info)
1626 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1628 g_print ("offset: 0x%x", shared->offset);
1631 void _wapi_handle_update_refs (void)
1636 _WAPI_HANDLE_COLLECTION_UNSAFE;
1638 /* Prevent file share entries racing with us */
1639 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE);
1640 g_assert(thr_ret == 0);
1642 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1643 (void *)&scan_mutex);
1644 thr_ret = mono_mutex_lock (&scan_mutex);
1646 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1647 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1648 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1649 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1651 if (_WAPI_SHARED_HANDLE(handle->type)) {
1652 struct _WapiHandleSharedMetadata *shared_meta;
1655 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__,
1656 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1659 shared_meta = &_wapi_shared_layout->metadata[handle->u.shared.offset];
1662 g_message ("%s: (%d) Updating timestamp of handle 0x%x",
1664 handle->u.shared.offset);
1667 InterlockedExchange (&shared_meta->timestamp, now);
1668 } else if (handle->type == WAPI_HANDLE_FILE) {
1669 struct _WapiHandle_file *file_handle = &handle->u.file;
1672 g_message ("%s: (%d) handle 0x%x is FILE", __func__,
1673 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1676 g_assert (file_handle->share_info != NULL);
1679 g_message ("%s: (%d) Inc refs on fileshare 0x%x",
1681 (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1684 InterlockedExchange (&file_handle->share_info->timestamp, now);
1689 thr_ret = mono_mutex_unlock (&scan_mutex);
1690 g_assert (thr_ret == 0);
1691 pthread_cleanup_pop (0);
1693 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE);
1695 _WAPI_HANDLE_COLLECTION_SAFE;