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 (struct _WapiHandleShared *handle,
143 gpointer handle_specific)
146 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
147 handle->signalled = FALSE;
148 handle->handle_refs = 1;
150 if (handle_specific != NULL) {
151 memcpy (&handle->u, handle_specific, sizeof (handle->u));
155 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
156 WapiHandleType type, gpointer handle_specific)
161 handle->signalled = FALSE;
164 if (!_WAPI_SHARED_HANDLE(type)) {
165 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
166 g_assert (thr_ret == 0);
168 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
169 g_assert (thr_ret == 0);
171 if (handle_specific != NULL) {
172 memcpy (&handle->u, handle_specific,
178 static guint32 _wapi_handle_new_shared (WapiHandleType type,
179 gpointer handle_specific)
182 static guint32 last = 1;
185 /* Leave the first slot empty as a guard */
187 /* FIXME: expandable array */
188 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
189 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
191 if(handle->type == WAPI_HANDLE_UNUSED) {
192 thr_ret = _wapi_handle_lock_shared_handles ();
193 g_assert (thr_ret == 0);
195 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
198 _wapi_handle_init_shared (handle, type,
201 _wapi_handle_unlock_shared_handles ();
205 /* Someone else beat us to it, just
210 _wapi_handle_unlock_shared_handles ();
215 /* Try again from the beginning */
220 /* Will need to expand the array. The caller will sort it out */
226 * _wapi_handle_new_internal:
227 * @type: Init handle to this type
229 * Search for a free handle and initialize it. Return the handle on
230 * success and 0 on failure. This is only called from
231 * _wapi_handle_new, and scan_mutex must be held.
233 static guint32 _wapi_handle_new_internal (WapiHandleType type,
234 gpointer handle_specific)
237 static guint32 last = 0;
238 gboolean retry = FALSE;
240 /* A linear scan should be fast enough. Start from the last
241 * allocation, assuming that handles are allocated more often
242 * than they're freed. Leave the space reserved for file
246 if (last < _wapi_fd_reserve) {
247 last = _wapi_fd_reserve;
254 for(i = SLOT_INDEX (count); _wapi_private_handles [i] != NULL; i++) {
255 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
256 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
258 if(handle->type == WAPI_HANDLE_UNUSED) {
261 _wapi_handle_init (handle, type, handle_specific);
268 if(retry && last > _wapi_fd_reserve) {
269 /* Try again from the beginning */
270 last = _wapi_fd_reserve;
274 /* Will need to expand the array. The caller will sort it out */
279 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
281 guint32 handle_idx = 0;
285 mono_once (&shared_init_once, shared_init);
288 g_message ("%s: Creating new handle of type %s", __func__,
289 _wapi_handle_typename[type]);
292 g_assert(!_WAPI_FD_HANDLE(type));
294 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
295 (void *)&scan_mutex);
296 thr_ret = mono_mutex_lock (&scan_mutex);
297 g_assert (thr_ret == 0);
299 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
300 /* Try and expand the array, and have another go */
301 int idx = SLOT_INDEX (_wapi_private_handle_count);
302 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
303 _WAPI_HANDLE_INITIAL_COUNT);
305 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
308 thr_ret = mono_mutex_unlock (&scan_mutex);
309 g_assert (thr_ret == 0);
310 pthread_cleanup_pop (0);
312 /* Make sure we left the space for fd mappings */
313 g_assert (handle_idx >= _wapi_fd_reserve);
315 handle = GUINT_TO_POINTER (handle_idx);
318 g_message ("%s: Allocated new handle %p", __func__, handle);
321 if (_WAPI_SHARED_HANDLE(type)) {
322 /* Add the shared section too */
325 ref = _wapi_handle_new_shared (type, handle_specific);
327 _wapi_handle_collect ();
328 ref = _wapi_handle_new_shared (type, handle_specific);
330 /* FIXME: grow the arrays */
331 handle = _WAPI_HANDLE_INVALID;
336 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
338 g_message ("%s: New shared handle at offset 0x%x", __func__,
347 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
350 guint32 handle_idx = 0;
351 gpointer handle = INVALID_HANDLE_VALUE;
353 struct _WapiHandleShared *shared;
354 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
356 mono_once (&shared_init_once, shared_init);
359 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
360 _wapi_handle_typename[type], offset);
363 g_assert(!_WAPI_FD_HANDLE(type));
364 g_assert(_WAPI_SHARED_HANDLE(type));
365 g_assert(offset != 0);
367 shared = &_wapi_shared_layout->handles[offset];
369 /* Bump up the timestamp for this offset */
370 InterlockedExchange (&shared->timestamp, now);
373 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
374 (void *)&scan_mutex);
375 thr_ret = mono_mutex_lock (&scan_mutex);
376 g_assert (thr_ret == 0);
378 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
379 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
380 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
382 if (handle_data->type == type &&
383 handle_data->u.shared.offset == offset) {
384 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
385 goto first_pass_done;
391 thr_ret = mono_mutex_unlock (&scan_mutex);
392 g_assert (thr_ret == 0);
393 pthread_cleanup_pop (0);
395 if (handle != INVALID_HANDLE_VALUE) {
396 _wapi_handle_ref (handle);
399 g_message ("%s: Returning old handle %p referencing 0x%x",
400 __func__, handle, offset);
405 /* Prevent entries expiring under us as we search */
406 thr_ret = _wapi_handle_lock_shared_handles ();
407 g_assert (thr_ret == 0);
409 if (shared->type == WAPI_HANDLE_UNUSED) {
410 /* Someone deleted this handle while we were working */
414 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
415 (void *)&scan_mutex);
416 thr_ret = mono_mutex_lock (&scan_mutex);
417 g_assert (thr_ret == 0);
419 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
420 /* Try and expand the array, and have another go */
421 int idx = SLOT_INDEX (_wapi_private_handle_count);
422 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
423 _WAPI_HANDLE_INITIAL_COUNT);
425 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
428 thr_ret = mono_mutex_unlock (&scan_mutex);
429 g_assert (thr_ret == 0);
430 pthread_cleanup_pop (0);
432 /* Make sure we left the space for fd mappings */
433 g_assert (handle_idx >= _wapi_fd_reserve);
435 handle = GUINT_TO_POINTER (handle_idx);
437 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
438 InterlockedIncrement (&shared->handle_refs);
441 g_message ("%s: Allocated new handle %p referencing 0x%x", __func__,
446 _wapi_handle_unlock_shared_handles ();
451 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
452 gpointer handle_specific)
454 struct _WapiHandleUnshared *handle;
457 mono_once (&shared_init_once, shared_init);
460 g_message ("%s: Creating new handle of type %s", __func__,
461 _wapi_handle_typename[type]);
464 g_assert(_WAPI_FD_HANDLE(type));
465 g_assert(!_WAPI_SHARED_HANDLE(type));
467 if (fd >= _wapi_fd_reserve) {
469 g_message ("%s: fd %d is too big", __func__, fd);
472 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
475 handle = &_WAPI_PRIVATE_HANDLES(fd);
477 if (handle->type != WAPI_HANDLE_UNUSED) {
479 g_message ("%s: fd %d is already in use!", __func__, fd);
481 /* FIXME: clean up this handle? We can't do anything
482 * with the fd, cos thats the new one
487 g_message ("%s: Assigning new fd handle %d", __func__, fd);
490 /* Prevent file share entries racing with us, when the file
491 * handle is only half initialised
493 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
494 g_assert(thr_ret == 0);
496 _wapi_handle_init (handle, type, handle_specific);
498 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
500 return(GUINT_TO_POINTER(fd));
503 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
504 gpointer *handle_specific)
506 struct _WapiHandleUnshared *handle_data;
507 guint32 handle_idx = GPOINTER_TO_UINT(handle);
509 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
511 if (handle_data->type != type) {
515 if (handle_specific == NULL) {
519 if (_WAPI_SHARED_HANDLE(type)) {
520 struct _WapiHandle_shared_ref *ref;
521 struct _WapiHandleShared *shared_handle_data;
523 ref = &handle_data->u.shared;
524 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
526 if (shared_handle_data->type != type) {
527 /* The handle must have been deleted on us
532 *handle_specific = &shared_handle_data->u;
534 *handle_specific = &handle_data->u;
541 _wapi_handle_foreach (WapiHandleType type,
542 gboolean (*on_each)(gpointer test, gpointer user),
545 struct _WapiHandleUnshared *handle_data = NULL;
550 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
551 (void *)&scan_mutex);
552 thr_ret = mono_mutex_lock (&scan_mutex);
553 g_assert (thr_ret == 0);
555 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
556 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
557 handle_data = &_wapi_private_handles [i][k];
559 if (handle_data->type == type) {
560 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
561 if (on_each (ret, user_data) == TRUE)
567 thr_ret = mono_mutex_unlock (&scan_mutex);
568 g_assert (thr_ret == 0);
569 pthread_cleanup_pop (0);
572 /* This might list some shared handles twice if they are already
573 * opened by this process, and the check function returns FALSE the
574 * first time. Shared handles that are created during the search are
575 * unreffed if the check function returns FALSE, so callers must not
576 * rely on the handle persisting (unless the check function returns
579 gpointer _wapi_search_handle (WapiHandleType type,
580 gboolean (*check)(gpointer test, gpointer user),
582 gpointer *handle_specific)
584 struct _WapiHandleUnshared *handle_data = NULL;
585 struct _WapiHandleShared *shared;
588 gboolean found = FALSE;
591 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
592 (void *)&scan_mutex);
593 thr_ret = mono_mutex_lock (&scan_mutex);
594 g_assert (thr_ret == 0);
596 for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) {
597 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
598 handle_data = &_wapi_private_handles [i][k];
600 if (handle_data->type == type) {
601 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
602 if (check (ret, user_data) == TRUE) {
610 thr_ret = mono_mutex_unlock (&scan_mutex);
611 g_assert (thr_ret == 0);
612 pthread_cleanup_pop (0);
614 if (!found && _WAPI_SHARED_HANDLE (type)) {
615 /* Not found yet, so search the shared memory too */
617 g_message ("%s: Looking at other shared handles...", __func__);
620 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
621 shared = &_wapi_shared_layout->handles[i];
623 if (shared->type == type) {
624 /* Tell new_from_offset to not
625 * timestamp this handle, because
626 * otherwise it will ping every handle
627 * in the list and they will never
630 ret = _wapi_handle_new_from_offset (type, i,
632 if (ret == INVALID_HANDLE_VALUE) {
633 /* This handle was deleted
634 * while we were looking at it
640 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
643 /* It's possible that the shared part
644 * of this handle has now been blown
645 * away (after new_from_offset
646 * successfully opened it,) if its
647 * timestamp is too old. The check
648 * function needs to be aware of this,
649 * and cope if the handle has
652 if (check (ret, user_data) == TRUE) {
653 /* Timestamp this handle, but make
654 * sure it still exists first
656 thr_ret = _wapi_handle_lock_shared_handles ();
657 g_assert (thr_ret == 0);
659 if (shared->type == type) {
660 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
661 InterlockedExchange (&shared->timestamp, now);
664 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
666 _wapi_handle_unlock_shared_handles ();
669 /* It's been deleted,
673 _wapi_handle_unlock_shared_handles ();
677 /* This isn't the handle we're looking
678 * for, so drop the reference we took
679 * in _wapi_handle_new_from_offset ()
681 _wapi_handle_unref (ret);
691 if(handle_specific != NULL) {
692 if (_WAPI_SHARED_HANDLE(type)) {
693 g_assert(shared->type == type);
695 *handle_specific = &shared->u;
697 *handle_specific = &handle_data->u;
705 /* Returns the offset of the metadata array, or -1 on error, or 0 for
706 * not found (0 is not a valid offset)
708 gint32 _wapi_search_handle_namespace (WapiHandleType type,
711 struct _WapiHandleShared *shared_handle_data;
716 g_assert(_WAPI_SHARED_HANDLE(type));
719 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
720 utf8_name, _wapi_handle_typename[type]);
723 thr_ret = _wapi_handle_lock_shared_handles ();
724 g_assert (thr_ret == 0);
726 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
727 WapiSharedNamespace *sharedns;
729 shared_handle_data = &_wapi_shared_layout->handles[i];
731 /* Check mutex, event, semaphore, timer, job and file-mapping
732 * object names. So far only mutex is implemented.
734 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
739 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
742 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
745 g_message ("%s: name is [%s]", __func__, sharedns->name);
748 if (strcmp (sharedns->name, utf8_name) == 0) {
749 if (shared_handle_data->type != type) {
750 /* Its the wrong type, so fail now */
752 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
758 g_message ("%s: handle 0x%x matches name and type", __func__, i);
767 _wapi_handle_unlock_shared_handles ();
772 void _wapi_handle_ref (gpointer handle)
774 guint32 idx = GPOINTER_TO_UINT(handle);
775 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
776 struct _WapiHandleUnshared *handle_data = &_WAPI_PRIVATE_HANDLES(idx);
778 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
779 g_warning ("%s: Attempting to ref unused handle %p", __func__,
784 InterlockedIncrement (&handle_data->ref);
786 /* It's possible for processes to exit before getting around
787 * to updating timestamps in the collection thread, so if a
788 * shared handle is reffed do the timestamp here as well just
791 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
792 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
794 InterlockedExchange (&shared_data->timestamp, now);
798 g_message ("%s: handle %p ref now %d", __func__, handle,
799 _WAPI_PRIVATE_HANDLES(idx).ref);
803 /* The handle must not be locked on entry to this function */
804 void _wapi_handle_unref (gpointer handle)
806 guint32 idx = GPOINTER_TO_UINT(handle);
807 gboolean destroy = FALSE;
810 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
811 g_warning ("%s: Attempting to unref unused handle %p",
816 /* Possible race condition here if another thread refs the
817 * handle between here and setting the type to UNUSED. I
818 * could lock a mutex, but I'm not sure that allowing a handle
819 * reference to reach 0 isn't an application bug anyway.
821 destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
824 g_message ("%s: handle %p ref now %d (destroy %s)", __func__, handle,
825 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
829 /* Need to copy the handle info, reset the slot in the
830 * array, and _only then_ call the close function to
831 * avoid race conditions (eg file descriptors being
832 * closed, and another file being opened getting the
833 * same fd racing the memset())
835 struct _WapiHandleUnshared handle_data;
836 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
837 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
838 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
841 /* If this is a shared handle we need to take
842 * the shared lock outside of the scan_mutex
843 * lock to avoid deadlocks
845 thr_ret = _wapi_handle_lock_shared_handles ();
846 g_assert (thr_ret == 0);
849 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
850 thr_ret = mono_mutex_lock (&scan_mutex);
853 g_message ("%s: Destroying handle %p", __func__, handle);
856 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
857 sizeof (struct _WapiHandleUnshared));
859 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
860 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
862 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
865 /* Destroy the mutex and cond var. We hope nobody
866 * tried to grab them between the handle unlock and
867 * now, but pthreads doesn't have a
868 * "unlock_and_destroy" atomic function.
870 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
871 g_assert (thr_ret == 0);
873 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
874 g_assert (thr_ret == 0);
876 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
878 /* It's possible that this handle is already
879 * pointing at a deleted shared section
881 if (shared->handle_refs > 0) {
882 shared->handle_refs--;
883 if (shared->handle_refs == 0) {
884 memset (shared, '\0', sizeof (struct _WapiHandleShared));
889 thr_ret = mono_mutex_unlock (&scan_mutex);
890 g_assert (thr_ret == 0);
891 pthread_cleanup_pop (0);
894 _wapi_handle_unlock_shared_handles ();
897 if (close_func != NULL) {
898 close_func (handle, &handle_data.u);
903 void _wapi_handle_register_capabilities (WapiHandleType type,
904 WapiHandleCapability caps)
906 handle_caps[type] = caps;
909 gboolean _wapi_handle_test_capabilities (gpointer handle,
910 WapiHandleCapability caps)
912 guint32 idx = GPOINTER_TO_UINT(handle);
915 type = _WAPI_PRIVATE_HANDLES(idx).type;
918 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
919 handle_caps[type], caps, handle_caps[type] & caps);
922 return((handle_caps[type] & caps) != 0);
925 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
927 if (handle_ops[type] != NULL &&
928 handle_ops[type]->close != NULL) {
929 return (handle_ops[type]->close);
935 void _wapi_handle_ops_close (gpointer handle, gpointer data)
937 guint32 idx = GPOINTER_TO_UINT(handle);
940 type = _WAPI_PRIVATE_HANDLES(idx).type;
942 if (handle_ops[type] != NULL &&
943 handle_ops[type]->close != NULL) {
944 handle_ops[type]->close (handle, data);
948 void _wapi_handle_ops_signal (gpointer handle)
950 guint32 idx = GPOINTER_TO_UINT(handle);
953 type = _WAPI_PRIVATE_HANDLES(idx).type;
955 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
956 handle_ops[type]->signal (handle);
960 gboolean _wapi_handle_ops_own (gpointer handle)
962 guint32 idx = GPOINTER_TO_UINT(handle);
965 type = _WAPI_PRIVATE_HANDLES(idx).type;
967 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
968 return(handle_ops[type]->own_handle (handle));
974 gboolean _wapi_handle_ops_isowned (gpointer handle)
976 guint32 idx = GPOINTER_TO_UINT(handle);
979 type = _WAPI_PRIVATE_HANDLES(idx).type;
981 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
982 return(handle_ops[type]->is_owned (handle));
988 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
990 guint32 idx = GPOINTER_TO_UINT(handle);
993 type = _WAPI_PRIVATE_HANDLES(idx).type;
995 if (handle_ops[type] != NULL &&
996 handle_ops[type]->special_wait != NULL) {
997 return(handle_ops[type]->special_wait (handle, timeout));
1006 * @handle: The handle to release
1008 * Closes and invalidates @handle, releasing any resources it
1009 * consumes. When the last handle to a temporary or non-persistent
1010 * object is closed, that object can be deleted. Closing the same
1011 * handle twice is an error.
1013 * Return value: %TRUE on success, %FALSE otherwise.
1015 gboolean CloseHandle(gpointer handle)
1017 _wapi_handle_unref (handle);
1022 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1028 guint32 count, i, iter=0;
1031 WapiHandleType type;
1033 /* Lock all the handles, with backoff */
1035 thr_ret = _wapi_handle_lock_shared_handles ();
1036 g_assert (thr_ret == 0);
1038 for(i=0; i<numhandles; i++) {
1039 gpointer handle = handles[i];
1040 guint32 idx = GPOINTER_TO_UINT(handle);
1043 g_message ("%s: attempting to lock %p", __func__, handle);
1046 type = _WAPI_PRIVATE_HANDLES(idx).type;
1048 thr_ret = _wapi_handle_trylock_handle (handle);
1054 g_message ("%s: attempt failed for %p: %s", __func__,
1055 handle, strerror (thr_ret));
1058 thr_ret = _wapi_handle_unlock_shared_handles ();
1059 g_assert (thr_ret == 0);
1062 handle = handles[i];
1063 idx = GPOINTER_TO_UINT(handle);
1065 thr_ret = _wapi_handle_unlock_handle (handle);
1066 g_assert (thr_ret == 0);
1069 /* If iter ever reaches 100 the nanosleep will
1070 * return EINVAL immediately, but we have a
1071 * design flaw if that happens.
1075 g_warning ("%s: iteration overflow!",
1081 g_message ("%s: Backing off for %d ms", __func__,
1084 _wapi_handle_spin (10 * iter);
1091 g_message ("%s: Locked all handles", __func__);
1097 for(i=0; i<numhandles; i++) {
1098 gpointer handle = handles[i];
1099 guint32 idx = GPOINTER_TO_UINT(handle);
1101 type = _WAPI_PRIVATE_HANDLES(idx).type;
1103 _wapi_handle_ref (handle);
1106 g_message ("%s: Checking handle %p", __func__, handle);
1109 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1110 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1111 (_WAPI_SHARED_HANDLE(type) &&
1112 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1113 (!_WAPI_SHARED_HANDLE(type) &&
1114 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1118 g_message ("%s: Handle %p signalled", __func__,
1128 g_message ("%s: %d event handles signalled", __func__, count);
1131 if ((waitall == TRUE && count == numhandles) ||
1132 (waitall == FALSE && count > 0)) {
1139 g_message ("%s: Returning %d", __func__, ret);
1147 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1152 thr_ret = _wapi_handle_unlock_shared_handles ();
1153 g_assert (thr_ret == 0);
1155 for(i=0; i<numhandles; i++) {
1156 gpointer handle = handles[i];
1159 g_message ("%s: unlocking handle %p", __func__, handle);
1162 thr_ret = _wapi_handle_unlock_handle (handle);
1163 g_assert (thr_ret == 0);
1167 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout)
1169 struct timespec fake_timeout;
1172 _wapi_calc_timeout (&fake_timeout, 100);
1174 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1175 (fake_timeout.tv_sec == timeout->tv_sec &&
1176 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1177 /* Real timeout is less than 100ms time */
1178 ret=mono_cond_timedwait (cond, mutex, timeout);
1180 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1182 /* Mask the fake timeout, this will cause
1183 * another poll if the cond was not really signaled
1185 if (ret==ETIMEDOUT) {
1193 int _wapi_handle_wait_signal (void)
1195 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL);
1198 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1200 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout);
1203 int _wapi_handle_wait_signal_handle (gpointer handle)
1206 g_message ("%s: waiting for %p", __func__, handle);
1209 return _wapi_handle_timedwait_signal_handle (handle, NULL);
1212 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1213 struct timespec *timeout)
1216 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1217 _wapi_handle_typename[_wapi_handle_type (handle)]);
1220 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1221 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1224 if (timeout != NULL) {
1225 struct timespec fake_timeout;
1226 _wapi_calc_timeout (&fake_timeout, 100);
1228 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1229 (fake_timeout.tv_sec == timeout->tv_sec &&
1230 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1231 /* FIXME: Real timeout is less than
1232 * 100ms time, but is it really worth
1233 * calculating to the exact ms?
1235 _wapi_handle_spin (100);
1237 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1244 _wapi_handle_spin (100);
1248 guint32 idx = GPOINTER_TO_UINT(handle);
1249 return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout);
1253 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1254 guint32 new_sharemode,
1256 guint32 *old_sharemode,
1257 guint32 *old_access,
1258 struct _WapiFileShare **share_info)
1260 struct _WapiFileShare *file_share;
1261 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1262 int thr_ret, i, first_unused = -1;
1263 gboolean exists = FALSE;
1265 /* Prevents entries from expiring under us as we search
1267 thr_ret = _wapi_handle_lock_shared_handles ();
1268 g_assert (thr_ret == 0);
1270 /* Prevent new entries racing with us */
1271 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1272 g_assert (thr_ret == 0);
1274 /* If a linear scan gets too slow we'll have to fit a hash
1275 * table onto the shared mem backing store
1278 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1279 file_share = &_wapi_fileshare_layout->share_info[i];
1281 /* Make a note of an unused slot, in case we need to
1284 if (first_unused == -1 && file_share->handle_refs == 0) {
1289 if (file_share->handle_refs == 0) {
1293 if (file_share->device == device &&
1294 file_share->inode == inode) {
1295 *old_sharemode = file_share->sharemode;
1296 *old_access = file_share->access;
1297 *share_info = file_share;
1299 /* Increment the reference count while we
1300 * still have sole access to the shared area.
1301 * This makes the increment atomic wrt
1304 InterlockedIncrement (&file_share->handle_refs);
1312 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1315 if (first_unused == -1) {
1316 file_share = &_wapi_fileshare_layout->share_info[++i];
1317 _wapi_fileshare_layout->hwm = i;
1319 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1322 file_share->device = device;
1323 file_share->inode = inode;
1324 file_share->opened_by_pid = getpid ();
1325 file_share->sharemode = new_sharemode;
1326 file_share->access = new_access;
1327 file_share->handle_refs = 1;
1328 *share_info = file_share;
1332 if (*share_info != NULL) {
1333 InterlockedExchange (&(*share_info)->timestamp, now);
1336 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1338 _wapi_handle_unlock_shared_handles ();
1343 /* If we don't have the info in /proc, check if the process that
1344 * opened this share info is still there (it's not a perfect method,
1347 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1349 if (kill (share_info->opened_by_pid, 0) == -1 &&
1352 /* It's gone completely (or there's a new process
1353 * owned by someone else) so mark this share info as
1357 g_message ("%s: Didn't find it, destroying entry", __func__);
1360 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1364 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1365 * question. If there are none, reset the share info.
1367 * This implementation is Linux-specific; legacy systems will have to
1368 * implement their own ways of finding out if a particular file is
1369 * open by a process.
1371 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1373 gboolean found = FALSE, proc_fds = FALSE;
1374 pid_t self = getpid();
1378 /* Prevents entries from expiring under us if we remove this
1381 thr_ret = _wapi_handle_lock_shared_handles ();
1382 g_assert (thr_ret == 0);
1384 /* Prevent new entries racing with us */
1385 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1386 g_assert (thr_ret == 0);
1388 /* If there is no /proc, there's nothing more we can do here */
1389 if (access ("/proc", F_OK) == -1) {
1390 _wapi_handle_check_share_by_pid (share_info);
1394 /* If there's another handle that thinks it owns this fd, then even
1395 * if the fd has been closed behind our back consider it still owned.
1396 * See bugs 75764 and 75891
1398 for (i = 0; i < _wapi_fd_reserve; i++) {
1399 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1402 handle->type == WAPI_HANDLE_FILE) {
1403 struct _WapiHandle_file *file_handle = &handle->u.file;
1405 if (file_handle->share_info == share_info) {
1407 g_message ("%s: handle 0x%x has this file open!",
1416 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1417 struct _WapiHandleShared *shared;
1418 struct _WapiHandle_process *process_handle;
1420 shared = &_wapi_shared_layout->handles[i];
1422 if (shared->type == WAPI_HANDLE_PROCESS) {
1424 struct dirent *fd_entry;
1425 char subdir[_POSIX_PATH_MAX];
1427 process_handle = &shared->u.process;
1428 pid = process_handle->id;
1430 /* Look in /proc/<pid>/fd/ but ignore
1431 * /proc/<our pid>/fd/<fd>, as we have the
1434 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1437 fd_dir = opendir (subdir);
1438 if (fd_dir == NULL) {
1443 g_message ("%s: Looking in %s", __func__, subdir);
1448 while ((fd_entry = readdir (fd_dir)) != NULL) {
1449 char path[_POSIX_PATH_MAX];
1450 struct stat link_stat;
1452 if (!strcmp (fd_entry->d_name, ".") ||
1453 !strcmp (fd_entry->d_name, "..") ||
1455 fd == atoi (fd_entry->d_name))) {
1459 g_snprintf (path, _POSIX_PATH_MAX,
1460 "/proc/%d/fd/%s", pid,
1463 stat (path, &link_stat);
1464 if (link_stat.st_dev == share_info->device &&
1465 link_stat.st_ino == share_info->inode) {
1467 g_message ("%s: Found it at %s",
1479 if (proc_fds == FALSE) {
1480 _wapi_handle_check_share_by_pid (share_info);
1481 } else if (found == FALSE) {
1482 /* Blank out this entry, as it is stale */
1484 g_message ("%s: Didn't find it, destroying entry", __func__);
1487 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1491 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1493 _wapi_handle_unlock_shared_handles ();
1496 void _wapi_handle_dump (void)
1498 struct _WapiHandleUnshared *handle_data;
1502 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1503 (void *)&scan_mutex);
1504 thr_ret = mono_mutex_lock (&scan_mutex);
1505 g_assert (thr_ret == 0);
1507 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1508 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1509 handle_data = &_wapi_private_handles [i][k];
1511 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1515 g_print ("%3x [%7s] %s %d ",
1516 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1517 _wapi_handle_typename[handle_data->type],
1518 handle_data->signalled?"Sg":"Un",
1520 handle_details[handle_data->type](&handle_data->u);
1525 thr_ret = mono_mutex_unlock (&scan_mutex);
1526 g_assert (thr_ret == 0);
1527 pthread_cleanup_pop (0);
1530 static void _wapi_shared_details (gpointer handle_info)
1532 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1534 g_print ("offset: 0x%x", shared->offset);
1537 void _wapi_handle_update_refs (void)
1541 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1543 thr_ret = _wapi_handle_lock_shared_handles ();
1544 g_assert (thr_ret == 0);
1546 /* Prevent file share entries racing with us */
1547 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1548 g_assert(thr_ret == 0);
1550 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1551 (void *)&scan_mutex);
1552 thr_ret = mono_mutex_lock (&scan_mutex);
1554 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1555 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1556 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1558 if (_WAPI_SHARED_HANDLE(handle->type)) {
1559 struct _WapiHandleShared *shared_data;
1562 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__,
1563 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1566 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
1569 g_message ("%s: (%d) Updating timestamp of handle 0x%x",
1571 handle->u.shared.offset);
1574 InterlockedExchange (&shared_data->timestamp,
1576 } else if (handle->type == WAPI_HANDLE_FILE) {
1577 struct _WapiHandle_file *file_handle = &handle->u.file;
1580 g_message ("%s: (%d) handle 0x%x is FILE", __func__,
1581 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1584 g_assert (file_handle->share_info != NULL);
1587 g_message ("%s: (%d) Inc refs on fileshare 0x%x",
1589 (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1592 InterlockedExchange (&file_handle->share_info->timestamp, now);
1597 thr_ret = mono_mutex_unlock (&scan_mutex);
1598 g_assert (thr_ret == 0);
1599 pthread_cleanup_pop (0);
1601 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1603 _wapi_handle_unlock_shared_handles ();