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,
53 &_wapi_namedevent_ops,
56 static void _wapi_shared_details (gpointer handle_info);
58 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
61 _wapi_console_details,
62 _wapi_shared_details, /* thread */
66 NULL, /* Nothing useful to see in a socket handle */
67 NULL, /* Nothing useful to see in a find handle */
68 _wapi_shared_details, /* process */
70 _wapi_shared_details, /* namedmutex */
71 _wapi_shared_details, /* namedsem */
72 _wapi_shared_details, /* namedevent */
75 const char *_wapi_handle_typename[] = {
94 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
95 * If 4M handles are not enough... Oh, well... we will crash.
97 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
98 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
100 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
101 static guint32 _wapi_private_handle_count = 0;
103 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
104 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
106 guint32 _wapi_fd_reserve;
108 mono_mutex_t _wapi_global_signal_mutex;
109 pthread_cond_t _wapi_global_signal_cond;
113 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
115 static mono_once_t shared_init_once = MONO_ONCE_INIT;
116 static void shared_init (void)
121 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
122 == WAPI_HANDLE_COUNT);
124 _wapi_fd_reserve = getdtablesize();
127 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
128 _WAPI_HANDLE_INITIAL_COUNT);
130 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
131 } while(_wapi_fd_reserve > _wapi_private_handle_count);
133 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
134 g_assert (_wapi_shared_layout != NULL);
136 _wapi_shm_semaphores_init ();
138 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
139 g_assert (_wapi_fileshare_layout != NULL);
141 _wapi_collection_init ();
143 thr_ret = pthread_cond_init(&_wapi_global_signal_cond, NULL);
144 g_assert (thr_ret == 0);
146 thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL);
147 g_assert (thr_ret == 0);
150 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
152 gpointer handle_specific)
155 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
156 handle->signalled = FALSE;
157 handle->handle_refs = 1;
159 if (handle_specific != NULL) {
160 memcpy (&handle->u, handle_specific, sizeof (handle->u));
164 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
165 WapiHandleType type, gpointer handle_specific)
170 handle->signalled = FALSE;
173 if (!_WAPI_SHARED_HANDLE(type)) {
174 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
175 g_assert (thr_ret == 0);
177 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
178 g_assert (thr_ret == 0);
180 if (handle_specific != NULL) {
181 memcpy (&handle->u, handle_specific,
187 static guint32 _wapi_handle_new_shared (WapiHandleType type,
188 gpointer handle_specific)
191 static guint32 last = 1;
194 /* Leave the first slot empty as a guard */
196 /* FIXME: expandable array */
197 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
198 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
200 if(handle->type == WAPI_HANDLE_UNUSED) {
201 thr_ret = _wapi_handle_lock_shared_handles ();
202 g_assert (thr_ret == 0);
204 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
207 _wapi_handle_init_shared (handle, type,
210 _wapi_handle_unlock_shared_handles ();
214 /* Someone else beat us to it, just
219 _wapi_handle_unlock_shared_handles ();
224 /* Try again from the beginning */
229 /* Will need to expand the array. The caller will sort it out */
235 * _wapi_handle_new_internal:
236 * @type: Init handle to this type
238 * Search for a free handle and initialize it. Return the handle on
239 * success and 0 on failure. This is only called from
240 * _wapi_handle_new, and scan_mutex must be held.
242 static guint32 _wapi_handle_new_internal (WapiHandleType type,
243 gpointer handle_specific)
246 static guint32 last = 0;
247 gboolean retry = FALSE;
249 /* A linear scan should be fast enough. Start from the last
250 * allocation, assuming that handles are allocated more often
251 * than they're freed. Leave the space reserved for file
255 if (last < _wapi_fd_reserve) {
256 last = _wapi_fd_reserve;
263 for(i = SLOT_INDEX (count); _wapi_private_handles [i] != NULL; i++) {
264 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
265 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
267 if(handle->type == WAPI_HANDLE_UNUSED) {
270 _wapi_handle_init (handle, type, handle_specific);
277 if(retry && last > _wapi_fd_reserve) {
278 /* Try again from the beginning */
279 last = _wapi_fd_reserve;
283 /* Will need to expand the array. The caller will sort it out */
288 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
290 guint32 handle_idx = 0;
294 mono_once (&shared_init_once, shared_init);
297 g_message ("%s: Creating new handle of type %s", __func__,
298 _wapi_handle_typename[type]);
301 g_assert(!_WAPI_FD_HANDLE(type));
303 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
304 (void *)&scan_mutex);
305 thr_ret = mono_mutex_lock (&scan_mutex);
306 g_assert (thr_ret == 0);
308 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
309 /* Try and expand the array, and have another go */
310 int idx = SLOT_INDEX (_wapi_private_handle_count);
311 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
315 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
316 _WAPI_HANDLE_INITIAL_COUNT);
318 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
321 thr_ret = mono_mutex_unlock (&scan_mutex);
322 g_assert (thr_ret == 0);
323 pthread_cleanup_pop (0);
325 if (handle_idx == 0) {
326 /* We ran out of slots */
327 handle = _WAPI_HANDLE_INVALID;
331 /* Make sure we left the space for fd mappings */
332 g_assert (handle_idx >= _wapi_fd_reserve);
334 handle = GUINT_TO_POINTER (handle_idx);
337 g_message ("%s: Allocated new handle %p", __func__, handle);
340 if (_WAPI_SHARED_HANDLE(type)) {
341 /* Add the shared section too */
344 ref = _wapi_handle_new_shared (type, handle_specific);
346 _wapi_handle_collect ();
347 ref = _wapi_handle_new_shared (type, handle_specific);
349 /* FIXME: grow the arrays */
350 handle = _WAPI_HANDLE_INVALID;
355 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
357 g_message ("%s: New shared handle at offset 0x%x", __func__,
366 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
369 guint32 handle_idx = 0;
370 gpointer handle = INVALID_HANDLE_VALUE;
372 struct _WapiHandleShared *shared;
373 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
375 mono_once (&shared_init_once, shared_init);
378 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
379 _wapi_handle_typename[type], offset);
382 g_assert(!_WAPI_FD_HANDLE(type));
383 g_assert(_WAPI_SHARED_HANDLE(type));
384 g_assert(offset != 0);
386 shared = &_wapi_shared_layout->handles[offset];
388 /* Bump up the timestamp for this offset */
389 InterlockedExchange (&shared->timestamp, now);
392 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
393 (void *)&scan_mutex);
394 thr_ret = mono_mutex_lock (&scan_mutex);
395 g_assert (thr_ret == 0);
397 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
398 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
399 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
401 if (handle_data->type == type &&
402 handle_data->u.shared.offset == offset) {
403 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
404 goto first_pass_done;
410 thr_ret = mono_mutex_unlock (&scan_mutex);
411 g_assert (thr_ret == 0);
412 pthread_cleanup_pop (0);
414 if (handle != INVALID_HANDLE_VALUE) {
415 _wapi_handle_ref (handle);
418 g_message ("%s: Returning old handle %p referencing 0x%x",
419 __func__, handle, offset);
424 /* Prevent entries expiring under us as we search */
425 thr_ret = _wapi_handle_lock_shared_handles ();
426 g_assert (thr_ret == 0);
428 if (shared->type == WAPI_HANDLE_UNUSED) {
429 /* Someone deleted this handle while we were working */
433 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
434 (void *)&scan_mutex);
435 thr_ret = mono_mutex_lock (&scan_mutex);
436 g_assert (thr_ret == 0);
438 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
439 /* Try and expand the array, and have another go */
440 int idx = SLOT_INDEX (_wapi_private_handle_count);
441 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
442 _WAPI_HANDLE_INITIAL_COUNT);
444 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
447 thr_ret = mono_mutex_unlock (&scan_mutex);
448 g_assert (thr_ret == 0);
449 pthread_cleanup_pop (0);
451 /* Make sure we left the space for fd mappings */
452 g_assert (handle_idx >= _wapi_fd_reserve);
454 handle = GUINT_TO_POINTER (handle_idx);
456 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
457 InterlockedIncrement (&shared->handle_refs);
460 g_message ("%s: Allocated new handle %p referencing 0x%x", __func__,
465 _wapi_handle_unlock_shared_handles ();
470 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
471 gpointer handle_specific)
473 struct _WapiHandleUnshared *handle;
476 mono_once (&shared_init_once, shared_init);
479 g_message ("%s: Creating new handle of type %s", __func__,
480 _wapi_handle_typename[type]);
483 g_assert(_WAPI_FD_HANDLE(type));
484 g_assert(!_WAPI_SHARED_HANDLE(type));
486 if (fd >= _wapi_fd_reserve) {
488 g_message ("%s: fd %d is too big", __func__, fd);
491 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
494 handle = &_WAPI_PRIVATE_HANDLES(fd);
496 if (handle->type != WAPI_HANDLE_UNUSED) {
498 g_message ("%s: fd %d is already in use!", __func__, fd);
500 /* FIXME: clean up this handle? We can't do anything
501 * with the fd, cos thats the new one
506 g_message ("%s: Assigning new fd handle %d", __func__, fd);
509 /* Prevent file share entries racing with us, when the file
510 * handle is only half initialised
512 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
513 g_assert(thr_ret == 0);
515 _wapi_handle_init (handle, type, handle_specific);
517 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
519 return(GUINT_TO_POINTER(fd));
522 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
523 gpointer *handle_specific)
525 struct _WapiHandleUnshared *handle_data;
526 guint32 handle_idx = GPOINTER_TO_UINT(handle);
528 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
532 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
534 if (handle_data->type != type) {
538 if (handle_specific == NULL) {
542 if (_WAPI_SHARED_HANDLE(type)) {
543 struct _WapiHandle_shared_ref *ref;
544 struct _WapiHandleShared *shared_handle_data;
546 ref = &handle_data->u.shared;
547 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
549 if (shared_handle_data->type != type) {
550 /* The handle must have been deleted on us
555 *handle_specific = &shared_handle_data->u;
557 *handle_specific = &handle_data->u;
564 _wapi_handle_foreach (WapiHandleType type,
565 gboolean (*on_each)(gpointer test, gpointer user),
568 struct _WapiHandleUnshared *handle_data = NULL;
573 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
574 (void *)&scan_mutex);
575 thr_ret = mono_mutex_lock (&scan_mutex);
576 g_assert (thr_ret == 0);
578 for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
579 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
580 handle_data = &_wapi_private_handles [i][k];
582 if (handle_data->type == type) {
583 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
584 if (on_each (ret, user_data) == TRUE)
590 thr_ret = mono_mutex_unlock (&scan_mutex);
591 g_assert (thr_ret == 0);
592 pthread_cleanup_pop (0);
595 /* This might list some shared handles twice if they are already
596 * opened by this process, and the check function returns FALSE the
597 * first time. Shared handles that are created during the search are
598 * unreffed if the check function returns FALSE, so callers must not
599 * rely on the handle persisting (unless the check function returns
602 gpointer _wapi_search_handle (WapiHandleType type,
603 gboolean (*check)(gpointer test, gpointer user),
605 gpointer *handle_specific)
607 struct _WapiHandleUnshared *handle_data = NULL;
608 struct _WapiHandleShared *shared;
611 gboolean found = FALSE;
614 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
615 (void *)&scan_mutex);
616 thr_ret = mono_mutex_lock (&scan_mutex);
617 g_assert (thr_ret == 0);
619 for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) {
620 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
621 handle_data = &_wapi_private_handles [i][k];
623 if (handle_data->type == type) {
624 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
625 if (check (ret, user_data) == TRUE) {
633 thr_ret = mono_mutex_unlock (&scan_mutex);
634 g_assert (thr_ret == 0);
635 pthread_cleanup_pop (0);
637 if (!found && _WAPI_SHARED_HANDLE (type)) {
638 /* Not found yet, so search the shared memory too */
640 g_message ("%s: Looking at other shared handles...", __func__);
643 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
644 shared = &_wapi_shared_layout->handles[i];
646 if (shared->type == type) {
647 /* Tell new_from_offset to not
648 * timestamp this handle, because
649 * otherwise it will ping every handle
650 * in the list and they will never
653 ret = _wapi_handle_new_from_offset (type, i,
655 if (ret == INVALID_HANDLE_VALUE) {
656 /* This handle was deleted
657 * while we were looking at it
663 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
666 /* It's possible that the shared part
667 * of this handle has now been blown
668 * away (after new_from_offset
669 * successfully opened it,) if its
670 * timestamp is too old. The check
671 * function needs to be aware of this,
672 * and cope if the handle has
675 if (check (ret, user_data) == TRUE) {
676 /* Timestamp this handle, but make
677 * sure it still exists first
679 thr_ret = _wapi_handle_lock_shared_handles ();
680 g_assert (thr_ret == 0);
682 if (shared->type == type) {
683 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
684 InterlockedExchange (&shared->timestamp, now);
687 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
689 _wapi_handle_unlock_shared_handles ();
692 /* It's been deleted,
696 _wapi_handle_unlock_shared_handles ();
700 /* This isn't the handle we're looking
701 * for, so drop the reference we took
702 * in _wapi_handle_new_from_offset ()
704 _wapi_handle_unref (ret);
714 if(handle_specific != NULL) {
715 if (_WAPI_SHARED_HANDLE(type)) {
716 g_assert(shared->type == type);
718 *handle_specific = &shared->u;
720 *handle_specific = &handle_data->u;
728 /* Returns the offset of the metadata array, or -1 on error, or 0 for
729 * not found (0 is not a valid offset)
731 gint32 _wapi_search_handle_namespace (WapiHandleType type,
734 struct _WapiHandleShared *shared_handle_data;
739 g_assert(_WAPI_SHARED_HANDLE(type));
742 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
743 utf8_name, _wapi_handle_typename[type]);
746 /* Do a handle collection before starting to look, so that any
747 * stale cruft gets removed
749 _wapi_handle_collect ();
751 thr_ret = _wapi_handle_lock_shared_handles ();
752 g_assert (thr_ret == 0);
754 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
755 WapiSharedNamespace *sharedns;
757 shared_handle_data = &_wapi_shared_layout->handles[i];
759 /* Check mutex, event, semaphore, timer, job and
760 * file-mapping object names. So far only mutex,
761 * semaphore and event are implemented.
763 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
768 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
771 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
774 g_message ("%s: name is [%s]", __func__, sharedns->name);
777 if (strcmp (sharedns->name, utf8_name) == 0) {
778 if (shared_handle_data->type != type) {
779 /* Its the wrong type, so fail now */
781 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
787 g_message ("%s: handle 0x%x matches name and type", __func__, i);
796 _wapi_handle_unlock_shared_handles ();
801 void _wapi_handle_ref (gpointer handle)
803 guint32 idx = GPOINTER_TO_UINT(handle);
804 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
805 struct _WapiHandleUnshared *handle_data;
807 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
811 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
812 g_warning ("%s: Attempting to ref unused handle %p", __func__,
817 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
819 InterlockedIncrement (&handle_data->ref);
821 /* It's possible for processes to exit before getting around
822 * to updating timestamps in the collection thread, so if a
823 * shared handle is reffed do the timestamp here as well just
826 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
827 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
829 InterlockedExchange (&shared_data->timestamp, now);
833 g_message ("%s: %s handle %p ref now %d", __func__,
834 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
836 _WAPI_PRIVATE_HANDLES(idx).ref);
840 /* The handle must not be locked on entry to this function */
841 void _wapi_handle_unref (gpointer handle)
843 guint32 idx = GPOINTER_TO_UINT(handle);
844 gboolean destroy = FALSE;
847 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
851 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
852 g_warning ("%s: Attempting to unref unused handle %p",
857 /* Possible race condition here if another thread refs the
858 * handle between here and setting the type to UNUSED. I
859 * could lock a mutex, but I'm not sure that allowing a handle
860 * reference to reach 0 isn't an application bug anyway.
862 destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
865 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
866 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
868 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
872 /* Need to copy the handle info, reset the slot in the
873 * array, and _only then_ call the close function to
874 * avoid race conditions (eg file descriptors being
875 * closed, and another file being opened getting the
876 * same fd racing the memset())
878 struct _WapiHandleUnshared handle_data;
879 struct _WapiHandleShared shared_handle_data;
880 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
881 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
882 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
885 /* If this is a shared handle we need to take
886 * the shared lock outside of the scan_mutex
887 * lock to avoid deadlocks
889 thr_ret = _wapi_handle_lock_shared_handles ();
890 g_assert (thr_ret == 0);
893 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
894 thr_ret = mono_mutex_lock (&scan_mutex);
897 g_message ("%s: Destroying handle %p", __func__, handle);
900 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
901 sizeof (struct _WapiHandleUnshared));
903 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
904 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
906 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
909 /* Destroy the mutex and cond var. We hope nobody
910 * tried to grab them between the handle unlock and
911 * now, but pthreads doesn't have a
912 * "unlock_and_destroy" atomic function.
914 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
915 g_assert (thr_ret == 0);
917 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
918 g_assert (thr_ret == 0);
920 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
922 memcpy (&shared_handle_data, shared,
923 sizeof (struct _WapiHandleShared));
925 /* It's possible that this handle is already
926 * pointing at a deleted shared section
928 if (shared->handle_refs > 0) {
929 shared->handle_refs--;
930 if (shared->handle_refs == 0) {
931 memset (shared, '\0', sizeof (struct _WapiHandleShared));
936 thr_ret = mono_mutex_unlock (&scan_mutex);
937 g_assert (thr_ret == 0);
938 pthread_cleanup_pop (0);
941 _wapi_handle_unlock_shared_handles ();
944 if (close_func != NULL) {
946 close_func (handle, &shared_handle_data.u);
948 close_func (handle, &handle_data.u);
954 void _wapi_handle_register_capabilities (WapiHandleType type,
955 WapiHandleCapability caps)
957 handle_caps[type] = caps;
960 gboolean _wapi_handle_test_capabilities (gpointer handle,
961 WapiHandleCapability caps)
963 guint32 idx = GPOINTER_TO_UINT(handle);
966 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
970 type = _WAPI_PRIVATE_HANDLES(idx).type;
973 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
974 handle_caps[type], caps, handle_caps[type] & caps);
977 return((handle_caps[type] & caps) != 0);
980 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
982 if (handle_ops[type] != NULL &&
983 handle_ops[type]->close != NULL) {
984 return (handle_ops[type]->close);
990 void _wapi_handle_ops_close (gpointer handle, gpointer data)
992 guint32 idx = GPOINTER_TO_UINT(handle);
995 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
999 type = _WAPI_PRIVATE_HANDLES(idx).type;
1001 if (handle_ops[type] != NULL &&
1002 handle_ops[type]->close != NULL) {
1003 handle_ops[type]->close (handle, data);
1007 void _wapi_handle_ops_signal (gpointer handle)
1009 guint32 idx = GPOINTER_TO_UINT(handle);
1010 WapiHandleType type;
1012 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1016 type = _WAPI_PRIVATE_HANDLES(idx).type;
1018 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1019 handle_ops[type]->signal (handle);
1023 gboolean _wapi_handle_ops_own (gpointer handle)
1025 guint32 idx = GPOINTER_TO_UINT(handle);
1026 WapiHandleType type;
1028 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1032 type = _WAPI_PRIVATE_HANDLES(idx).type;
1034 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1035 return(handle_ops[type]->own_handle (handle));
1041 gboolean _wapi_handle_ops_isowned (gpointer handle)
1043 guint32 idx = GPOINTER_TO_UINT(handle);
1044 WapiHandleType type;
1046 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1050 type = _WAPI_PRIVATE_HANDLES(idx).type;
1052 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1053 return(handle_ops[type]->is_owned (handle));
1059 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1061 guint32 idx = GPOINTER_TO_UINT(handle);
1062 WapiHandleType type;
1064 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1065 return(WAIT_FAILED);
1068 type = _WAPI_PRIVATE_HANDLES(idx).type;
1070 if (handle_ops[type] != NULL &&
1071 handle_ops[type]->special_wait != NULL) {
1072 return(handle_ops[type]->special_wait (handle, timeout));
1074 return(WAIT_FAILED);
1081 * @handle: The handle to release
1083 * Closes and invalidates @handle, releasing any resources it
1084 * consumes. When the last handle to a temporary or non-persistent
1085 * object is closed, that object can be deleted. Closing the same
1086 * handle twice is an error.
1088 * Return value: %TRUE on success, %FALSE otherwise.
1090 gboolean CloseHandle(gpointer handle)
1092 _wapi_handle_unref (handle);
1097 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1103 guint32 count, i, iter=0;
1106 WapiHandleType type;
1108 /* Lock all the handles, with backoff */
1110 thr_ret = _wapi_handle_lock_shared_handles ();
1111 g_assert (thr_ret == 0);
1113 for(i=0; i<numhandles; i++) {
1114 gpointer handle = handles[i];
1115 guint32 idx = GPOINTER_TO_UINT(handle);
1118 g_message ("%s: attempting to lock %p", __func__, handle);
1121 type = _WAPI_PRIVATE_HANDLES(idx).type;
1123 thr_ret = _wapi_handle_trylock_handle (handle);
1129 g_message ("%s: attempt failed for %p: %s", __func__,
1130 handle, strerror (thr_ret));
1133 thr_ret = _wapi_handle_unlock_shared_handles ();
1134 g_assert (thr_ret == 0);
1137 handle = handles[i];
1138 idx = GPOINTER_TO_UINT(handle);
1140 thr_ret = _wapi_handle_unlock_handle (handle);
1141 g_assert (thr_ret == 0);
1144 /* If iter ever reaches 100 the nanosleep will
1145 * return EINVAL immediately, but we have a
1146 * design flaw if that happens.
1150 g_warning ("%s: iteration overflow!",
1156 g_message ("%s: Backing off for %d ms", __func__,
1159 _wapi_handle_spin (10 * iter);
1166 g_message ("%s: Locked all handles", __func__);
1172 for(i=0; i<numhandles; i++) {
1173 gpointer handle = handles[i];
1174 guint32 idx = GPOINTER_TO_UINT(handle);
1176 type = _WAPI_PRIVATE_HANDLES(idx).type;
1178 _wapi_handle_ref (handle);
1181 g_message ("%s: Checking handle %p", __func__, handle);
1184 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1185 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1186 (_WAPI_SHARED_HANDLE(type) &&
1187 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1188 (!_WAPI_SHARED_HANDLE(type) &&
1189 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1193 g_message ("%s: Handle %p signalled", __func__,
1203 g_message ("%s: %d event handles signalled", __func__, count);
1206 if ((waitall == TRUE && count == numhandles) ||
1207 (waitall == FALSE && count > 0)) {
1214 g_message ("%s: Returning %d", __func__, ret);
1222 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1227 thr_ret = _wapi_handle_unlock_shared_handles ();
1228 g_assert (thr_ret == 0);
1230 for(i=0; i<numhandles; i++) {
1231 gpointer handle = handles[i];
1234 g_message ("%s: unlocking handle %p", __func__, handle);
1237 thr_ret = _wapi_handle_unlock_handle (handle);
1238 g_assert (thr_ret == 0);
1242 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout)
1244 struct timespec fake_timeout;
1247 _wapi_calc_timeout (&fake_timeout, 100);
1249 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1250 (fake_timeout.tv_sec == timeout->tv_sec &&
1251 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1252 /* Real timeout is less than 100ms time */
1253 ret=mono_cond_timedwait (cond, mutex, timeout);
1255 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1257 /* Mask the fake timeout, this will cause
1258 * another poll if the cond was not really signaled
1260 if (ret==ETIMEDOUT) {
1268 int _wapi_handle_wait_signal (void)
1270 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL);
1273 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1275 return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout);
1278 int _wapi_handle_wait_signal_handle (gpointer handle)
1281 g_message ("%s: waiting for %p", __func__, handle);
1284 return _wapi_handle_timedwait_signal_handle (handle, NULL);
1287 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1288 struct timespec *timeout)
1291 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1292 _wapi_handle_typename[_wapi_handle_type (handle)]);
1295 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1296 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1299 if (timeout != NULL) {
1300 struct timespec fake_timeout;
1301 _wapi_calc_timeout (&fake_timeout, 100);
1303 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1304 (fake_timeout.tv_sec == timeout->tv_sec &&
1305 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1306 /* FIXME: Real timeout is less than
1307 * 100ms time, but is it really worth
1308 * calculating to the exact ms?
1310 _wapi_handle_spin (100);
1312 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1319 _wapi_handle_spin (100);
1323 guint32 idx = GPOINTER_TO_UINT(handle);
1324 return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout);
1328 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1329 guint32 new_sharemode,
1331 guint32 *old_sharemode,
1332 guint32 *old_access,
1333 struct _WapiFileShare **share_info)
1335 struct _WapiFileShare *file_share;
1336 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1337 int thr_ret, i, first_unused = -1;
1338 gboolean exists = FALSE;
1340 /* Prevents entries from expiring under us as we search
1342 thr_ret = _wapi_handle_lock_shared_handles ();
1343 g_assert (thr_ret == 0);
1345 /* Prevent new entries racing with us */
1346 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1347 g_assert (thr_ret == 0);
1349 /* If a linear scan gets too slow we'll have to fit a hash
1350 * table onto the shared mem backing store
1353 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1354 file_share = &_wapi_fileshare_layout->share_info[i];
1356 /* Make a note of an unused slot, in case we need to
1359 if (first_unused == -1 && file_share->handle_refs == 0) {
1364 if (file_share->handle_refs == 0) {
1368 if (file_share->device == device &&
1369 file_share->inode == inode) {
1370 *old_sharemode = file_share->sharemode;
1371 *old_access = file_share->access;
1372 *share_info = file_share;
1374 /* Increment the reference count while we
1375 * still have sole access to the shared area.
1376 * This makes the increment atomic wrt
1379 InterlockedIncrement (&file_share->handle_refs);
1387 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1390 if (first_unused == -1) {
1391 file_share = &_wapi_fileshare_layout->share_info[++i];
1392 _wapi_fileshare_layout->hwm = i;
1394 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1397 file_share->device = device;
1398 file_share->inode = inode;
1399 file_share->opened_by_pid = getpid ();
1400 file_share->sharemode = new_sharemode;
1401 file_share->access = new_access;
1402 file_share->handle_refs = 1;
1403 *share_info = file_share;
1407 if (*share_info != NULL) {
1408 InterlockedExchange (&(*share_info)->timestamp, now);
1411 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1413 _wapi_handle_unlock_shared_handles ();
1418 /* If we don't have the info in /proc, check if the process that
1419 * opened this share info is still there (it's not a perfect method,
1422 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1424 if (kill (share_info->opened_by_pid, 0) == -1 &&
1427 /* It's gone completely (or there's a new process
1428 * owned by someone else) so mark this share info as
1432 g_message ("%s: Didn't find it, destroying entry", __func__);
1435 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1439 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1440 * question. If there are none, reset the share info.
1442 * This implementation is Linux-specific; legacy systems will have to
1443 * implement their own ways of finding out if a particular file is
1444 * open by a process.
1446 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1448 gboolean found = FALSE, proc_fds = FALSE;
1449 pid_t self = getpid();
1453 /* Prevents entries from expiring under us if we remove this
1456 thr_ret = _wapi_handle_lock_shared_handles ();
1457 g_assert (thr_ret == 0);
1459 /* Prevent new entries racing with us */
1460 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1461 g_assert (thr_ret == 0);
1463 /* If there is no /proc, there's nothing more we can do here */
1464 if (access ("/proc", F_OK) == -1) {
1465 _wapi_handle_check_share_by_pid (share_info);
1469 /* If there's another handle that thinks it owns this fd, then even
1470 * if the fd has been closed behind our back consider it still owned.
1471 * See bugs 75764 and 75891
1473 for (i = 0; i < _wapi_fd_reserve; i++) {
1474 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1477 handle->type == WAPI_HANDLE_FILE) {
1478 struct _WapiHandle_file *file_handle = &handle->u.file;
1480 if (file_handle->share_info == share_info) {
1482 g_message ("%s: handle 0x%x has this file open!",
1491 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1492 struct _WapiHandleShared *shared;
1493 struct _WapiHandle_process *process_handle;
1495 shared = &_wapi_shared_layout->handles[i];
1497 if (shared->type == WAPI_HANDLE_PROCESS) {
1499 struct dirent *fd_entry;
1500 char subdir[_POSIX_PATH_MAX];
1502 process_handle = &shared->u.process;
1503 pid = process_handle->id;
1505 /* Look in /proc/<pid>/fd/ but ignore
1506 * /proc/<our pid>/fd/<fd>, as we have the
1509 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1512 fd_dir = opendir (subdir);
1513 if (fd_dir == NULL) {
1518 g_message ("%s: Looking in %s", __func__, subdir);
1523 while ((fd_entry = readdir (fd_dir)) != NULL) {
1524 char path[_POSIX_PATH_MAX];
1525 struct stat link_stat;
1527 if (!strcmp (fd_entry->d_name, ".") ||
1528 !strcmp (fd_entry->d_name, "..") ||
1530 fd == atoi (fd_entry->d_name))) {
1534 g_snprintf (path, _POSIX_PATH_MAX,
1535 "/proc/%d/fd/%s", pid,
1538 stat (path, &link_stat);
1539 if (link_stat.st_dev == share_info->device &&
1540 link_stat.st_ino == share_info->inode) {
1542 g_message ("%s: Found it at %s",
1554 if (proc_fds == FALSE) {
1555 _wapi_handle_check_share_by_pid (share_info);
1556 } else if (found == FALSE) {
1557 /* Blank out this entry, as it is stale */
1559 g_message ("%s: Didn't find it, destroying entry", __func__);
1562 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1566 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1568 _wapi_handle_unlock_shared_handles ();
1571 void _wapi_handle_dump (void)
1573 struct _WapiHandleUnshared *handle_data;
1577 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1578 (void *)&scan_mutex);
1579 thr_ret = mono_mutex_lock (&scan_mutex);
1580 g_assert (thr_ret == 0);
1582 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1583 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1584 handle_data = &_wapi_private_handles [i][k];
1586 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1590 g_print ("%3x [%7s] %s %d ",
1591 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1592 _wapi_handle_typename[handle_data->type],
1593 handle_data->signalled?"Sg":"Un",
1595 handle_details[handle_data->type](&handle_data->u);
1600 thr_ret = mono_mutex_unlock (&scan_mutex);
1601 g_assert (thr_ret == 0);
1602 pthread_cleanup_pop (0);
1605 static void _wapi_shared_details (gpointer handle_info)
1607 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1609 g_print ("offset: 0x%x", shared->offset);
1612 void _wapi_handle_update_refs (void)
1616 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1618 thr_ret = _wapi_handle_lock_shared_handles ();
1619 g_assert (thr_ret == 0);
1621 /* Prevent file share entries racing with us */
1622 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1623 g_assert(thr_ret == 0);
1625 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1626 (void *)&scan_mutex);
1627 thr_ret = mono_mutex_lock (&scan_mutex);
1629 for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1630 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1631 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1633 if (_WAPI_SHARED_HANDLE(handle->type)) {
1634 struct _WapiHandleShared *shared_data;
1637 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__,
1638 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1641 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
1644 g_message ("%s: (%d) Updating timestamp of handle 0x%x",
1646 handle->u.shared.offset);
1649 InterlockedExchange (&shared_data->timestamp,
1651 } else if (handle->type == WAPI_HANDLE_FILE) {
1652 struct _WapiHandle_file *file_handle = &handle->u.file;
1655 g_message ("%s: (%d) handle 0x%x is FILE", __func__,
1656 getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1659 g_assert (file_handle->share_info != NULL);
1662 g_message ("%s: (%d) Inc refs on fileshare 0x%x",
1664 (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1667 InterlockedExchange (&file_handle->share_info->timestamp, now);
1672 thr_ret = mono_mutex_unlock (&scan_mutex);
1673 g_assert (thr_ret == 0);
1674 pthread_cleanup_pop (0);
1676 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1678 _wapi_handle_unlock_shared_handles ();