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,
55 static void _wapi_shared_details (gpointer handle_info);
57 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
60 _wapi_console_details,
61 _wapi_shared_details, /* thread */
65 NULL, /* Nothing useful to see in a socket handle */
66 NULL, /* Nothing useful to see in a find handle */
67 _wapi_shared_details, /* process */
69 _wapi_shared_details, /* namedmutex */
70 _wapi_shared_details, /* namedsem */
73 const char *_wapi_handle_typename[] = {
91 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
92 * If 4M handles are not enough... Oh, well... we will crash.
94 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
95 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
97 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
98 static guint32 _wapi_private_handle_count = 0;
100 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
101 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
103 guint32 _wapi_fd_reserve;
105 mono_mutex_t _wapi_global_signal_mutex;
106 pthread_cond_t _wapi_global_signal_cond;
110 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
112 static mono_once_t shared_init_once = MONO_ONCE_INIT;
113 static void shared_init (void)
118 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
119 == WAPI_HANDLE_COUNT);
121 _wapi_fd_reserve = getdtablesize();
124 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
125 _WAPI_HANDLE_INITIAL_COUNT);
127 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
128 } while(_wapi_fd_reserve > _wapi_private_handle_count);
130 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
131 g_assert (_wapi_shared_layout != NULL);
133 _wapi_shm_semaphores_init ();
135 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
136 g_assert (_wapi_fileshare_layout != NULL);
138 _wapi_collection_init ();
140 thr_ret = pthread_cond_init(&_wapi_global_signal_cond, NULL);
141 g_assert (thr_ret == 0);
143 thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL);
144 g_assert (thr_ret == 0);
147 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
149 gpointer handle_specific)
152 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
153 handle->signalled = FALSE;
154 handle->handle_refs = 1;
156 if (handle_specific != NULL) {
157 memcpy (&handle->u, handle_specific, sizeof (handle->u));
161 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
162 WapiHandleType type, gpointer handle_specific)
167 handle->signalled = FALSE;
170 if (!_WAPI_SHARED_HANDLE(type)) {
171 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
172 g_assert (thr_ret == 0);
174 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
175 g_assert (thr_ret == 0);
177 if (handle_specific != NULL) {
178 memcpy (&handle->u, handle_specific,
184 static guint32 _wapi_handle_new_shared (WapiHandleType type,
185 gpointer handle_specific)
188 static guint32 last = 1;
191 /* Leave the first slot empty as a guard */
193 /* FIXME: expandable array */
194 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
195 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
197 if(handle->type == WAPI_HANDLE_UNUSED) {
198 thr_ret = _wapi_handle_lock_shared_handles ();
199 g_assert (thr_ret == 0);
201 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
204 _wapi_handle_init_shared (handle, type,
207 _wapi_handle_unlock_shared_handles ();
211 /* Someone else beat us to it, just
216 _wapi_handle_unlock_shared_handles ();
221 /* Try again from the beginning */
226 /* Will need to expand the array. The caller will sort it out */
232 * _wapi_handle_new_internal:
233 * @type: Init handle to this type
235 * Search for a free handle and initialize it. Return the handle on
236 * success and 0 on failure. This is only called from
237 * _wapi_handle_new, and scan_mutex must be held.
239 static guint32 _wapi_handle_new_internal (WapiHandleType type,
240 gpointer handle_specific)
243 static guint32 last = 0;
244 gboolean retry = FALSE;
246 /* A linear scan should be fast enough. Start from the last
247 * allocation, assuming that handles are allocated more often
248 * than they're freed. Leave the space reserved for file
252 if (last < _wapi_fd_reserve) {
253 last = _wapi_fd_reserve;
260 for(i = SLOT_INDEX (count); _wapi_private_handles [i] != NULL; i++) {
261 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
262 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
264 if(handle->type == WAPI_HANDLE_UNUSED) {
267 _wapi_handle_init (handle, type, handle_specific);
274 if(retry && last > _wapi_fd_reserve) {
275 /* Try again from the beginning */
276 last = _wapi_fd_reserve;
280 /* Will need to expand the array. The caller will sort it out */
285 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
287 guint32 handle_idx = 0;
291 mono_once (&shared_init_once, shared_init);
294 g_message ("%s: Creating new handle of type %s", __func__,
295 _wapi_handle_typename[type]);
298 g_assert(!_WAPI_FD_HANDLE(type));
300 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
301 (void *)&scan_mutex);
302 thr_ret = mono_mutex_lock (&scan_mutex);
303 g_assert (thr_ret == 0);
305 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
306 /* Try and expand the array, and have another go */
307 int idx = SLOT_INDEX (_wapi_private_handle_count);
308 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
309 _WAPI_HANDLE_INITIAL_COUNT);
311 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
314 thr_ret = mono_mutex_unlock (&scan_mutex);
315 g_assert (thr_ret == 0);
316 pthread_cleanup_pop (0);
318 /* Make sure we left the space for fd mappings */
319 g_assert (handle_idx >= _wapi_fd_reserve);
321 handle = GUINT_TO_POINTER (handle_idx);
324 g_message ("%s: Allocated new handle %p", __func__, handle);
327 if (_WAPI_SHARED_HANDLE(type)) {
328 /* Add the shared section too */
331 ref = _wapi_handle_new_shared (type, handle_specific);
333 _wapi_handle_collect ();
334 ref = _wapi_handle_new_shared (type, handle_specific);
336 /* FIXME: grow the arrays */
337 handle = _WAPI_HANDLE_INVALID;
342 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
344 g_message ("%s: New shared handle at offset 0x%x", __func__,
353 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
356 guint32 handle_idx = 0;
357 gpointer handle = INVALID_HANDLE_VALUE;
359 struct _WapiHandleShared *shared;
360 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
362 mono_once (&shared_init_once, shared_init);
365 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
366 _wapi_handle_typename[type], offset);
369 g_assert(!_WAPI_FD_HANDLE(type));
370 g_assert(_WAPI_SHARED_HANDLE(type));
371 g_assert(offset != 0);
373 shared = &_wapi_shared_layout->handles[offset];
375 /* Bump up the timestamp for this offset */
376 InterlockedExchange (&shared->timestamp, now);
379 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
380 (void *)&scan_mutex);
381 thr_ret = mono_mutex_lock (&scan_mutex);
382 g_assert (thr_ret == 0);
384 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
385 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
386 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
388 if (handle_data->type == type &&
389 handle_data->u.shared.offset == offset) {
390 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
391 goto first_pass_done;
397 thr_ret = mono_mutex_unlock (&scan_mutex);
398 g_assert (thr_ret == 0);
399 pthread_cleanup_pop (0);
401 if (handle != INVALID_HANDLE_VALUE) {
402 _wapi_handle_ref (handle);
405 g_message ("%s: Returning old handle %p referencing 0x%x",
406 __func__, handle, offset);
411 /* Prevent entries expiring under us as we search */
412 thr_ret = _wapi_handle_lock_shared_handles ();
413 g_assert (thr_ret == 0);
415 if (shared->type == WAPI_HANDLE_UNUSED) {
416 /* Someone deleted this handle while we were working */
420 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
421 (void *)&scan_mutex);
422 thr_ret = mono_mutex_lock (&scan_mutex);
423 g_assert (thr_ret == 0);
425 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
426 /* Try and expand the array, and have another go */
427 int idx = SLOT_INDEX (_wapi_private_handle_count);
428 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
429 _WAPI_HANDLE_INITIAL_COUNT);
431 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
434 thr_ret = mono_mutex_unlock (&scan_mutex);
435 g_assert (thr_ret == 0);
436 pthread_cleanup_pop (0);
438 /* Make sure we left the space for fd mappings */
439 g_assert (handle_idx >= _wapi_fd_reserve);
441 handle = GUINT_TO_POINTER (handle_idx);
443 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
444 InterlockedIncrement (&shared->handle_refs);
447 g_message ("%s: Allocated new handle %p referencing 0x%x", __func__,
452 _wapi_handle_unlock_shared_handles ();
457 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
458 gpointer handle_specific)
460 struct _WapiHandleUnshared *handle;
463 mono_once (&shared_init_once, shared_init);
466 g_message ("%s: Creating new handle of type %s", __func__,
467 _wapi_handle_typename[type]);
470 g_assert(_WAPI_FD_HANDLE(type));
471 g_assert(!_WAPI_SHARED_HANDLE(type));
473 if (fd >= _wapi_fd_reserve) {
475 g_message ("%s: fd %d is too big", __func__, fd);
478 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
481 handle = &_WAPI_PRIVATE_HANDLES(fd);
483 if (handle->type != WAPI_HANDLE_UNUSED) {
485 g_message ("%s: fd %d is already in use!", __func__, fd);
487 /* FIXME: clean up this handle? We can't do anything
488 * with the fd, cos thats the new one
493 g_message ("%s: Assigning new fd handle %d", __func__, fd);
496 /* Prevent file share entries racing with us, when the file
497 * handle is only half initialised
499 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
500 g_assert(thr_ret == 0);
502 _wapi_handle_init (handle, type, handle_specific);
504 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
506 return(GUINT_TO_POINTER(fd));
509 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
510 gpointer *handle_specific)
512 struct _WapiHandleUnshared *handle_data;
513 guint32 handle_idx = GPOINTER_TO_UINT(handle);
515 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
517 if (handle_data->type != type) {
521 if (handle_specific == NULL) {
525 if (_WAPI_SHARED_HANDLE(type)) {
526 struct _WapiHandle_shared_ref *ref;
527 struct _WapiHandleShared *shared_handle_data;
529 ref = &handle_data->u.shared;
530 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
532 if (shared_handle_data->type != type) {
533 /* The handle must have been deleted on us
538 *handle_specific = &shared_handle_data->u;
540 *handle_specific = &handle_data->u;
547 _wapi_handle_foreach (WapiHandleType type,
548 gboolean (*on_each)(gpointer test, gpointer user),
551 struct _WapiHandleUnshared *handle_data = NULL;
556 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
557 (void *)&scan_mutex);
558 thr_ret = mono_mutex_lock (&scan_mutex);
559 g_assert (thr_ret == 0);
561 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
562 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
563 handle_data = &_wapi_private_handles [i][k];
565 if (handle_data->type == type) {
566 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
567 if (on_each (ret, user_data) == TRUE)
573 thr_ret = mono_mutex_unlock (&scan_mutex);
574 g_assert (thr_ret == 0);
575 pthread_cleanup_pop (0);
578 /* This might list some shared handles twice if they are already
579 * opened by this process, and the check function returns FALSE the
580 * first time. Shared handles that are created during the search are
581 * unreffed if the check function returns FALSE, so callers must not
582 * rely on the handle persisting (unless the check function returns
585 gpointer _wapi_search_handle (WapiHandleType type,
586 gboolean (*check)(gpointer test, gpointer user),
588 gpointer *handle_specific)
590 struct _WapiHandleUnshared *handle_data = NULL;
591 struct _WapiHandleShared *shared;
594 gboolean found = FALSE;
597 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
598 (void *)&scan_mutex);
599 thr_ret = mono_mutex_lock (&scan_mutex);
600 g_assert (thr_ret == 0);
602 for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) {
603 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
604 handle_data = &_wapi_private_handles [i][k];
606 if (handle_data->type == type) {
607 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
608 if (check (ret, user_data) == TRUE) {
616 thr_ret = mono_mutex_unlock (&scan_mutex);
617 g_assert (thr_ret == 0);
618 pthread_cleanup_pop (0);
620 if (!found && _WAPI_SHARED_HANDLE (type)) {
621 /* Not found yet, so search the shared memory too */
623 g_message ("%s: Looking at other shared handles...", __func__);
626 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
627 shared = &_wapi_shared_layout->handles[i];
629 if (shared->type == type) {
630 /* Tell new_from_offset to not
631 * timestamp this handle, because
632 * otherwise it will ping every handle
633 * in the list and they will never
636 ret = _wapi_handle_new_from_offset (type, i,
638 if (ret == INVALID_HANDLE_VALUE) {
639 /* This handle was deleted
640 * while we were looking at it
646 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
649 /* It's possible that the shared part
650 * of this handle has now been blown
651 * away (after new_from_offset
652 * successfully opened it,) if its
653 * timestamp is too old. The check
654 * function needs to be aware of this,
655 * and cope if the handle has
658 if (check (ret, user_data) == TRUE) {
659 /* Timestamp this handle, but make
660 * sure it still exists first
662 thr_ret = _wapi_handle_lock_shared_handles ();
663 g_assert (thr_ret == 0);
665 if (shared->type == type) {
666 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
667 InterlockedExchange (&shared->timestamp, now);
670 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
672 _wapi_handle_unlock_shared_handles ();
675 /* It's been deleted,
679 _wapi_handle_unlock_shared_handles ();
683 /* This isn't the handle we're looking
684 * for, so drop the reference we took
685 * in _wapi_handle_new_from_offset ()
687 _wapi_handle_unref (ret);
697 if(handle_specific != NULL) {
698 if (_WAPI_SHARED_HANDLE(type)) {
699 g_assert(shared->type == type);
701 *handle_specific = &shared->u;
703 *handle_specific = &handle_data->u;
711 /* Returns the offset of the metadata array, or -1 on error, or 0 for
712 * not found (0 is not a valid offset)
714 gint32 _wapi_search_handle_namespace (WapiHandleType type,
717 struct _WapiHandleShared *shared_handle_data;
722 g_assert(_WAPI_SHARED_HANDLE(type));
725 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
726 utf8_name, _wapi_handle_typename[type]);
729 /* Do a handle collection before starting to look, so that any
730 * stale cruft gets removed
732 _wapi_handle_collect ();
734 thr_ret = _wapi_handle_lock_shared_handles ();
735 g_assert (thr_ret == 0);
737 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
738 WapiSharedNamespace *sharedns;
740 shared_handle_data = &_wapi_shared_layout->handles[i];
742 /* Check mutex, event, semaphore, timer, job and
743 * file-mapping object names. So far only mutex and
744 * semaphore are implemented.
746 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
751 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
754 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
757 g_message ("%s: name is [%s]", __func__, sharedns->name);
760 if (strcmp (sharedns->name, utf8_name) == 0) {
761 if (shared_handle_data->type != type) {
762 /* Its the wrong type, so fail now */
764 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
770 g_message ("%s: handle 0x%x matches name and type", __func__, i);
779 _wapi_handle_unlock_shared_handles ();
784 void _wapi_handle_ref (gpointer handle)
786 guint32 idx = GPOINTER_TO_UINT(handle);
787 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
788 struct _WapiHandleUnshared *handle_data = &_WAPI_PRIVATE_HANDLES(idx);
790 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
791 g_warning ("%s: Attempting to ref unused handle %p", __func__,
796 InterlockedIncrement (&handle_data->ref);
798 /* It's possible for processes to exit before getting around
799 * to updating timestamps in the collection thread, so if a
800 * shared handle is reffed do the timestamp here as well just
803 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
804 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
806 InterlockedExchange (&shared_data->timestamp, now);
810 g_message ("%s: handle %p ref now %d", __func__, handle,
811 _WAPI_PRIVATE_HANDLES(idx).ref);
815 /* The handle must not be locked on entry to this function */
816 void _wapi_handle_unref (gpointer handle)
818 guint32 idx = GPOINTER_TO_UINT(handle);
819 gboolean destroy = FALSE;
822 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
823 g_warning ("%s: Attempting to unref unused handle %p",
828 /* Possible race condition here if another thread refs the
829 * handle between here and setting the type to UNUSED. I
830 * could lock a mutex, but I'm not sure that allowing a handle
831 * reference to reach 0 isn't an application bug anyway.
833 destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
836 g_message ("%s: handle %p ref now %d (destroy %s)", __func__, handle,
837 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
841 /* Need to copy the handle info, reset the slot in the
842 * array, and _only then_ call the close function to
843 * avoid race conditions (eg file descriptors being
844 * closed, and another file being opened getting the
845 * same fd racing the memset())
847 struct _WapiHandleUnshared handle_data;
848 struct _WapiHandleShared shared_handle_data;
849 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
850 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
851 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
854 /* If this is a shared handle we need to take
855 * the shared lock outside of the scan_mutex
856 * lock to avoid deadlocks
858 thr_ret = _wapi_handle_lock_shared_handles ();
859 g_assert (thr_ret == 0);
862 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
863 thr_ret = mono_mutex_lock (&scan_mutex);
866 g_message ("%s: Destroying handle %p", __func__, handle);
869 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
870 sizeof (struct _WapiHandleUnshared));
872 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
873 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
875 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
878 /* Destroy the mutex and cond var. We hope nobody
879 * tried to grab them between the handle unlock and
880 * now, but pthreads doesn't have a
881 * "unlock_and_destroy" atomic function.
883 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
884 g_assert (thr_ret == 0);
886 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
887 g_assert (thr_ret == 0);
889 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
891 memcpy (&shared_handle_data, shared,
892 sizeof (struct _WapiHandleShared));
894 /* It's possible that this handle is already
895 * pointing at a deleted shared section
897 if (shared->handle_refs > 0) {
898 shared->handle_refs--;
899 if (shared->handle_refs == 0) {
900 memset (shared, '\0', sizeof (struct _WapiHandleShared));
905 thr_ret = mono_mutex_unlock (&scan_mutex);
906 g_assert (thr_ret == 0);
907 pthread_cleanup_pop (0);
910 _wapi_handle_unlock_shared_handles ();
913 if (close_func != NULL) {
915 close_func (handle, &shared_handle_data.u);
917 close_func (handle, &handle_data.u);
923 void _wapi_handle_register_capabilities (WapiHandleType type,
924 WapiHandleCapability caps)
926 handle_caps[type] = caps;
929 gboolean _wapi_handle_test_capabilities (gpointer handle,
930 WapiHandleCapability caps)
932 guint32 idx = GPOINTER_TO_UINT(handle);
935 type = _WAPI_PRIVATE_HANDLES(idx).type;
938 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
939 handle_caps[type], caps, handle_caps[type] & caps);
942 return((handle_caps[type] & caps) != 0);
945 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
947 if (handle_ops[type] != NULL &&
948 handle_ops[type]->close != NULL) {
949 return (handle_ops[type]->close);
955 void _wapi_handle_ops_close (gpointer handle, gpointer data)
957 guint32 idx = GPOINTER_TO_UINT(handle);
960 type = _WAPI_PRIVATE_HANDLES(idx).type;
962 if (handle_ops[type] != NULL &&
963 handle_ops[type]->close != NULL) {
964 handle_ops[type]->close (handle, data);
968 void _wapi_handle_ops_signal (gpointer handle)
970 guint32 idx = GPOINTER_TO_UINT(handle);
973 type = _WAPI_PRIVATE_HANDLES(idx).type;
975 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
976 handle_ops[type]->signal (handle);
980 gboolean _wapi_handle_ops_own (gpointer handle)
982 guint32 idx = GPOINTER_TO_UINT(handle);
985 type = _WAPI_PRIVATE_HANDLES(idx).type;
987 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
988 return(handle_ops[type]->own_handle (handle));
994 gboolean _wapi_handle_ops_isowned (gpointer handle)
996 guint32 idx = GPOINTER_TO_UINT(handle);
999 type = _WAPI_PRIVATE_HANDLES(idx).type;
1001 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1002 return(handle_ops[type]->is_owned (handle));
1008 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1010 guint32 idx = GPOINTER_TO_UINT(handle);
1011 WapiHandleType type;
1013 type = _WAPI_PRIVATE_HANDLES(idx).type;
1015 if (handle_ops[type] != NULL &&
1016 handle_ops[type]->special_wait != NULL) {
1017 return(handle_ops[type]->special_wait (handle, timeout));
1019 return(WAIT_FAILED);
1026 * @handle: The handle to release
1028 * Closes and invalidates @handle, releasing any resources it
1029 * consumes. When the last handle to a temporary or non-persistent
1030 * object is closed, that object can be deleted. Closing the same
1031 * handle twice is an error.
1033 * Return value: %TRUE on success, %FALSE otherwise.
1035 gboolean CloseHandle(gpointer handle)
1037 _wapi_handle_unref (handle);
1042 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1048 guint32 count, i, iter=0;
1051 WapiHandleType type;
1053 /* Lock all the handles, with backoff */
1055 thr_ret = _wapi_handle_lock_shared_handles ();
1056 g_assert (thr_ret == 0);
1058 for(i=0; i<numhandles; i++) {
1059 gpointer handle = handles[i];
1060 guint32 idx = GPOINTER_TO_UINT(handle);
1063 g_message ("%s: attempting to lock %p", __func__, handle);
1066 type = _WAPI_PRIVATE_HANDLES(idx).type;
1068 thr_ret = _wapi_handle_trylock_handle (handle);
1074 g_message ("%s: attempt failed for %p: %s", __func__,
1075 handle, strerror (thr_ret));
1078 thr_ret = _wapi_handle_unlock_shared_handles ();
1079 g_assert (thr_ret == 0);
1082 handle = handles[i];
1083 idx = GPOINTER_TO_UINT(handle);
1085 thr_ret = _wapi_handle_unlock_handle (handle);
1086 g_assert (thr_ret == 0);
1089 /* If iter ever reaches 100 the nanosleep will
1090 * return EINVAL immediately, but we have a
1091 * design flaw if that happens.
1095 g_warning ("%s: iteration overflow!",
1101 g_message ("%s: Backing off for %d ms", __func__,
1104 _wapi_handle_spin (10 * iter);
1111 g_message ("%s: Locked all handles", __func__);
1117 for(i=0; i<numhandles; i++) {
1118 gpointer handle = handles[i];
1119 guint32 idx = GPOINTER_TO_UINT(handle);
1121 type = _WAPI_PRIVATE_HANDLES(idx).type;
1123 _wapi_handle_ref (handle);
1126 g_message ("%s: Checking handle %p", __func__, handle);
1129 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1130 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1131 (_WAPI_SHARED_HANDLE(type) &&
1132 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1133 (!_WAPI_SHARED_HANDLE(type) &&
1134 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1138 g_message ("%s: Handle %p signalled", __func__,
1148 g_message ("%s: %d event handles signalled", __func__, count);
1151 if ((waitall == TRUE && count == numhandles) ||
1152 (waitall == FALSE && count > 0)) {
1159 g_message ("%s: Returning %d", __func__, ret);
1167 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1172 thr_ret = _wapi_handle_unlock_shared_handles ();
1173 g_assert (thr_ret == 0);
1175 for(i=0; i<numhandles; i++) {
1176 gpointer handle = handles[i];
1179 g_message ("%s: unlocking handle %p", __func__, handle);
1182 thr_ret = _wapi_handle_unlock_handle (handle);
1183 g_assert (thr_ret == 0);
1187 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout)
1189 struct timespec fake_timeout;
1192 _wapi_calc_timeout (&fake_timeout, 100);
1194 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1195 (fake_timeout.tv_sec == timeout->tv_sec &&
1196 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1197 /* Real timeout is less than 100ms time */
1198 ret=mono_cond_timedwait (cond, mutex, timeout);
1200 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1202 /* Mask the fake timeout, this will cause
1203 * another poll if the cond was not really signaled
1205 if (ret==ETIMEDOUT) {
1213 int _wapi_handle_wait_signal (void)
1215 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL);
1218 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1220 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout);
1223 int _wapi_handle_wait_signal_handle (gpointer handle)
1226 g_message ("%s: waiting for %p", __func__, handle);
1229 return _wapi_handle_timedwait_signal_handle (handle, NULL);
1232 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1233 struct timespec *timeout)
1236 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1237 _wapi_handle_typename[_wapi_handle_type (handle)]);
1240 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1241 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1244 if (timeout != NULL) {
1245 struct timespec fake_timeout;
1246 _wapi_calc_timeout (&fake_timeout, 100);
1248 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1249 (fake_timeout.tv_sec == timeout->tv_sec &&
1250 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1251 /* FIXME: Real timeout is less than
1252 * 100ms time, but is it really worth
1253 * calculating to the exact ms?
1255 _wapi_handle_spin (100);
1257 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1264 _wapi_handle_spin (100);
1268 guint32 idx = GPOINTER_TO_UINT(handle);
1269 return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout);
1273 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1274 guint32 new_sharemode,
1276 guint32 *old_sharemode,
1277 guint32 *old_access,
1278 struct _WapiFileShare **share_info)
1280 struct _WapiFileShare *file_share;
1281 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1282 int thr_ret, i, first_unused = -1;
1283 gboolean exists = FALSE;
1285 /* Prevents entries from expiring under us as we search
1287 thr_ret = _wapi_handle_lock_shared_handles ();
1288 g_assert (thr_ret == 0);
1290 /* Prevent new entries racing with us */
1291 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1292 g_assert (thr_ret == 0);
1294 /* If a linear scan gets too slow we'll have to fit a hash
1295 * table onto the shared mem backing store
1298 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1299 file_share = &_wapi_fileshare_layout->share_info[i];
1301 /* Make a note of an unused slot, in case we need to
1304 if (first_unused == -1 && file_share->handle_refs == 0) {
1309 if (file_share->handle_refs == 0) {
1313 if (file_share->device == device &&
1314 file_share->inode == inode) {
1315 *old_sharemode = file_share->sharemode;
1316 *old_access = file_share->access;
1317 *share_info = file_share;
1319 /* Increment the reference count while we
1320 * still have sole access to the shared area.
1321 * This makes the increment atomic wrt
1324 InterlockedIncrement (&file_share->handle_refs);
1332 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1335 if (first_unused == -1) {
1336 file_share = &_wapi_fileshare_layout->share_info[++i];
1337 _wapi_fileshare_layout->hwm = i;
1339 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1342 file_share->device = device;
1343 file_share->inode = inode;
1344 file_share->opened_by_pid = getpid ();
1345 file_share->sharemode = new_sharemode;
1346 file_share->access = new_access;
1347 file_share->handle_refs = 1;
1348 *share_info = file_share;
1352 if (*share_info != NULL) {
1353 InterlockedExchange (&(*share_info)->timestamp, now);
1356 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1358 _wapi_handle_unlock_shared_handles ();
1363 /* If we don't have the info in /proc, check if the process that
1364 * opened this share info is still there (it's not a perfect method,
1367 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1369 if (kill (share_info->opened_by_pid, 0) == -1 &&
1372 /* It's gone completely (or there's a new process
1373 * owned by someone else) so mark this share info as
1377 g_message ("%s: Didn't find it, destroying entry", __func__);
1380 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1384 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1385 * question. If there are none, reset the share info.
1387 * This implementation is Linux-specific; legacy systems will have to
1388 * implement their own ways of finding out if a particular file is
1389 * open by a process.
1391 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1393 gboolean found = FALSE, proc_fds = FALSE;
1394 pid_t self = getpid();
1398 /* Prevents entries from expiring under us if we remove this
1401 thr_ret = _wapi_handle_lock_shared_handles ();
1402 g_assert (thr_ret == 0);
1404 /* Prevent new entries racing with us */
1405 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1406 g_assert (thr_ret == 0);
1408 /* If there is no /proc, there's nothing more we can do here */
1409 if (access ("/proc", F_OK) == -1) {
1410 _wapi_handle_check_share_by_pid (share_info);
1414 /* If there's another handle that thinks it owns this fd, then even
1415 * if the fd has been closed behind our back consider it still owned.
1416 * See bugs 75764 and 75891
1418 for (i = 0; i < _wapi_fd_reserve; i++) {
1419 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1422 handle->type == WAPI_HANDLE_FILE) {
1423 struct _WapiHandle_file *file_handle = &handle->u.file;
1425 if (file_handle->share_info == share_info) {
1427 g_message ("%s: handle 0x%x has this file open!",
1436 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1437 struct _WapiHandleShared *shared;
1438 struct _WapiHandle_process *process_handle;
1440 shared = &_wapi_shared_layout->handles[i];
1442 if (shared->type == WAPI_HANDLE_PROCESS) {
1444 struct dirent *fd_entry;
1445 char subdir[_POSIX_PATH_MAX];
1447 process_handle = &shared->u.process;
1448 pid = process_handle->id;
1450 /* Look in /proc/<pid>/fd/ but ignore
1451 * /proc/<our pid>/fd/<fd>, as we have the
1454 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1457 fd_dir = opendir (subdir);
1458 if (fd_dir == NULL) {
1463 g_message ("%s: Looking in %s", __func__, subdir);
1468 while ((fd_entry = readdir (fd_dir)) != NULL) {
1469 char path[_POSIX_PATH_MAX];
1470 struct stat link_stat;
1472 if (!strcmp (fd_entry->d_name, ".") ||
1473 !strcmp (fd_entry->d_name, "..") ||
1475 fd == atoi (fd_entry->d_name))) {
1479 g_snprintf (path, _POSIX_PATH_MAX,
1480 "/proc/%d/fd/%s", pid,
1483 stat (path, &link_stat);
1484 if (link_stat.st_dev == share_info->device &&
1485 link_stat.st_ino == share_info->inode) {
1487 g_message ("%s: Found it at %s",
1499 if (proc_fds == FALSE) {
1500 _wapi_handle_check_share_by_pid (share_info);
1501 } else if (found == FALSE) {
1502 /* Blank out this entry, as it is stale */
1504 g_message ("%s: Didn't find it, destroying entry", __func__);
1507 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1511 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1513 _wapi_handle_unlock_shared_handles ();
1516 void _wapi_handle_dump (void)
1518 struct _WapiHandleUnshared *handle_data;
1522 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1523 (void *)&scan_mutex);
1524 thr_ret = mono_mutex_lock (&scan_mutex);
1525 g_assert (thr_ret == 0);
1527 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1528 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1529 handle_data = &_wapi_private_handles [i][k];
1531 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1535 g_print ("%3x [%7s] %s %d ",
1536 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1537 _wapi_handle_typename[handle_data->type],
1538 handle_data->signalled?"Sg":"Un",
1540 handle_details[handle_data->type](&handle_data->u);
1545 thr_ret = mono_mutex_unlock (&scan_mutex);
1546 g_assert (thr_ret == 0);
1547 pthread_cleanup_pop (0);
1550 static void _wapi_shared_details (gpointer handle_info)
1552 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1554 g_print ("offset: 0x%x", shared->offset);
1557 void _wapi_handle_update_refs (void)
1561 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1563 thr_ret = _wapi_handle_lock_shared_handles ();
1564 g_assert (thr_ret == 0);
1566 /* Prevent file share entries racing with us */
1567 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1568 g_assert(thr_ret == 0);
1570 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1571 (void *)&scan_mutex);
1572 thr_ret = mono_mutex_lock (&scan_mutex);
1574 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1575 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1576 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1578 if (_WAPI_SHARED_HANDLE(handle->type)) {
1579 struct _WapiHandleShared *shared_data;
1582 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__,
1583 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1586 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
1589 g_message ("%s: (%d) Updating timestamp of handle 0x%x",
1591 handle->u.shared.offset);
1594 InterlockedExchange (&shared_data->timestamp,
1596 } else if (handle->type == WAPI_HANDLE_FILE) {
1597 struct _WapiHandle_file *file_handle = &handle->u.file;
1600 g_message ("%s: (%d) handle 0x%x is FILE", __func__,
1601 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1604 g_assert (file_handle->share_info != NULL);
1607 g_message ("%s: (%d) Inc refs on fileshare 0x%x",
1609 (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1612 InterlockedExchange (&file_handle->share_info->timestamp, now);
1617 thr_ret = mono_mutex_unlock (&scan_mutex);
1618 g_assert (thr_ret == 0);
1619 pthread_cleanup_pop (0);
1621 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1623 _wapi_handle_unlock_shared_handles ();