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 WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
37 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
49 &_wapi_namedmutex_ops,
52 static void _wapi_shared_details (gpointer handle_info);
54 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
57 _wapi_console_details,
58 _wapi_shared_details, /* thread */
62 NULL, /* Nothing useful to see in a socket handle */
63 NULL, /* Nothing useful to see in a find handle */
64 _wapi_shared_details, /* process */
66 _wapi_shared_details, /* namedmutex */
69 const char *_wapi_handle_typename[] = {
85 struct _WapiHandleUnshared *_wapi_private_handles = NULL;
86 static guint32 _wapi_private_handle_count = 0;
88 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
89 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
91 guint32 _wapi_fd_reserve;
93 mono_mutex_t _wapi_global_signal_mutex;
94 pthread_cond_t _wapi_global_signal_cond;
96 static mono_once_t shared_init_once = MONO_ONCE_INIT;
97 static void shared_init (void)
101 _wapi_fd_reserve = getdtablesize();
103 _wapi_private_handle_count = _WAPI_HANDLE_INITIAL_COUNT;
105 while(_wapi_fd_reserve > _wapi_private_handle_count) {
106 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
109 _wapi_private_handles = g_new0 (struct _WapiHandleUnshared,
110 _wapi_private_handle_count);
113 _wapi_shared_layout = _wapi_shm_attach ();
114 g_assert (_wapi_shared_layout != NULL);
116 _wapi_fileshare_layout = _wapi_fileshare_shm_attach ();
117 g_assert (_wapi_fileshare_layout != NULL);
119 _wapi_collection_init ();
121 thr_ret = pthread_cond_init(&_wapi_global_signal_cond, NULL);
122 g_assert (thr_ret == 0);
124 thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL);
125 g_assert (thr_ret == 0);
128 static void _wapi_handle_init_shared_metadata (struct _WapiHandleSharedMetadata *meta)
130 meta->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
131 meta->signalled = FALSE;
134 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
136 gpointer handle_specific)
139 handle->stale = FALSE;
141 if (handle_specific != NULL) {
142 memcpy (&handle->u, handle_specific, sizeof (handle->u));
146 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
147 WapiHandleType type, gpointer handle_specific)
152 handle->signalled = FALSE;
155 if (!_WAPI_SHARED_HANDLE(type)) {
156 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
157 g_assert (thr_ret == 0);
159 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
160 g_assert (thr_ret == 0);
162 if (handle_specific != NULL) {
163 memcpy (&handle->u, handle_specific,
169 static guint32 _wapi_handle_new_shared_offset (guint32 offset)
172 static guint32 last = 1;
175 /* FIXME: expandable array */
176 /* leave a few slots at the end so that there's always space
177 * to move a handle. (We leave the space in the offset table
178 * too, so we don't have to keep track of inter-segment
181 for(i = last; i <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM; i++) {
182 struct _WapiHandleSharedMetadata *meta = &_wapi_shared_layout->metadata[i];
184 if(meta->offset == 0) {
185 if (InterlockedCompareExchange (&meta->offset, offset,
189 _wapi_handle_init_shared_metadata (meta);
192 /* Someone else beat us to it, just
200 /* Try again from the beginning */
205 /* Will need to expand the array. The caller will sort it out */
210 static guint32 _wapi_handle_new_shared (WapiHandleType type,
211 gpointer handle_specific)
214 static guint32 last = 1;
216 /* The shared memory holds an offset to the real data, so we
217 * can update the handle RCU-style without taking a lock.
218 * This function just allocates the next available data slot,
219 * use _wapi_handle_new_shared_offset to get the offset entry.
222 /* Leave the first slot empty as a guard */
224 /* FIXME: expandable array */
225 /* Leave a few slots at the end so that there's always space
228 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM;
230 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
232 if(handle->type == WAPI_HANDLE_UNUSED) {
233 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
236 _wapi_handle_init_shared (handle, type,
240 /* Someone else beat us to it, just
248 /* Try again from the beginning */
253 /* Will need to expand the array. The caller will sort it out */
259 * _wapi_handle_new_internal:
260 * @type: Init handle to this type
262 * Search for a free handle and initialize it. Return the handle on
263 * success and 0 on failure. This is only called from
264 * _wapi_handle_new, and scan_mutex must be held.
266 static guint32 _wapi_handle_new_internal (WapiHandleType type,
267 gpointer handle_specific)
270 static guint32 last = 0;
272 /* A linear scan should be fast enough. Start from the last
273 * allocation, assuming that handles are allocated more often
274 * than they're freed. Leave the space reserved for file
278 if (last < _wapi_fd_reserve) {
279 last = _wapi_fd_reserve;
283 for(i = last; i <_wapi_private_handle_count; i++) {
284 struct _WapiHandleUnshared *handle = &_wapi_private_handles[i];
286 if(handle->type == WAPI_HANDLE_UNUSED) {
289 _wapi_handle_init (handle, type, handle_specific);
294 if(last > _wapi_fd_reserve) {
295 /* Try again from the beginning */
296 last = _wapi_fd_reserve;
300 /* Will need to expand the array. The caller will sort it out */
305 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
307 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
308 guint32 handle_idx = 0;
312 mono_once (&shared_init_once, shared_init);
315 g_message ("%s: Creating new handle of type %s", __func__,
316 _wapi_handle_typename[type]);
319 g_assert(!_WAPI_FD_HANDLE(type));
321 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
322 (void *)&scan_mutex);
323 thr_ret = mono_mutex_lock (&scan_mutex);
324 g_assert (thr_ret == 0);
326 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
327 /* Try and expand the array, and have another go */
328 _wapi_private_handles = g_renew (struct _WapiHandleUnshared,
329 _wapi_private_handles,
330 _wapi_private_handle_count +
331 _WAPI_HANDLE_INITIAL_COUNT);
332 memset (_wapi_private_handles +
333 (_wapi_private_handle_count *
334 sizeof(struct _WapiHandleUnshared)), '\0',
335 (_WAPI_HANDLE_INITIAL_COUNT *
336 sizeof(struct _WapiHandleUnshared)));
338 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
341 thr_ret = mono_mutex_unlock (&scan_mutex);
342 g_assert (thr_ret == 0);
343 pthread_cleanup_pop (0);
345 /* Make sure we left the space for fd mappings */
346 g_assert (handle_idx >= _wapi_fd_reserve);
348 handle = GUINT_TO_POINTER (handle_idx);
351 g_message ("%s: Allocated new handle %p", __func__, handle);
354 if (_WAPI_SHARED_HANDLE(type)) {
355 /* Add the shared section too */
358 offset = _wapi_handle_new_shared (type, handle_specific);
360 _wapi_handle_collect ();
361 offset = _wapi_handle_new_shared (type,
363 /* FIXME: grow the arrays */
364 g_assert (offset != 0);
367 ref = _wapi_handle_new_shared_offset (offset);
369 _wapi_handle_collect ();
370 ref = _wapi_handle_new_shared_offset (offset);
371 /* FIXME: grow the arrays */
375 _wapi_private_handles[handle_idx].u.shared.offset = ref;
377 g_message ("%s: New shared handle at offset 0x%x", __func__,
385 gpointer _wapi_handle_new_for_existing_ns (WapiHandleType type,
386 gpointer handle_specific,
389 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
390 guint32 handle_idx = 0;
394 mono_once (&shared_init_once, shared_init);
397 g_message ("%s: Creating new handle of type %s", __func__,
398 _wapi_handle_typename[type]);
401 g_assert(!_WAPI_FD_HANDLE(type));
402 g_assert(_WAPI_SHARED_HANDLE(type));
403 g_assert(offset != 0);
405 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
406 (void *)&scan_mutex);
407 thr_ret = mono_mutex_lock (&scan_mutex);
408 g_assert (thr_ret == 0);
410 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
411 /* Try and expand the array, and have another go */
412 _wapi_private_handles = g_renew (struct _WapiHandleUnshared,
413 _wapi_private_handles,
414 _wapi_private_handle_count +
415 _WAPI_HANDLE_INITIAL_COUNT);
416 memset (_wapi_private_handles +
417 (_wapi_private_handle_count *
418 sizeof(struct _WapiHandleUnshared)), '\0',
419 (_WAPI_HANDLE_INITIAL_COUNT *
420 sizeof(struct _WapiHandleUnshared)));
422 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
425 thr_ret = mono_mutex_unlock (&scan_mutex);
426 g_assert (thr_ret == 0);
427 pthread_cleanup_pop (0);
429 /* Make sure we left the space for fd mappings */
430 g_assert (handle_idx >= _wapi_fd_reserve);
432 handle = GUINT_TO_POINTER (handle_idx);
434 _wapi_private_handles[handle_idx].u.shared.offset = offset;
437 g_message ("%s: Allocated new handle %p referencing 0x%x", __func__,
444 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
445 gpointer handle_specific)
447 struct _WapiHandleUnshared *handle;
449 mono_once (&shared_init_once, shared_init);
452 g_message ("%s: Creating new handle of type %s", __func__,
453 _wapi_handle_typename[type]);
456 g_assert(_WAPI_FD_HANDLE(type));
457 g_assert(!_WAPI_SHARED_HANDLE(type));
459 if (fd >= _wapi_fd_reserve) {
461 g_message ("%s: fd %d is too big", __func__, fd);
464 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
467 handle = &_wapi_private_handles[fd];
469 if (handle->type != WAPI_HANDLE_UNUSED) {
471 g_message ("%s: fd %d is already in use!", __func__, fd);
473 /* FIXME: clean up this handle? We can't do anything
474 * with the fd, cos thats the new one
479 g_message ("%s: Assigning new fd handle %d", __func__, fd);
482 _wapi_handle_init (handle, type, handle_specific);
484 return(GUINT_TO_POINTER(fd));
487 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
488 gpointer *handle_specific)
490 struct _WapiHandleUnshared *handle_data;
491 guint32 handle_idx = GPOINTER_TO_UINT(handle);
493 handle_data = &_wapi_private_handles[handle_idx];
495 if (handle_data->type != type) {
499 if (handle_specific == NULL) {
503 if (_WAPI_SHARED_HANDLE(type)) {
504 struct _WapiHandle_shared_ref *ref;
505 struct _WapiHandleShared *shared_handle_data;
506 struct _WapiHandleSharedMetadata *shared_meta;
510 ref = &handle_data->u.shared;
511 shared_meta = &_wapi_shared_layout->metadata[ref->offset];
512 offset = shared_meta->offset;
513 shared_handle_data = &_wapi_shared_layout->handles[offset];
515 g_assert(shared_handle_data->type == type);
517 *handle_specific = &shared_handle_data->u;
518 } while (offset != shared_meta->offset);
520 *handle_specific = &handle_data->u;
526 gboolean _wapi_copy_handle (gpointer handle, WapiHandleType type,
527 struct _WapiHandleShared *handle_specific)
529 struct _WapiHandleUnshared *handle_data;
530 guint32 handle_idx = GPOINTER_TO_UINT(handle);
531 struct _WapiHandle_shared_ref *ref;
532 struct _WapiHandleShared *shared_handle_data;
533 struct _WapiHandleSharedMetadata *shared_meta;
536 g_assert(_WAPI_SHARED_HANDLE(type));
539 g_message ("%s: copying handle %p type %s", __func__, handle,
540 _wapi_handle_typename[type]);
543 handle_data = &_wapi_private_handles[handle_idx];
545 if(handle_data->type != type) {
547 g_message ("%s: incorrect type, %p has type %s", __func__,
548 handle, _wapi_handle_typename[handle_data->type]);
554 if(handle_specific == NULL) {
556 g_message ("%s: Nowhere to store data", __func__);
563 ref = &handle_data->u.shared;
564 shared_meta = &_wapi_shared_layout->metadata[ref->offset];
565 offset = shared_meta->offset;
566 shared_handle_data = &_wapi_shared_layout->handles[offset];
568 g_assert(shared_handle_data->type == type);
570 memcpy(handle_specific, shared_handle_data,
571 sizeof(struct _WapiHandleShared));
572 } while (offset != shared_meta->offset);
575 g_message ("%s: OK", __func__);
581 void _wapi_replace_handle (gpointer handle, WapiHandleType type,
582 struct _WapiHandleShared *handle_specific)
584 struct _WapiHandleShared *shared_handle_data;
585 struct _WapiHandleSharedMetadata *shared_meta;
586 guint32 handle_idx = GPOINTER_TO_UINT(handle);
587 guint32 old_off, new_off, ref;
590 g_message ("%s: Replacing handle %p of type %s", __func__, handle,
591 _wapi_handle_typename[type]);
594 g_assert(_WAPI_SHARED_HANDLE(type));
595 g_assert(_wapi_private_handles[handle_idx].type == type);
597 ref = _wapi_private_handles[handle_idx].u.shared.offset;
598 shared_meta = &_wapi_shared_layout->metadata[ref];
601 old_off = shared_meta->offset;
602 new_off = _wapi_handle_new_shared (type, handle_specific);
604 _wapi_handle_collect ();
605 new_off = _wapi_handle_new_shared (type,
607 /* FIXME: grow the arrays */
608 g_assert (new_off != 0);
611 shared_handle_data = &_wapi_shared_layout->handles[new_off];
613 memcpy (shared_handle_data, handle_specific,
614 sizeof(struct _WapiHandleShared));
616 /* An entry can't become fresh again (its going to be
617 * collected eventually), so no need for atomic ops
620 _wapi_shared_layout->handles[old_off].stale = TRUE;
621 } while(InterlockedCompareExchange (&shared_meta->offset, new_off,
622 old_off) != old_off);
625 g_message ("%s: handle at 0x%x is now found at 0x%x", __func__, ref,
630 gboolean _wapi_try_replace_handle (gpointer handle, WapiHandleType type,
631 struct _WapiHandleShared *handle_specific)
633 struct _WapiHandleShared *shared_handle_data;
634 struct _WapiHandleSharedMetadata *shared_meta;
635 guint32 handle_idx = GPOINTER_TO_UINT(handle);
636 guint32 old_off, new_off, ref;
640 g_message ("%s: Trying to replace handle %p of type %s", __func__,
641 handle, _wapi_handle_typename[type]);
644 g_assert(_WAPI_SHARED_HANDLE(type));
645 g_assert(_wapi_private_handles[handle_idx].type == type);
647 ref = _wapi_private_handles[handle_idx].u.shared.offset;
648 shared_meta = &_wapi_shared_layout->metadata[ref];
650 old_off = shared_meta->offset;
651 new_off = _wapi_handle_new_shared (type, handle_specific);
654 _wapi_handle_collect ();
655 new_off = _wapi_handle_new_shared (type, handle_specific);
657 /* FIXME: grow the arrays */
658 g_assert (new_off != 0);
661 shared_handle_data = &_wapi_shared_layout->handles[new_off];
664 g_message ("%s: Old offset: 0x%x, trying to move to 0x%x", __func__,
668 memcpy (shared_handle_data, handle_specific,
669 sizeof(struct _WapiHandleShared));
671 ret = (InterlockedCompareExchange (&shared_meta->offset, new_off,
672 old_off) == old_off);
675 /* An entry can't become fresh again (its going to be
676 * collected eventually), so no need for atomic ops
679 _wapi_shared_layout->handles[old_off].stale = TRUE;
684 g_message ("%s: handle at 0x%x is now found at 0x%x", __func__,
687 g_message ("%s: handle at 0x%x already updated", __func__,
695 /* This will only find shared handles that have already been opened by
696 * this process. To look up shared handles by name, use
697 * _wapi_search_handle_namespace
699 gpointer _wapi_search_handle (WapiHandleType type,
700 gboolean (*check)(gpointer test, gpointer user),
702 gpointer *handle_specific)
704 struct _WapiHandleUnshared *handle_data = NULL;
708 /* Unsafe, because we don't want the handle to vanish while
711 _WAPI_HANDLE_COLLECTION_UNSAFE;
713 for(i = 0; i < _wapi_private_handle_count; i++) {
714 handle_data = &_wapi_private_handles[i];
716 if(handle_data->type == type) {
717 ret = GUINT_TO_POINTER (i);
718 if(check (ret, user_data) == TRUE) {
724 if(i == _wapi_private_handle_count) {
728 if(handle_specific != NULL) {
729 if (_WAPI_SHARED_HANDLE(type)) {
730 struct _WapiHandle_shared_ref *ref ;
731 struct _WapiHandleShared *shared_handle_data;
732 struct _WapiHandleSharedMetadata *shared_meta;
736 ref = &handle_data->u.shared;
737 shared_meta = &_wapi_shared_layout->metadata[ref->offset];
738 offset = shared_meta->offset;
739 shared_handle_data = &_wapi_shared_layout->handles[offset];
741 g_assert(shared_handle_data->type == type);
743 *handle_specific = &shared_handle_data->u;
744 } while (offset != shared_meta->offset);
746 /* Make sure this handle doesn't vanish in the
749 now = (guint32)(time (NULL) & 0xFFFFFFFF);
750 InterlockedExchange (&shared_meta->timestamp, now);
752 *handle_specific = &handle_data->u;
757 _WAPI_HANDLE_COLLECTION_SAFE;
762 /* This signature makes it easier to use in pthread cleanup handlers */
763 int _wapi_namespace_timestamp_release (gpointer nowptr)
765 guint32 now = GPOINTER_TO_UINT(nowptr);
767 return (_wapi_timestamp_release (&_wapi_shared_layout->namespace_check,
771 int _wapi_namespace_timestamp (guint32 now)
776 ret = _wapi_timestamp_exclusion (&_wapi_shared_layout->namespace_check, now);
777 /* sleep for a bit */
779 _wapi_handle_spin (100);
781 } while (ret == EBUSY);
786 /* Returns the offset of the metadata array, or -1 on error, or 0 for
787 * not found (0 is not a valid offset)
789 gint32 _wapi_search_handle_namespace (WapiHandleType type,
792 struct _WapiHandleShared *shared_handle_data;
793 struct _WapiHandleSharedMetadata *shared_meta;
797 g_assert(_WAPI_SHARED_HANDLE(type));
798 g_assert(_wapi_shared_layout->namespace_check != 0);
801 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
802 utf8_name, _wapi_handle_typename[type]);
805 _WAPI_HANDLE_COLLECTION_UNSAFE;
807 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
808 WapiSharedNamespace *sharedns;
810 shared_meta = &_wapi_shared_layout->metadata[i];
811 shared_handle_data = &_wapi_shared_layout->handles[shared_meta->offset];
813 /* Check mutex, event, semaphore, timer, job and file-mapping
814 * object names. So far only mutex is implemented.
816 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
821 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
824 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
827 g_message ("%s: name is [%s]", __func__, sharedns->name);
830 if (strcmp (sharedns->name, utf8_name) == 0) {
831 if (shared_handle_data->type != type) {
832 /* Its the wrong type, so fail now */
834 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
840 g_message ("%s: handle 0x%x matches name and type", __func__, i);
849 _WAPI_HANDLE_COLLECTION_SAFE;
854 void _wapi_handle_ref (gpointer handle)
856 guint32 idx = GPOINTER_TO_UINT(handle);
858 InterlockedIncrement (&_wapi_private_handles[idx].ref);
861 g_message ("%s: handle %p ref now %d", __func__, handle,
862 _wapi_private_handles[idx].ref);
866 /* The handle must not be locked on entry to this function */
867 void _wapi_handle_unref (gpointer handle)
869 guint32 idx = GPOINTER_TO_UINT(handle);
870 gboolean destroy = FALSE;
873 /* Possible race condition here if another thread refs the
874 * handle between here and setting the type to UNUSED. I
875 * could lock a mutex, but I'm not sure that allowing a handle
876 * reference to reach 0 isn't an application bug anyway.
878 destroy = (InterlockedDecrement (&_wapi_private_handles[idx].ref) ==0);
881 g_message ("%s: handle %p ref now %d (destroy %s)", __func__, handle,
882 _wapi_private_handles[idx].ref, destroy?"TRUE":"FALSE");
887 g_message ("%s: Destroying handle %p", __func__, handle);
890 _wapi_handle_ops_close (handle);
892 memset (&_wapi_private_handles[idx].u, '\0',
893 sizeof(_wapi_private_handles[idx].u));
895 _wapi_private_handles[idx].type = WAPI_HANDLE_UNUSED;
897 /* Destroy the mutex and cond var. We hope nobody
898 * tried to grab them between the handle unlock and
899 * now, but pthreads doesn't have a
900 * "unlock_and_destroy" atomic function.
902 thr_ret = mono_mutex_destroy (&_wapi_private_handles[idx].signal_mutex);
903 g_assert (thr_ret == 0);
905 thr_ret = pthread_cond_destroy (&_wapi_private_handles[idx].signal_cond);
906 g_assert (thr_ret == 0);
908 /* The garbage collector will take care of shared data
909 * if this is a shared handle
914 void _wapi_handle_register_capabilities (WapiHandleType type,
915 WapiHandleCapability caps)
917 handle_caps[type] = caps;
920 gboolean _wapi_handle_test_capabilities (gpointer handle,
921 WapiHandleCapability caps)
923 guint32 idx = GPOINTER_TO_UINT(handle);
926 type = _wapi_private_handles[idx].type;
929 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
930 handle_caps[type], caps, handle_caps[type] & caps);
933 return((handle_caps[type] & caps) != 0);
936 void _wapi_handle_ops_close (gpointer handle)
938 guint32 idx = GPOINTER_TO_UINT(handle);
941 type = _wapi_private_handles[idx].type;
943 if (handle_ops[type] != NULL &&
944 handle_ops[type]->close != NULL) {
945 handle_ops[type]->close (handle);
949 void _wapi_handle_ops_signal (gpointer handle)
951 guint32 idx = GPOINTER_TO_UINT(handle);
954 type = _wapi_private_handles[idx].type;
956 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
957 handle_ops[type]->signal (handle);
961 gboolean _wapi_handle_ops_own (gpointer handle)
963 guint32 idx = GPOINTER_TO_UINT(handle);
966 type = _wapi_private_handles[idx].type;
968 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
969 return(handle_ops[type]->own_handle (handle));
975 gboolean _wapi_handle_ops_isowned (gpointer handle)
977 guint32 idx = GPOINTER_TO_UINT(handle);
980 type = _wapi_private_handles[idx].type;
982 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
983 return(handle_ops[type]->is_owned (handle));
991 * @handle: The handle to release
993 * Closes and invalidates @handle, releasing any resources it
994 * consumes. When the last handle to a temporary or non-persistent
995 * object is closed, that object can be deleted. Closing the same
996 * handle twice is an error.
998 * Return value: %TRUE on success, %FALSE otherwise.
1000 gboolean CloseHandle(gpointer handle)
1002 _wapi_handle_unref (handle);
1007 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1013 guint32 count, i, iter=0;
1016 WapiHandleType type;
1018 /* Lock all the handles, with backoff */
1020 for(i=0; i<numhandles; i++) {
1021 gpointer handle = handles[i];
1022 guint32 idx = GPOINTER_TO_UINT(handle);
1023 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1026 g_message ("%s: attempting to lock %p", __func__, handle);
1029 type = _wapi_private_handles[idx].type;
1031 if (_WAPI_SHARED_HANDLE(type)) {
1032 /* We don't lock shared handles, but we need
1033 * to be able to simultaneously check the
1034 * signal state of all handles in the array
1036 * We do this by atomically putting the
1037 * least-significant 32 bits of time(2) into
1038 * the 'checking' field if it is zero. If it
1039 * isn't zero, then it means that either
1040 * another thread is looking at this handle
1041 * right now, or someone crashed here. Assume
1042 * that if the time value is more than 10
1043 * seconds old, its a crash and override it.
1044 * 10 seconds should be enough for anyone...
1046 * If the time value is within 10 seconds,
1047 * back off and try again as per the
1050 thr_ret = _wapi_timestamp_exclusion (&WAPI_SHARED_HANDLE_METADATA(handle).checking, now);
1052 thr_ret = _wapi_handle_trylock_handle (handle);
1059 g_message ("%s: attempt failed for %p: %s", __func__,
1060 handle, strerror (thr_ret));
1064 handle = handles[i];
1065 idx = GPOINTER_TO_UINT(handle);
1067 if (_WAPI_SHARED_HANDLE(type)) {
1068 /* Reset the checking field */
1069 thr_ret = _wapi_timestamp_release (&WAPI_SHARED_HANDLE_METADATA(handle).checking, now);
1071 thr_ret = _wapi_handle_unlock_handle (handle);
1073 g_assert (thr_ret == 0);
1076 /* If iter ever reaches 100 the nanosleep will
1077 * return EINVAL immediately, but we have a
1078 * design flaw if that happens.
1082 g_warning ("%s: iteration overflow!",
1088 g_message ("%s: Backing off for %d ms", __func__,
1091 _wapi_handle_spin (10 * iter);
1098 g_message ("%s: Locked all handles", __func__);
1104 for(i=0; i<numhandles; i++) {
1105 gpointer handle = handles[i];
1106 guint32 idx = GPOINTER_TO_UINT(handle);
1108 type = _wapi_private_handles[idx].type;
1110 _wapi_handle_ref (handle);
1113 g_message ("%s: Checking handle %p", __func__, handle);
1116 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1117 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1118 (_WAPI_SHARED_HANDLE(type) &&
1119 WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) ||
1120 (!_WAPI_SHARED_HANDLE(type) &&
1121 _wapi_private_handles[idx].signalled == TRUE)) {
1125 g_message ("%s: Handle %p signalled", __func__,
1135 g_message ("%s: %d event handles signalled", __func__, count);
1138 if ((waitall == TRUE && count == numhandles) ||
1139 (waitall == FALSE && count > 0)) {
1146 g_message ("%s: Returning %d", __func__, ret);
1154 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1159 for(i=0; i<numhandles; i++) {
1160 gpointer handle = handles[i];
1161 guint32 idx = GPOINTER_TO_UINT(handle);
1162 WapiHandleType type = _wapi_private_handles[idx].type;
1165 g_message ("%s: unlocking handle %p", __func__, handle);
1168 if (_WAPI_SHARED_HANDLE(type)) {
1169 WAPI_SHARED_HANDLE_METADATA(handle).checking = 0;
1171 thr_ret = mono_mutex_unlock (&_wapi_private_handles[idx].signal_mutex);
1172 g_assert (thr_ret == 0);
1175 _wapi_handle_unref (handle);
1179 int _wapi_handle_wait_signal (void)
1181 return(mono_cond_wait (&_wapi_global_signal_cond,
1182 &_wapi_global_signal_mutex));
1185 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1187 return(mono_cond_timedwait (&_wapi_global_signal_cond,
1188 &_wapi_global_signal_mutex,
1192 int _wapi_handle_wait_signal_poll_share (void)
1194 struct timespec fake_timeout;
1195 guint32 signal_count = _wapi_shared_layout->signal_count;
1199 g_message ("%s: poll private and shared handles", __func__);
1203 _wapi_calc_timeout (&fake_timeout, 100);
1205 ret = mono_cond_timedwait (&_wapi_global_signal_cond,
1206 &_wapi_global_signal_mutex,
1209 /* Check the shared signal counter */
1210 if (ret == ETIMEDOUT) {
1211 if (signal_count != _wapi_shared_layout->signal_count) {
1213 g_message ("%s: A shared handle was signalled",
1220 /* This will be 0 indicating a private handle
1221 * was signalled, or an error
1224 g_message ("%s: returning: %d", __func__, ret);
1230 /* If timeout and no shared handle was signalled, go
1236 int _wapi_handle_timedwait_signal_poll_share (struct timespec *timeout)
1238 struct timespec fake_timeout;
1239 guint32 signal_count = _wapi_shared_layout->signal_count;
1243 g_message ("%s: poll private and shared handles", __func__);
1247 _wapi_calc_timeout (&fake_timeout, 100);
1249 if ((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 */
1255 g_message ("%s: last few ms", __func__);
1258 ret = mono_cond_timedwait (&_wapi_global_signal_cond,
1259 &_wapi_global_signal_mutex,
1261 /* If this times out, it will compare the
1262 * shared signal counter and then if that
1263 * hasn't increased will fall out of the
1266 if (ret != ETIMEDOUT) {
1267 /* Either a private handle was
1268 * signalled, or an error.
1271 g_message ("%s: returning: %d", __func__, ret);
1276 ret = mono_cond_timedwait (&_wapi_global_signal_cond,
1277 &_wapi_global_signal_mutex,
1280 /* Mask the fake timeout, this will cause
1281 * another poll if the shared counter hasn't
1284 if (ret == ETIMEDOUT) {
1287 /* Either a private handle was
1288 * signalled, or an error
1291 g_message ("%s: returning: %d", __func__, ret);
1297 /* No private handle was signalled, so check the
1298 * shared signal counter
1300 if (signal_count != _wapi_shared_layout->signal_count) {
1302 g_message ("%s: A shared handle was signalled",
1307 } while (ret != ETIMEDOUT);
1310 g_message ("%s: returning ETIMEDOUT", __func__);
1316 int _wapi_handle_wait_signal_handle (gpointer handle)
1318 guint32 idx = GPOINTER_TO_UINT(handle);
1321 g_message ("%s: waiting for %p", __func__, handle);
1324 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1326 if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) {
1330 _wapi_handle_spin (100);
1333 return(mono_cond_wait (&_wapi_private_handles[idx].signal_cond,
1334 &_wapi_private_handles[idx].signal_mutex));
1338 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1339 struct timespec *timeout)
1341 guint32 idx = GPOINTER_TO_UINT(handle);
1344 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1345 _wapi_handle_typename[_wapi_handle_type (handle)]);
1348 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1349 struct timespec fake_timeout;
1352 if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) {
1356 _wapi_calc_timeout (&fake_timeout, 100);
1358 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1359 (fake_timeout.tv_sec == timeout->tv_sec &&
1360 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1361 /* FIXME: Real timeout is less than
1362 * 100ms time, but is it really worth
1363 * calculating to the exact ms?
1365 _wapi_handle_spin (100);
1367 if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) {
1373 _wapi_handle_spin (100);
1377 return(mono_cond_timedwait (&_wapi_private_handles[idx].signal_cond, &_wapi_private_handles[idx].signal_mutex, timeout));
1381 gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, guint32 dir,
1382 gboolean inherit, guint32 flags,
1383 gpointer stdin_handle,
1384 gpointer stdout_handle,
1385 gpointer stderr_handle,
1386 gpointer *process_handle,
1387 gpointer *thread_handle, guint32 *pid,
1391 WapiHandleRequest fork_proc={0};
1392 WapiHandleResponse fork_proc_resp={0};
1393 int in_fd, out_fd, err_fd;
1399 fork_proc.type=WapiHandleRequestType_ProcessFork;
1400 fork_proc.u.process_fork.cmd=cmd;
1401 fork_proc.u.process_fork.env=env;
1402 fork_proc.u.process_fork.dir=dir;
1403 fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
1404 fork_proc.u.process_fork.stdout_handle=GPOINTER_TO_UINT (stdout_handle);
1405 fork_proc.u.process_fork.stderr_handle=GPOINTER_TO_UINT (stderr_handle);
1406 fork_proc.u.process_fork.inherit=inherit;
1407 fork_proc.u.process_fork.flags=flags;
1409 in_fd = GPOINTER_TO_UINT (stdin_handle);
1410 out_fd = GPOINTER_TO_UINT (stdout_handle);
1411 err_fd = GPOINTER_TO_UINT (stderr_handle);
1413 if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
1414 /* We were given duff handles */
1415 /* FIXME: error code */
1419 _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
1420 &fork_proc_resp, in_fd,
1422 if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
1423 *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
1424 *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
1425 *pid=fork_proc_resp.u.process_fork.pid;
1426 *tid=fork_proc_resp.u.process_fork.tid;
1428 /* If there was an internal error, the handles will be
1429 * 0. If there was an error forking or execing, the
1430 * handles will have values, and process_handle's
1431 * exec_errno will be set, and the handle will be
1432 * signalled immediately.
1434 if(*process_handle==0 || *thread_handle==0) {
1437 /* This call returns new handles, so we need to do
1438 * a little bookkeeping
1440 if (_wapi_private_data != NULL) {
1441 guint32 segment, idx;
1443 _wapi_handle_segment (*process_handle,
1445 _wapi_handle_ensure_mapped (segment);
1446 _wapi_handle_get_private_segment (segment)->handles[idx].type = WAPI_HANDLE_PROCESS;
1448 _wapi_handle_segment (*thread_handle,
1450 _wapi_handle_ensure_mapped (segment);
1451 _wapi_handle_get_private_segment (segment)->handles[idx].type = WAPI_HANDLE_THREAD;
1457 g_warning ("%s: bogus daemon response, type %d", __func__,
1458 fork_proc_resp.type);
1459 g_assert_not_reached ();
1469 _wapi_handle_process_kill (pid_t process, guint32 signo, gint *errnum)
1472 WapiHandleRequest killproc = {0};
1473 WapiHandleResponse killprocresp = {0};
1476 if (shared != TRUE) {
1477 if (errnum) *errnum = EINVAL;
1481 killproc.type = WapiHandleRequestType_ProcessKill;
1482 killproc.u.process_kill.pid = process;
1483 killproc.u.process_kill.signo = signo;
1485 _wapi_daemon_request_response (daemon_sock, &killproc, &killprocresp);
1487 if (killprocresp.type != WapiHandleResponseType_ProcessKill) {
1488 g_warning ("%s: bogus daemon response, type %d", __func__,
1490 g_assert_not_reached ();
1493 result = killprocresp.u.process_kill.err;
1494 if (result != 0 && errnum != NULL)
1495 *errnum = (result == FALSE) ? result : 0;
1497 return (result == 0);
1503 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1504 guint32 new_sharemode,
1506 guint32 *old_sharemode,
1507 guint32 *old_access,
1508 struct _WapiFileShare **share_info)
1510 struct _WapiFileShare *file_share;
1511 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1512 int thr_ret, i, first_unused = -1;
1513 gboolean exists = FALSE;
1515 /* Marking this as COLLECTION_UNSAFE prevents entries from
1516 * expiring under us as we search
1518 _WAPI_HANDLE_COLLECTION_UNSAFE;
1520 /* Prevent new entries racing with us */
1522 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1524 thr_ret = _wapi_timestamp_exclusion (&_wapi_fileshare_layout->share_check, now);
1525 if (thr_ret == EBUSY) {
1526 _wapi_handle_spin (100);
1528 } while (thr_ret == EBUSY);
1529 g_assert (thr_ret == 0);
1531 /* If a linear scan gets too slow we'll have to fit a hash
1532 * table onto the shared mem backing store
1535 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1536 file_share = &_wapi_fileshare_layout->share_info[i];
1538 /* Make a note of an unused slot, in case we need to
1541 if (first_unused == -1 && file_share->handle_refs == 0) {
1546 if (file_share->handle_refs == 0) {
1550 if (file_share->device == device &&
1551 file_share->inode == inode) {
1552 *old_sharemode = file_share->sharemode;
1553 *old_access = file_share->access;
1554 *share_info = file_share;
1556 /* Increment the reference count while we
1557 * still have sole access to the shared area.
1558 * This makes the increment atomic wrt
1561 InterlockedIncrement (&file_share->handle_refs);
1569 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1572 if (first_unused == -1) {
1573 file_share = &_wapi_fileshare_layout->share_info[++i];
1574 _wapi_fileshare_layout->hwm = i;
1576 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1579 file_share->device = device;
1580 file_share->inode = inode;
1581 file_share->sharemode = new_sharemode;
1582 file_share->access = new_access;
1583 file_share->handle_refs = 1;
1584 *share_info = file_share;
1588 if (*share_info != NULL) {
1589 InterlockedExchange (&(*share_info)->timestamp, now);
1592 thr_ret = _wapi_timestamp_release (&_wapi_fileshare_layout->share_check, now);
1594 _WAPI_HANDLE_COLLECTION_SAFE;
1599 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1600 * question. If there are none, reset the share info.
1602 * This implementation is Linux-specific; legacy systems will have to
1603 * implement their own ways of finding out if a particular file is
1604 * open by a process.
1606 void _wapi_handle_check_share (struct _WapiFileShare *share_info)
1609 struct dirent *proc_entry;
1610 gboolean found = FALSE;
1611 pid_t self = getpid();
1613 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1616 proc_dir = opendir ("/proc");
1617 if (proc_dir == NULL) {
1621 /* Marking this as COLLECTION_UNSAFE prevents entries from
1622 * expiring under us if we remove this one
1624 _WAPI_HANDLE_COLLECTION_UNSAFE;
1626 /* Prevent new entries racing with us */
1628 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1630 thr_ret = _wapi_timestamp_exclusion (&_wapi_fileshare_layout->share_check, now);
1631 if (thr_ret == EBUSY) {
1632 _wapi_handle_spin (100);
1634 } while (thr_ret == EBUSY);
1635 g_assert (thr_ret == 0);
1637 while ((proc_entry = readdir (proc_dir)) != NULL) {
1638 /* We only care about numerically-named directories */
1639 pid = atoi (proc_entry->d_name);
1640 if (pid != 0 && pid != self) {
1641 /* Look in /proc/<pid>/fd/ but ignore
1642 * ourselves, as we have the file open too
1645 struct dirent *fd_entry;
1646 char subdir[_POSIX_PATH_MAX];
1648 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1651 fd_dir = opendir (subdir);
1652 if (fd_dir == NULL) {
1656 while ((fd_entry = readdir (fd_dir)) != NULL) {
1657 char path[_POSIX_PATH_MAX];
1658 struct stat link_stat;
1660 if (!strcmp (fd_entry->d_name, ".") ||
1661 !strcmp (fd_entry->d_name, "..")) {
1665 g_snprintf (path, _POSIX_PATH_MAX,
1666 "/proc/%d/fd/%s", pid,
1669 stat (path, &link_stat);
1670 if (link_stat.st_dev == share_info->device &&
1671 link_stat.st_ino == share_info->inode) {
1673 g_message ("%s: Found it at %s",
1685 closedir (proc_dir);
1687 if (found == FALSE) {
1688 /* Blank out this entry, as it is stale */
1690 g_message ("%s: Didn't find it, destroying entry", __func__);
1693 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1696 thr_ret = _wapi_timestamp_release (&_wapi_fileshare_layout->share_check, now);
1698 _WAPI_HANDLE_COLLECTION_SAFE;
1701 void _wapi_handle_dump (void)
1703 struct _WapiHandleUnshared *handle_data;
1706 for (i = 0; i < _wapi_private_handle_count; i++) {
1707 handle_data = &_wapi_private_handles[i];
1709 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1713 g_print ("%3x [%7s] %s %d ", i,
1714 _wapi_handle_typename[handle_data->type],
1715 handle_data->signalled?"Sg":"Un", handle_data->ref);
1716 handle_details[handle_data->type](&handle_data->u);
1721 static void _wapi_shared_details (gpointer handle_info)
1723 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1725 g_print ("offset: 0x%x", shared->offset);
1728 void _wapi_handle_update_refs (void)
1732 _WAPI_HANDLE_COLLECTION_UNSAFE;
1734 for (i = 0; i < _wapi_private_handle_count; i++) {
1735 struct _WapiHandleUnshared *handle = &_wapi_private_handles[i];
1736 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1738 if (_WAPI_SHARED_HANDLE(handle->type)) {
1739 struct _WapiHandleSharedMetadata *shared_meta;
1742 g_message ("%s: (%d) handle 0x%x is SHARED", __func__,
1746 shared_meta = &_wapi_shared_layout->metadata[handle->u.shared.offset];
1749 g_message ("%s: (%d) Updating timstamp of handle 0x%x",
1751 handle->u.shared.offset);
1754 InterlockedExchange (&shared_meta->timestamp, now);
1755 } else if (handle->type == WAPI_HANDLE_FILE) {
1756 struct _WapiHandle_file *file_handle = &handle->u.file;
1759 g_message ("%s: (%d) handle 0x%x is FILE", __func__,
1763 g_assert (file_handle->share_info != NULL);
1766 g_message ("%s: (%d) Inc refs on fileshare 0x%x",
1768 (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1771 InterlockedExchange (&file_handle->share_info->timestamp, now);
1775 _WAPI_HANDLE_COLLECTION_SAFE;