[runtime] Fix "make distcheck"
[mono-project.git] / mono / io-layer / handles.c
blobac064054bd10240cc24a53c8c139e8595a27764e
1 /*
2 * handles.c: Generic and internal operations on handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2011 Novell, Inc.
8 * Copyright 2011 Xamarin Inc
9 */
11 #include <config.h>
12 #include <glib.h>
13 #include <pthread.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #ifdef HAVE_SIGNAL_H
17 #include <signal.h>
18 #endif
19 #include <string.h>
20 #include <sys/types.h>
21 #ifdef HAVE_SYS_SOCKET_H
22 # include <sys/socket.h>
23 #endif
24 #ifdef HAVE_SYS_UN_H
25 # include <sys/un.h>
26 #endif
27 #ifdef HAVE_SYS_MMAN_H
28 # include <sys/mman.h>
29 #endif
30 #ifdef HAVE_DIRENT_H
31 # include <dirent.h>
32 #endif
33 #include <sys/stat.h>
34 #ifdef HAVE_SYS_RESOURCE_H
35 # include <sys/resource.h>
36 #endif
38 #include <mono/io-layer/wapi.h>
39 #include <mono/io-layer/wapi-private.h>
40 #include <mono/io-layer/handles-private.h>
41 #include <mono/io-layer/misc-private.h>
42 #include <mono/io-layer/shared.h>
43 #include <mono/io-layer/collection.h>
44 #include <mono/io-layer/process-private.h>
46 #include <mono/utils/mono-mutex.h>
47 #include <mono/utils/mono-proclib.h>
48 #undef DEBUG_REFS
50 #if 0
51 #define DEBUG(...) g_message(__VA_ARGS__)
52 #else
53 #define DEBUG(...)
54 #endif
56 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
58 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
59 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
60 NULL,
61 &_wapi_file_ops,
62 &_wapi_console_ops,
63 &_wapi_thread_ops,
64 &_wapi_sem_ops,
65 &_wapi_mutex_ops,
66 &_wapi_event_ops,
67 #ifndef DISABLE_SOCKETS
68 &_wapi_socket_ops,
69 #endif
70 &_wapi_find_ops,
71 &_wapi_process_ops,
72 &_wapi_pipe_ops,
73 &_wapi_namedmutex_ops,
74 &_wapi_namedsem_ops,
75 &_wapi_namedevent_ops,
78 static void _wapi_shared_details (gpointer handle_info);
80 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
81 NULL,
82 _wapi_file_details,
83 _wapi_console_details,
84 _wapi_shared_details, /* thread */
85 _wapi_sem_details,
86 _wapi_mutex_details,
87 _wapi_event_details,
88 NULL, /* Nothing useful to see in a socket handle */
89 NULL, /* Nothing useful to see in a find handle */
90 _wapi_shared_details, /* process */
91 _wapi_pipe_details,
92 _wapi_shared_details, /* namedmutex */
93 _wapi_shared_details, /* namedsem */
94 _wapi_shared_details, /* namedevent */
97 const char *_wapi_handle_typename[] = {
98 "Unused",
99 "File",
100 "Console",
101 "Thread",
102 "Sem",
103 "Mutex",
104 "Event",
105 "Socket",
106 "Find",
107 "Process",
108 "Pipe",
109 "N.Mutex",
110 "N.Sem",
111 "N.Event",
112 "Error!!"
116 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
117 * If 4M handles are not enough... Oh, well... we will crash.
119 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
120 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
122 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
123 static guint32 _wapi_private_handle_count = 0;
124 static guint32 _wapi_private_handle_slot_count = 0;
126 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
129 * If SHM is enabled, this will point to shared memory, otherwise it will be NULL.
131 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
134 * If SHM is disabled, this will point to a hash of _WapiFileShare structures, otherwise
135 * it will be NULL. We use this instead of _wapi_fileshare_layout to avoid allocating a
136 * 4MB array.
138 static GHashTable *file_share_hash;
139 static mono_mutex_t file_share_hash_mutex;
141 #define file_share_hash_lock() mono_mutex_lock (&file_share_hash_mutex)
142 #define file_share_hash_unlock() mono_mutex_unlock (&file_share_hash_mutex)
144 guint32 _wapi_fd_reserve;
147 * This is an internal handle which is used for handling waiting for multiple handles.
148 * Threads which wait for multiple handles wait on this one handle, and when a handle
149 * is signalled, this handle is signalled too.
151 static gpointer _wapi_global_signal_handle;
153 /* Point to the mutex/cond inside _wapi_global_signal_handle */
154 mono_mutex_t *_wapi_global_signal_mutex;
155 pthread_cond_t *_wapi_global_signal_cond;
157 int _wapi_sem_id;
158 gboolean _wapi_has_shut_down = FALSE;
160 /* Use this instead of getpid(), to cope with linuxthreads. It's a
161 * function rather than a variable lookup because we need to get at
162 * this before share_init() might have been called.
164 static pid_t _wapi_pid;
165 static mono_once_t pid_init_once = MONO_ONCE_INIT;
167 static void _wapi_handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles);
169 static void pid_init (void)
171 _wapi_pid = getpid ();
174 pid_t _wapi_getpid (void)
176 mono_once (&pid_init_once, pid_init);
178 return(_wapi_pid);
182 static mono_mutex_t scan_mutex;
184 static void handle_cleanup (void)
186 int i, j, k;
188 /* Every shared handle we were using ought really to be closed
189 * by now, but to make sure just blow them all away. The
190 * exiting finalizer thread in particular races us to the
191 * program exit and doesn't always win, so it can be left
192 * cluttering up the shared file. Anything else left over is
193 * really a bug.
195 for(i = SLOT_INDEX (0); _wapi_private_handles[i] != NULL; i++) {
196 for(j = SLOT_OFFSET (0); j < _WAPI_HANDLE_INITIAL_COUNT; j++) {
197 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles[i][j];
198 gpointer handle = GINT_TO_POINTER (i*_WAPI_HANDLE_INITIAL_COUNT+j);
200 for(k = handle_data->ref; k > 0; k--) {
201 DEBUG ("%s: unreffing %s handle %p", __func__, _wapi_handle_typename[type], handle);
203 _wapi_handle_unref_full (handle, TRUE);
208 _wapi_shm_semaphores_remove ();
210 _wapi_shm_detach (WAPI_SHM_DATA);
211 _wapi_shm_detach (WAPI_SHM_FILESHARE);
213 if (file_share_hash) {
214 g_hash_table_destroy (file_share_hash);
215 mono_mutex_destroy (&file_share_hash_mutex);
218 for (i = 0; i < _WAPI_PRIVATE_MAX_SLOTS; ++i)
219 g_free (_wapi_private_handles [i]);
223 wapi_getdtablesize (void)
225 return eg_getdtablesize ();
229 * wapi_init:
231 * Initialize the io-layer.
233 void
234 wapi_init (void)
236 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
237 == WAPI_HANDLE_COUNT);
239 _wapi_fd_reserve = wapi_getdtablesize ();
241 /* This is needed by the code in _wapi_handle_new_internal */
242 _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
244 do {
246 * The entries in _wapi_private_handles reserved for fds are allocated lazily to
247 * save memory.
250 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
251 _WAPI_HANDLE_INITIAL_COUNT);
254 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
255 _wapi_private_handle_slot_count ++;
256 } while(_wapi_fd_reserve > _wapi_private_handle_count);
258 _wapi_shm_semaphores_init ();
260 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
261 g_assert (_wapi_shared_layout != NULL);
263 if (_wapi_shm_enabled ()) {
264 /* This allocates a 4mb array, so do it only if SHM is enabled */
265 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
266 g_assert (_wapi_fileshare_layout != NULL);
269 #if !defined (DISABLE_SHARED_HANDLES)
270 if (_wapi_shm_enabled ())
271 _wapi_collection_init ();
272 #endif
273 _wapi_io_init ();
274 mono_mutex_init (&scan_mutex);
276 _wapi_global_signal_handle = _wapi_handle_new (WAPI_HANDLE_EVENT, NULL);
278 _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
279 _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
281 wapi_processes_init ();
284 void
285 wapi_cleanup (void)
287 g_assert (_wapi_has_shut_down == FALSE);
289 _wapi_has_shut_down = TRUE;
291 _wapi_error_cleanup ();
292 _wapi_thread_cleanup ();
293 wapi_processes_cleanup ();
294 handle_cleanup ();
297 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
298 WapiHandleType type,
299 gpointer handle_specific)
301 g_assert (_wapi_has_shut_down == FALSE);
303 handle->type = type;
304 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
305 handle->signalled = FALSE;
306 handle->handle_refs = 1;
308 if (handle_specific != NULL) {
309 memcpy (&handle->u, handle_specific, sizeof (handle->u));
313 static size_t _wapi_handle_struct_size (WapiHandleType type)
315 size_t type_size;
317 switch (type) {
318 case WAPI_HANDLE_FILE: case WAPI_HANDLE_CONSOLE: case WAPI_HANDLE_PIPE:
319 type_size = sizeof (struct _WapiHandle_file);
320 break;
321 case WAPI_HANDLE_THREAD:
322 type_size = sizeof (struct _WapiHandle_thread);
323 break;
324 case WAPI_HANDLE_SEM:
325 type_size = sizeof (struct _WapiHandle_sem);
326 break;
327 case WAPI_HANDLE_MUTEX:
328 type_size = sizeof (struct _WapiHandle_mutex);
329 break;
330 case WAPI_HANDLE_EVENT:
331 type_size = sizeof (struct _WapiHandle_event);
332 break;
333 case WAPI_HANDLE_SOCKET:
334 type_size = sizeof (struct _WapiHandle_socket);
335 break;
336 case WAPI_HANDLE_FIND:
337 type_size = sizeof (struct _WapiHandle_find);
338 break;
339 case WAPI_HANDLE_PROCESS:
340 type_size = sizeof (struct _WapiHandle_process);
341 break;
342 case WAPI_HANDLE_NAMEDMUTEX:
343 type_size = sizeof (struct _WapiHandle_namedmutex);
344 break;
345 case WAPI_HANDLE_NAMEDSEM:
346 type_size = sizeof (struct _WapiHandle_namedsem);
347 break;
348 case WAPI_HANDLE_NAMEDEVENT:
349 type_size = sizeof (struct _WapiHandle_namedevent);
350 break;
352 default:
353 g_error ("Unknown WapiHandleType: %d\n", type);
356 return type_size;
359 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
360 WapiHandleType type, gpointer handle_specific)
362 int thr_ret;
363 int type_size;
365 g_assert (_wapi_has_shut_down == FALSE);
367 handle->type = type;
368 handle->signalled = FALSE;
369 handle->ref = 1;
371 if (!_WAPI_SHARED_HANDLE(type)) {
372 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
373 g_assert (thr_ret == 0);
375 thr_ret = mono_mutex_init (&handle->signal_mutex);
376 g_assert (thr_ret == 0);
378 if (handle_specific != NULL) {
379 type_size = _wapi_handle_struct_size (type);
380 memcpy (&handle->u, handle_specific,
381 type_size);
386 static guint32 _wapi_handle_new_shared (WapiHandleType type,
387 gpointer handle_specific)
389 guint32 offset;
390 static guint32 last = 1;
391 int thr_ret;
393 g_assert (_wapi_has_shut_down == FALSE);
395 /* Leave the first slot empty as a guard */
396 again:
397 /* FIXME: expandable array */
398 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
399 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
401 if(handle->type == WAPI_HANDLE_UNUSED) {
402 thr_ret = _wapi_handle_lock_shared_handles ();
403 g_assert (thr_ret == 0);
405 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
406 last = offset + 1;
408 _wapi_handle_init_shared (handle, type,
409 handle_specific);
411 _wapi_handle_unlock_shared_handles ();
413 return(offset);
414 } else {
415 /* Someone else beat us to it, just
416 * continue looking
420 _wapi_handle_unlock_shared_handles ();
424 if(last > 1) {
425 /* Try again from the beginning */
426 last = 1;
427 goto again;
430 /* Will need to expand the array. The caller will sort it out */
432 return(0);
436 * _wapi_handle_new_internal:
437 * @type: Init handle to this type
439 * Search for a free handle and initialize it. Return the handle on
440 * success and 0 on failure. This is only called from
441 * _wapi_handle_new, and scan_mutex must be held.
443 static guint32 _wapi_handle_new_internal (WapiHandleType type,
444 gpointer handle_specific)
446 guint32 i, k, count;
447 static guint32 last = 0;
448 gboolean retry = FALSE;
450 g_assert (_wapi_has_shut_down == FALSE);
452 /* A linear scan should be fast enough. Start from the last
453 * allocation, assuming that handles are allocated more often
454 * than they're freed. Leave the space reserved for file
455 * descriptors
458 if (last < _wapi_fd_reserve) {
459 last = _wapi_fd_reserve;
460 } else {
461 retry = TRUE;
464 again:
465 count = last;
466 for(i = SLOT_INDEX (count); i < _wapi_private_handle_slot_count; i++) {
467 if (_wapi_private_handles [i]) {
468 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
469 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
471 if(handle->type == WAPI_HANDLE_UNUSED) {
472 last = count + 1;
474 _wapi_handle_init (handle, type, handle_specific);
475 return (count);
477 count++;
482 if(retry && last > _wapi_fd_reserve) {
483 /* Try again from the beginning */
484 last = _wapi_fd_reserve;
485 goto again;
488 /* Will need to expand the array. The caller will sort it out */
490 return(0);
493 gpointer
494 _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
496 guint32 handle_idx = 0;
497 gpointer handle;
498 int thr_ret;
500 g_assert (_wapi_has_shut_down == FALSE);
502 DEBUG ("%s: Creating new handle of type %s", __func__,
503 _wapi_handle_typename[type]);
505 g_assert(!_WAPI_FD_HANDLE(type));
507 thr_ret = mono_mutex_lock (&scan_mutex);
508 g_assert (thr_ret == 0);
510 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
511 /* Try and expand the array, and have another go */
512 int idx = SLOT_INDEX (_wapi_private_handle_count);
513 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
514 break;
517 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
518 _WAPI_HANDLE_INITIAL_COUNT);
520 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
521 _wapi_private_handle_slot_count ++;
524 thr_ret = mono_mutex_unlock (&scan_mutex);
525 g_assert (thr_ret == 0);
527 if (handle_idx == 0) {
528 /* We ran out of slots */
529 handle = _WAPI_HANDLE_INVALID;
530 goto done;
533 /* Make sure we left the space for fd mappings */
534 g_assert (handle_idx >= _wapi_fd_reserve);
536 handle = GUINT_TO_POINTER (handle_idx);
538 DEBUG ("%s: Allocated new handle %p", __func__, handle);
540 if (_WAPI_SHARED_HANDLE(type)) {
541 /* Add the shared section too */
542 guint32 ref;
544 ref = _wapi_handle_new_shared (type, handle_specific);
545 if (ref == 0) {
546 _wapi_handle_collect ();
547 ref = _wapi_handle_new_shared (type, handle_specific);
548 if (ref == 0) {
549 /* FIXME: grow the arrays */
550 handle = _WAPI_HANDLE_INVALID;
551 goto done;
555 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
556 DEBUG ("%s: New shared handle at offset 0x%x", __func__,
557 ref);
560 done:
561 return(handle);
564 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
565 gboolean timestamp)
567 guint32 handle_idx = 0;
568 gpointer handle = INVALID_HANDLE_VALUE;
569 int thr_ret, i, k;
570 struct _WapiHandleShared *shared;
572 g_assert (_wapi_has_shut_down == FALSE);
574 DEBUG ("%s: Creating new handle of type %s to offset %d", __func__,
575 _wapi_handle_typename[type], offset);
577 g_assert(!_WAPI_FD_HANDLE(type));
578 g_assert(_WAPI_SHARED_HANDLE(type));
579 g_assert(offset != 0);
581 shared = &_wapi_shared_layout->handles[offset];
582 if (timestamp) {
583 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
584 /* Bump up the timestamp for this offset */
585 InterlockedExchange ((gint32 *)&shared->timestamp, now);
588 thr_ret = mono_mutex_lock (&scan_mutex);
589 g_assert (thr_ret == 0);
591 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
592 if (_wapi_private_handles [i]) {
593 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
594 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
596 if (handle_data->type == type &&
597 handle_data->u.shared.offset == offset) {
598 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
599 goto first_pass_done;
605 first_pass_done:
606 thr_ret = mono_mutex_unlock (&scan_mutex);
607 g_assert (thr_ret == 0);
609 if (handle != INVALID_HANDLE_VALUE) {
610 _wapi_handle_ref (handle);
612 DEBUG ("%s: Returning old handle %p referencing 0x%x",
613 __func__, handle, offset);
614 return (handle);
617 /* Prevent entries expiring under us as we search */
618 thr_ret = _wapi_handle_lock_shared_handles ();
619 g_assert (thr_ret == 0);
621 if (shared->type == WAPI_HANDLE_UNUSED) {
622 /* Someone deleted this handle while we were working */
623 DEBUG ("%s: Handle at 0x%x unused", __func__, offset);
624 goto done;
627 if (shared->type != type) {
628 DEBUG ("%s: Wrong type at %d 0x%x! Found %s wanted %s",
629 __func__, offset, offset,
630 _wapi_handle_typename[shared->type],
631 _wapi_handle_typename[type]);
632 goto done;
635 thr_ret = mono_mutex_lock (&scan_mutex);
636 g_assert (thr_ret == 0);
638 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
639 /* Try and expand the array, and have another go */
640 int idx = SLOT_INDEX (_wapi_private_handle_count);
641 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
642 _WAPI_HANDLE_INITIAL_COUNT);
644 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
645 _wapi_private_handle_slot_count ++;
648 thr_ret = mono_mutex_unlock (&scan_mutex);
649 g_assert (thr_ret == 0);
651 /* Make sure we left the space for fd mappings */
652 g_assert (handle_idx >= _wapi_fd_reserve);
654 handle = GUINT_TO_POINTER (handle_idx);
656 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
657 InterlockedIncrement ((gint32 *)&shared->handle_refs);
659 DEBUG ("%s: Allocated new handle %p referencing 0x%x (shared refs %d)", __func__, handle, offset, shared->handle_refs);
661 done:
662 _wapi_handle_unlock_shared_handles ();
664 return(handle);
667 static void
668 init_handles_slot (int idx)
670 int thr_ret;
672 thr_ret = mono_mutex_lock (&scan_mutex);
673 g_assert (thr_ret == 0);
675 if (_wapi_private_handles [idx] == NULL) {
676 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
677 _WAPI_HANDLE_INITIAL_COUNT);
678 g_assert (_wapi_private_handles [idx]);
681 thr_ret = mono_mutex_unlock (&scan_mutex);
682 g_assert (thr_ret == 0);
685 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
686 gpointer handle_specific)
688 struct _WapiHandleUnshared *handle;
689 int thr_ret;
691 g_assert (_wapi_has_shut_down == FALSE);
693 DEBUG ("%s: Creating new handle of type %s", __func__,
694 _wapi_handle_typename[type]);
696 g_assert(_WAPI_FD_HANDLE(type));
697 g_assert(!_WAPI_SHARED_HANDLE(type));
699 if (fd >= _wapi_fd_reserve) {
700 DEBUG ("%s: fd %d is too big", __func__, fd);
702 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
705 /* Initialize the array entries on demand */
706 if (_wapi_private_handles [SLOT_INDEX (fd)] == NULL)
707 init_handles_slot (SLOT_INDEX (fd));
709 handle = &_WAPI_PRIVATE_HANDLES(fd);
711 if (handle->type != WAPI_HANDLE_UNUSED) {
712 DEBUG ("%s: fd %d is already in use!", __func__, fd);
713 /* FIXME: clean up this handle? We can't do anything
714 * with the fd, cos thats the new one
718 DEBUG ("%s: Assigning new fd handle %d", __func__, fd);
720 /* Prevent file share entries racing with us, when the file
721 * handle is only half initialised
723 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
724 g_assert(thr_ret == 0);
726 _wapi_handle_init (handle, type, handle_specific);
728 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
730 return(GUINT_TO_POINTER(fd));
733 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
734 gpointer *handle_specific)
736 struct _WapiHandleUnshared *handle_data;
737 guint32 handle_idx = GPOINTER_TO_UINT(handle);
739 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
740 return(FALSE);
743 /* Initialize the array entries on demand */
744 if (_wapi_private_handles [SLOT_INDEX (handle_idx)] == NULL)
745 init_handles_slot (SLOT_INDEX (handle_idx));
747 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
749 if (handle_data->type != type) {
750 return(FALSE);
753 if (handle_specific == NULL) {
754 return(FALSE);
757 if (_WAPI_SHARED_HANDLE(type)) {
758 struct _WapiHandle_shared_ref *ref;
759 struct _WapiHandleShared *shared_handle_data;
761 ref = &handle_data->u.shared;
762 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
764 if (shared_handle_data->type != type) {
765 /* The handle must have been deleted on us
767 return (FALSE);
770 *handle_specific = &shared_handle_data->u;
771 } else {
772 *handle_specific = &handle_data->u;
775 return(TRUE);
778 void
779 _wapi_handle_foreach (WapiHandleType type,
780 gboolean (*on_each)(gpointer test, gpointer user),
781 gpointer user_data)
783 struct _WapiHandleUnshared *handle_data = NULL;
784 gpointer ret = NULL;
785 guint32 i, k;
786 int thr_ret;
788 thr_ret = mono_mutex_lock (&scan_mutex);
789 g_assert (thr_ret == 0);
791 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
792 if (_wapi_private_handles [i]) {
793 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
794 handle_data = &_wapi_private_handles [i][k];
796 if (handle_data->type == type) {
797 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
798 if (on_each (ret, user_data) == TRUE)
799 break;
805 thr_ret = mono_mutex_unlock (&scan_mutex);
806 g_assert (thr_ret == 0);
809 /* This might list some shared handles twice if they are already
810 * opened by this process, and the check function returns FALSE the
811 * first time. Shared handles that are created during the search are
812 * unreffed if the check function returns FALSE, so callers must not
813 * rely on the handle persisting (unless the check function returns
814 * TRUE)
815 * The caller owns the returned handle.
817 gpointer _wapi_search_handle (WapiHandleType type,
818 gboolean (*check)(gpointer test, gpointer user),
819 gpointer user_data,
820 gpointer *handle_specific,
821 gboolean search_shared)
823 struct _WapiHandleUnshared *handle_data = NULL;
824 struct _WapiHandleShared *shared = NULL;
825 gpointer ret = NULL;
826 guint32 i, k;
827 gboolean found = FALSE;
828 int thr_ret;
830 thr_ret = mono_mutex_lock (&scan_mutex);
831 g_assert (thr_ret == 0);
833 for (i = SLOT_INDEX (0); !found && i < _wapi_private_handle_slot_count; i++) {
834 if (_wapi_private_handles [i]) {
835 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
836 handle_data = &_wapi_private_handles [i][k];
838 if (handle_data->type == type) {
839 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
840 if (check (ret, user_data) == TRUE) {
841 _wapi_handle_ref (ret);
842 found = TRUE;
844 if (_WAPI_SHARED_HANDLE (type)) {
845 shared = &_wapi_shared_layout->handles[i];
848 break;
855 thr_ret = mono_mutex_unlock (&scan_mutex);
856 g_assert (thr_ret == 0);
858 if (!found && search_shared && _WAPI_SHARED_HANDLE (type)) {
859 /* Not found yet, so search the shared memory too */
860 DEBUG ("%s: Looking at other shared handles...", __func__);
862 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
863 shared = &_wapi_shared_layout->handles[i];
865 if (shared->type == type) {
866 /* Tell new_from_offset to not
867 * timestamp this handle, because
868 * otherwise it will ping every handle
869 * in the list and they will never
870 * expire
872 ret = _wapi_handle_new_from_offset (type, i,
873 FALSE);
874 if (ret == INVALID_HANDLE_VALUE) {
875 /* This handle was deleted
876 * while we were looking at it
878 continue;
881 DEBUG ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
883 /* It's possible that the shared part
884 * of this handle has now been blown
885 * away (after new_from_offset
886 * successfully opened it,) if its
887 * timestamp is too old. The check
888 * function needs to be aware of this,
889 * and cope if the handle has
890 * vanished.
892 if (check (ret, user_data) == TRUE) {
893 /* Timestamp this handle, but make
894 * sure it still exists first
896 thr_ret = _wapi_handle_lock_shared_handles ();
897 g_assert (thr_ret == 0);
899 if (shared->type == type) {
900 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
901 InterlockedExchange ((gint32 *)&shared->timestamp, now);
903 found = TRUE;
904 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
906 _wapi_handle_unlock_shared_handles ();
907 break;
908 } else {
909 /* It's been deleted,
910 * so just keep
911 * looking
913 _wapi_handle_unlock_shared_handles ();
917 /* This isn't the handle we're looking
918 * for, so drop the reference we took
919 * in _wapi_handle_new_from_offset ()
921 _wapi_handle_unref (ret);
926 if (!found) {
927 ret = NULL;
928 goto done;
931 if(handle_specific != NULL) {
932 if (_WAPI_SHARED_HANDLE(type)) {
933 g_assert(shared->type == type);
935 *handle_specific = &shared->u;
936 } else {
937 *handle_specific = &handle_data->u;
941 done:
942 return(ret);
945 /* Returns the offset of the metadata array, or -1 on error, or 0 for
946 * not found (0 is not a valid offset)
948 gint32 _wapi_search_handle_namespace (WapiHandleType type,
949 gchar *utf8_name)
951 struct _WapiHandleShared *shared_handle_data;
952 guint32 i;
953 gint32 ret = 0;
954 int thr_ret;
956 g_assert(_WAPI_SHARED_HANDLE(type));
958 DEBUG ("%s: Lookup for handle named [%s] type %s", __func__,
959 utf8_name, _wapi_handle_typename[type]);
961 /* Do a handle collection before starting to look, so that any
962 * stale cruft gets removed
964 _wapi_handle_collect ();
966 thr_ret = _wapi_handle_lock_shared_handles ();
967 g_assert (thr_ret == 0);
969 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
970 WapiSharedNamespace *sharedns;
972 shared_handle_data = &_wapi_shared_layout->handles[i];
974 /* Check mutex, event, semaphore, timer, job and
975 * file-mapping object names. So far only mutex,
976 * semaphore and event are implemented.
978 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
979 continue;
982 DEBUG ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
984 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
986 DEBUG ("%s: name is [%s]", __func__, sharedns->name);
988 if (strcmp (sharedns->name, utf8_name) == 0) {
989 if (shared_handle_data->type != type) {
990 /* Its the wrong type, so fail now */
991 DEBUG ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
992 ret = -1;
993 goto done;
994 } else {
995 DEBUG ("%s: handle 0x%x matches name and type", __func__, i);
996 ret = i;
997 goto done;
1002 done:
1003 _wapi_handle_unlock_shared_handles ();
1005 return(ret);
1008 void _wapi_handle_ref (gpointer handle)
1010 guint32 idx = GPOINTER_TO_UINT(handle);
1011 struct _WapiHandleUnshared *handle_data;
1013 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1014 return;
1017 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1018 g_warning ("%s: Attempting to ref unused handle %p", __func__,
1019 handle);
1020 return;
1023 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
1025 InterlockedIncrement ((gint32 *)&handle_data->ref);
1027 /* It's possible for processes to exit before getting around
1028 * to updating timestamps in the collection thread, so if a
1029 * shared handle is reffed do the timestamp here as well just
1030 * to make sure.
1032 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
1033 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
1034 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1035 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
1038 #ifdef DEBUG_REFS
1039 g_message ("%s: %s handle %p ref now %d", __func__,
1040 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1041 handle,
1042 _WAPI_PRIVATE_HANDLES(idx).ref);
1043 #endif
1046 /* The handle must not be locked on entry to this function */
1047 static void _wapi_handle_unref_full (gpointer handle, gboolean ignore_private_busy_handles)
1049 guint32 idx = GPOINTER_TO_UINT(handle);
1050 gboolean destroy = FALSE, early_exit = FALSE;
1051 int thr_ret;
1053 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1054 return;
1057 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1058 g_warning ("%s: Attempting to unref unused handle %p",
1059 __func__, handle);
1060 return;
1063 /* Possible race condition here if another thread refs the
1064 * handle between here and setting the type to UNUSED. I
1065 * could lock a mutex, but I'm not sure that allowing a handle
1066 * reference to reach 0 isn't an application bug anyway.
1068 destroy = (InterlockedDecrement ((gint32 *)&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
1070 #ifdef DEBUG_REFS
1071 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
1072 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1073 handle,
1074 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
1075 #endif
1077 if(destroy==TRUE) {
1078 /* Need to copy the handle info, reset the slot in the
1079 * array, and _only then_ call the close function to
1080 * avoid race conditions (eg file descriptors being
1081 * closed, and another file being opened getting the
1082 * same fd racing the memset())
1084 struct _WapiHandleUnshared handle_data;
1085 struct _WapiHandleShared shared_handle_data;
1086 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
1087 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
1088 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
1090 if (is_shared) {
1091 /* If this is a shared handle we need to take
1092 * the shared lock outside of the scan_mutex
1093 * lock to avoid deadlocks
1095 thr_ret = _wapi_handle_lock_shared_handles ();
1096 g_assert (thr_ret == 0);
1099 thr_ret = mono_mutex_lock (&scan_mutex);
1101 DEBUG ("%s: Destroying handle %p", __func__, handle);
1103 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
1104 sizeof (struct _WapiHandleUnshared));
1106 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
1107 sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
1109 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
1111 if (!is_shared) {
1112 /* Destroy the mutex and cond var. We hope nobody
1113 * tried to grab them between the handle unlock and
1114 * now, but pthreads doesn't have a
1115 * "unlock_and_destroy" atomic function.
1117 thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
1118 /*WARNING gross hack to make cleanup not crash when exiting without the whole runtime teardown.*/
1119 if (thr_ret == EBUSY && ignore_private_busy_handles) {
1120 early_exit = TRUE;
1121 } else {
1122 if (thr_ret != 0)
1123 g_error ("Error destroying handle %p mutex due to %d\n", handle, thr_ret);
1125 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
1126 if (thr_ret == EBUSY && ignore_private_busy_handles)
1127 early_exit = TRUE;
1128 else if (thr_ret != 0)
1129 g_error ("Error destroying handle %p cond var due to %d\n", handle, thr_ret);
1131 } else {
1132 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
1134 memcpy (&shared_handle_data, shared,
1135 sizeof (struct _WapiHandleShared));
1137 /* It's possible that this handle is already
1138 * pointing at a deleted shared section
1140 #ifdef DEBUG_REFS
1141 g_message ("%s: %s handle %p shared refs before dec %d", __func__, _wapi_handle_typename[type], handle, shared->handle_refs);
1142 #endif
1144 if (shared->handle_refs > 0) {
1145 shared->handle_refs--;
1146 if (shared->handle_refs == 0) {
1147 memset (shared, '\0', sizeof (struct _WapiHandleShared));
1152 thr_ret = mono_mutex_unlock (&scan_mutex);
1153 g_assert (thr_ret == 0);
1155 if (early_exit)
1156 return;
1157 if (is_shared) {
1158 _wapi_handle_unlock_shared_handles ();
1161 if (close_func != NULL) {
1162 if (is_shared) {
1163 close_func (handle, &shared_handle_data.u);
1164 } else {
1165 close_func (handle, &handle_data.u);
1171 void _wapi_handle_unref (gpointer handle)
1173 _wapi_handle_unref_full (handle, FALSE);
1176 void _wapi_handle_register_capabilities (WapiHandleType type,
1177 WapiHandleCapability caps)
1179 handle_caps[type] = caps;
1182 gboolean _wapi_handle_test_capabilities (gpointer handle,
1183 WapiHandleCapability caps)
1185 guint32 idx = GPOINTER_TO_UINT(handle);
1186 WapiHandleType type;
1188 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1189 return(FALSE);
1192 type = _WAPI_PRIVATE_HANDLES(idx).type;
1194 DEBUG ("%s: testing 0x%x against 0x%x (%d)", __func__,
1195 handle_caps[type], caps, handle_caps[type] & caps);
1197 return((handle_caps[type] & caps) != 0);
1200 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
1202 if (handle_ops[type] != NULL &&
1203 handle_ops[type]->close != NULL) {
1204 return (handle_ops[type]->close);
1207 return (NULL);
1210 void _wapi_handle_ops_close (gpointer handle, gpointer data)
1212 guint32 idx = GPOINTER_TO_UINT(handle);
1213 WapiHandleType type;
1215 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1216 return;
1219 type = _WAPI_PRIVATE_HANDLES(idx).type;
1221 if (handle_ops[type] != NULL &&
1222 handle_ops[type]->close != NULL) {
1223 handle_ops[type]->close (handle, data);
1227 void _wapi_handle_ops_signal (gpointer handle)
1229 guint32 idx = GPOINTER_TO_UINT(handle);
1230 WapiHandleType type;
1232 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1233 return;
1236 type = _WAPI_PRIVATE_HANDLES(idx).type;
1238 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1239 handle_ops[type]->signal (handle);
1243 gboolean _wapi_handle_ops_own (gpointer handle)
1245 guint32 idx = GPOINTER_TO_UINT(handle);
1246 WapiHandleType type;
1248 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1249 return(FALSE);
1252 type = _WAPI_PRIVATE_HANDLES(idx).type;
1254 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1255 return(handle_ops[type]->own_handle (handle));
1256 } else {
1257 return(FALSE);
1261 gboolean _wapi_handle_ops_isowned (gpointer handle)
1263 guint32 idx = GPOINTER_TO_UINT(handle);
1264 WapiHandleType type;
1266 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1267 return(FALSE);
1270 type = _WAPI_PRIVATE_HANDLES(idx).type;
1272 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1273 return(handle_ops[type]->is_owned (handle));
1274 } else {
1275 return(FALSE);
1279 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout, gboolean alertable)
1281 guint32 idx = GPOINTER_TO_UINT(handle);
1282 WapiHandleType type;
1284 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1285 return(WAIT_FAILED);
1288 type = _WAPI_PRIVATE_HANDLES(idx).type;
1290 if (handle_ops[type] != NULL &&
1291 handle_ops[type]->special_wait != NULL) {
1292 return(handle_ops[type]->special_wait (handle, timeout, alertable));
1293 } else {
1294 return(WAIT_FAILED);
1298 void _wapi_handle_ops_prewait (gpointer handle)
1300 guint32 idx = GPOINTER_TO_UINT (handle);
1301 WapiHandleType type;
1303 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1304 return;
1307 type = _WAPI_PRIVATE_HANDLES (idx).type;
1309 if (handle_ops[type] != NULL &&
1310 handle_ops[type]->prewait != NULL) {
1311 handle_ops[type]->prewait (handle);
1317 * CloseHandle:
1318 * @handle: The handle to release
1320 * Closes and invalidates @handle, releasing any resources it
1321 * consumes. When the last handle to a temporary or non-persistent
1322 * object is closed, that object can be deleted. Closing the same
1323 * handle twice is an error.
1325 * Return value: %TRUE on success, %FALSE otherwise.
1327 gboolean CloseHandle(gpointer handle)
1329 if (handle == NULL) {
1330 /* Problem: because we map file descriptors to the
1331 * same-numbered handle we can't tell the difference
1332 * between a bogus handle and the handle to stdin.
1333 * Assume that it's the console handle if that handle
1334 * exists...
1336 if (_WAPI_PRIVATE_HANDLES (0).type != WAPI_HANDLE_CONSOLE) {
1337 SetLastError (ERROR_INVALID_PARAMETER);
1338 return(FALSE);
1341 if (handle == _WAPI_HANDLE_INVALID){
1342 SetLastError (ERROR_INVALID_PARAMETER);
1343 return(FALSE);
1346 _wapi_handle_unref (handle);
1348 return(TRUE);
1351 /* Lots more to implement here, but this is all we need at the moment */
1352 gboolean DuplicateHandle (gpointer srcprocess, gpointer src,
1353 gpointer targetprocess, gpointer *target,
1354 guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 options G_GNUC_UNUSED)
1356 if (srcprocess != _WAPI_PROCESS_CURRENT ||
1357 targetprocess != _WAPI_PROCESS_CURRENT) {
1358 /* Duplicating other process's handles is not supported */
1359 SetLastError (ERROR_INVALID_HANDLE);
1360 return(FALSE);
1363 if (src == _WAPI_PROCESS_CURRENT) {
1364 *target = _wapi_process_duplicate ();
1365 } else if (src == _WAPI_THREAD_CURRENT) {
1366 g_assert_not_reached ();
1367 } else {
1368 _wapi_handle_ref (src);
1369 *target = src;
1372 return(TRUE);
1375 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1376 gpointer *handles,
1377 gboolean waitall,
1378 guint32 *retcount,
1379 guint32 *lowest)
1381 guint32 count, i, iter=0;
1382 gboolean ret;
1383 int thr_ret;
1384 WapiHandleType type;
1386 /* Lock all the handles, with backoff */
1387 again:
1388 thr_ret = _wapi_handle_lock_shared_handles ();
1389 g_assert (thr_ret == 0);
1391 for(i=0; i<numhandles; i++) {
1392 gpointer handle = handles[i];
1393 guint32 idx = GPOINTER_TO_UINT(handle);
1395 DEBUG ("%s: attempting to lock %p", __func__, handle);
1397 type = _WAPI_PRIVATE_HANDLES(idx).type;
1399 thr_ret = _wapi_handle_trylock_handle (handle);
1401 if (thr_ret != 0) {
1402 /* Bummer */
1404 DEBUG ("%s: attempt failed for %p: %s", __func__,
1405 handle, strerror (thr_ret));
1407 thr_ret = _wapi_handle_unlock_shared_handles ();
1408 g_assert (thr_ret == 0);
1410 while (i--) {
1411 handle = handles[i];
1412 idx = GPOINTER_TO_UINT(handle);
1414 thr_ret = _wapi_handle_unlock_handle (handle);
1415 g_assert (thr_ret == 0);
1418 /* If iter ever reaches 100 the nanosleep will
1419 * return EINVAL immediately, but we have a
1420 * design flaw if that happens.
1422 iter++;
1423 if(iter==100) {
1424 g_warning ("%s: iteration overflow!",
1425 __func__);
1426 iter=1;
1429 DEBUG ("%s: Backing off for %d ms", __func__,
1430 iter*10);
1431 _wapi_handle_spin (10 * iter);
1433 goto again;
1437 DEBUG ("%s: Locked all handles", __func__);
1439 count=0;
1440 *lowest=numhandles;
1442 for(i=0; i<numhandles; i++) {
1443 gpointer handle = handles[i];
1444 guint32 idx = GPOINTER_TO_UINT(handle);
1446 type = _WAPI_PRIVATE_HANDLES(idx).type;
1448 DEBUG ("%s: Checking handle %p", __func__, handle);
1450 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1451 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1452 (_WAPI_SHARED_HANDLE(type) &&
1453 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1454 (!_WAPI_SHARED_HANDLE(type) &&
1455 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1456 count++;
1458 DEBUG ("%s: Handle %p signalled", __func__,
1459 handle);
1460 if(*lowest>i) {
1461 *lowest=i;
1466 DEBUG ("%s: %d event handles signalled", __func__, count);
1468 if ((waitall == TRUE && count == numhandles) ||
1469 (waitall == FALSE && count > 0)) {
1470 ret=TRUE;
1471 } else {
1472 ret=FALSE;
1475 DEBUG ("%s: Returning %d", __func__, ret);
1477 *retcount=count;
1479 return(ret);
1482 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1484 guint32 i;
1485 int thr_ret;
1487 thr_ret = _wapi_handle_unlock_shared_handles ();
1488 g_assert (thr_ret == 0);
1490 for(i=0; i<numhandles; i++) {
1491 gpointer handle = handles[i];
1493 DEBUG ("%s: unlocking handle %p", __func__, handle);
1495 thr_ret = _wapi_handle_unlock_handle (handle);
1496 g_assert (thr_ret == 0);
1500 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout, gboolean alertable)
1502 struct timespec fake_timeout;
1503 int ret;
1505 if (!alertable) {
1506 if (timeout)
1507 ret=mono_cond_timedwait (cond, mutex, timeout);
1508 else
1509 ret=mono_cond_wait (cond, mutex);
1510 } else {
1511 _wapi_calc_timeout (&fake_timeout, 100);
1513 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1514 (fake_timeout.tv_sec == timeout->tv_sec &&
1515 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1516 /* Real timeout is less than 100ms time */
1517 ret=mono_cond_timedwait (cond, mutex, timeout);
1518 } else {
1519 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1521 /* Mask the fake timeout, this will cause
1522 * another poll if the cond was not really signaled
1524 if (ret==ETIMEDOUT) {
1525 ret=0;
1530 return(ret);
1533 int _wapi_handle_wait_signal (gboolean poll)
1535 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
1538 int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
1540 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
1543 int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
1545 DEBUG ("%s: waiting for %p", __func__, handle);
1547 return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
1550 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1551 struct timespec *timeout, gboolean alertable, gboolean poll)
1553 DEBUG ("%s: waiting for %p (type %s)", __func__, handle,
1554 _wapi_handle_typename[_wapi_handle_type (handle)]);
1556 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1557 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1558 return (0);
1560 if (timeout != NULL) {
1561 struct timespec fake_timeout;
1562 _wapi_calc_timeout (&fake_timeout, 100);
1564 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1565 (fake_timeout.tv_sec == timeout->tv_sec &&
1566 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1567 /* FIXME: Real timeout is less than
1568 * 100ms time, but is it really worth
1569 * calculating to the exact ms?
1571 _wapi_handle_spin (100);
1573 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1574 return (0);
1575 } else {
1576 return (ETIMEDOUT);
1580 _wapi_handle_spin (100);
1581 return (0);
1583 } else {
1584 guint32 idx = GPOINTER_TO_UINT(handle);
1585 int res;
1586 pthread_cond_t *cond;
1587 mono_mutex_t *mutex;
1589 if (alertable && !wapi_thread_set_wait_handle (handle))
1590 return 0;
1592 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1593 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1595 if (poll) {
1596 /* This is needed when waiting for process handles */
1597 res = timedwait_signal_poll_cond (cond, mutex, timeout, alertable);
1598 } else {
1599 if (timeout)
1600 res = mono_cond_timedwait (cond, mutex, timeout);
1601 else
1602 res = mono_cond_wait (cond, mutex);
1605 if (alertable)
1606 wapi_thread_clear_wait_handle (handle);
1608 return res;
1612 void
1613 _wapi_free_share_info (_WapiFileShare *share_info)
1615 if (!_wapi_shm_enabled ()) {
1616 file_share_hash_lock ();
1617 g_hash_table_remove (file_share_hash, share_info);
1618 file_share_hash_unlock ();
1619 /* The hashtable dtor frees share_info */
1620 } else {
1621 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1625 static gint
1626 wapi_share_info_equal (gconstpointer ka, gconstpointer kb)
1628 const _WapiFileShare *s1 = ka;
1629 const _WapiFileShare *s2 = kb;
1631 return (s1->device == s2->device && s1->inode == s2->inode) ? 1 : 0;
1634 static guint
1635 wapi_share_info_hash (gconstpointer data)
1637 const _WapiFileShare *s = data;
1639 return s->inode;
1642 gboolean _wapi_handle_get_or_set_share (guint64 device, guint64 inode,
1643 guint32 new_sharemode,
1644 guint32 new_access,
1645 guint32 *old_sharemode,
1646 guint32 *old_access,
1647 struct _WapiFileShare **share_info)
1649 struct _WapiFileShare *file_share;
1650 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1651 int thr_ret, i, first_unused = -1;
1652 gboolean exists = FALSE;
1654 /* Prevents entries from expiring under us as we search
1656 thr_ret = _wapi_handle_lock_shared_handles ();
1657 g_assert (thr_ret == 0);
1659 /* Prevent new entries racing with us */
1660 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1661 g_assert (thr_ret == 0);
1663 if (!_wapi_shm_enabled ()) {
1664 _WapiFileShare tmp;
1667 * Instead of allocating a 4MB array, we use a hash table to keep track of this
1668 * info. This is needed even if SHM is disabled, to track sharing inside
1669 * the current process.
1671 if (!file_share_hash) {
1672 file_share_hash = g_hash_table_new_full (wapi_share_info_hash, wapi_share_info_equal, NULL, g_free);
1673 mono_mutex_init_recursive (&file_share_hash_mutex);
1676 tmp.device = device;
1677 tmp.inode = inode;
1679 file_share_hash_lock ();
1681 file_share = g_hash_table_lookup (file_share_hash, &tmp);
1682 if (file_share) {
1683 *old_sharemode = file_share->sharemode;
1684 *old_access = file_share->access;
1685 *share_info = file_share;
1687 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1688 exists = TRUE;
1689 } else {
1690 file_share = g_new0 (_WapiFileShare, 1);
1692 file_share->device = device;
1693 file_share->inode = inode;
1694 file_share->opened_by_pid = _wapi_getpid ();
1695 file_share->sharemode = new_sharemode;
1696 file_share->access = new_access;
1697 file_share->handle_refs = 1;
1698 *share_info = file_share;
1700 g_hash_table_insert (file_share_hash, file_share, file_share);
1703 file_share_hash_unlock ();
1704 } else {
1705 /* If a linear scan gets too slow we'll have to fit a hash
1706 * table onto the shared mem backing store
1708 *share_info = NULL;
1709 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1710 file_share = &_wapi_fileshare_layout->share_info[i];
1712 /* Make a note of an unused slot, in case we need to
1713 * store share info
1715 if (first_unused == -1 && file_share->handle_refs == 0) {
1716 first_unused = i;
1717 continue;
1720 if (file_share->handle_refs == 0) {
1721 continue;
1724 if (file_share->device == device &&
1725 file_share->inode == inode) {
1726 *old_sharemode = file_share->sharemode;
1727 *old_access = file_share->access;
1728 *share_info = file_share;
1730 /* Increment the reference count while we
1731 * still have sole access to the shared area.
1732 * This makes the increment atomic wrt
1733 * collections
1735 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1737 exists = TRUE;
1738 break;
1742 if (!exists) {
1743 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1744 /* No more space */
1745 } else {
1746 if (first_unused == -1) {
1747 file_share = &_wapi_fileshare_layout->share_info[++i];
1748 _wapi_fileshare_layout->hwm = i;
1749 } else {
1750 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1753 file_share->device = device;
1754 file_share->inode = inode;
1755 file_share->opened_by_pid = _wapi_getpid ();
1756 file_share->sharemode = new_sharemode;
1757 file_share->access = new_access;
1758 file_share->handle_refs = 1;
1759 *share_info = file_share;
1763 if (*share_info != NULL) {
1764 InterlockedExchange ((gint32 *)&(*share_info)->timestamp, now);
1768 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1770 _wapi_handle_unlock_shared_handles ();
1772 return(exists);
1775 /* If we don't have the info in /proc, check if the process that
1776 * opened this share info is still there (it's not a perfect method,
1777 * due to pid reuse)
1779 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1781 #if defined(__native_client__)
1782 g_assert_not_reached ();
1783 #else
1784 if (kill (share_info->opened_by_pid, 0) == -1 &&
1785 (errno == ESRCH ||
1786 errno == EPERM)) {
1787 /* It's gone completely (or there's a new process
1788 * owned by someone else) so mark this share info as
1789 * dead
1791 DEBUG ("%s: Didn't find it, destroying entry", __func__);
1793 _wapi_free_share_info (share_info);
1795 #endif
1798 #ifdef __linux__
1799 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1800 * question. If there are none, reset the share info.
1802 * This implementation is Linux-specific; legacy systems will have to
1803 * implement their own ways of finding out if a particular file is
1804 * open by a process.
1806 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1808 gboolean found = FALSE, proc_fds = FALSE;
1809 int thr_ret, i;
1811 /* Prevents entries from expiring under us if we remove this
1812 * one
1814 thr_ret = _wapi_handle_lock_shared_handles ();
1815 g_assert (thr_ret == 0);
1817 /* Prevent new entries racing with us */
1818 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1819 g_assert (thr_ret == 0);
1821 /* If there is no /proc, there's nothing more we can do here */
1822 if (access ("/proc", F_OK) == -1) {
1823 _wapi_handle_check_share_by_pid (share_info);
1824 goto done;
1827 /* If there's another handle that thinks it owns this fd, then even
1828 * if the fd has been closed behind our back consider it still owned.
1829 * See bugs 75764 and 75891
1831 for (i = 0; i < _wapi_fd_reserve; i++) {
1832 if (_wapi_private_handles [SLOT_INDEX (i)]) {
1833 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1835 if (i != fd &&
1836 handle->type == WAPI_HANDLE_FILE) {
1837 struct _WapiHandle_file *file_handle = &handle->u.file;
1839 if (file_handle->share_info == share_info) {
1840 DEBUG ("%s: handle 0x%x has this file open!",
1841 __func__, i);
1843 goto done;
1849 if (proc_fds == FALSE) {
1850 _wapi_handle_check_share_by_pid (share_info);
1851 } else if (found == FALSE) {
1852 /* Blank out this entry, as it is stale */
1853 DEBUG ("%s: Didn't find it, destroying entry", __func__);
1855 _wapi_free_share_info (share_info);
1858 done:
1859 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1861 _wapi_handle_unlock_shared_handles ();
1863 #else
1865 // Other implementations (non-Linux)
1867 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1869 int thr_ret;
1871 /* Prevents entries from expiring under us if we remove this
1872 * one */
1873 thr_ret = _wapi_handle_lock_shared_handles ();
1874 g_assert (thr_ret == 0);
1876 /* Prevent new entries racing with us */
1877 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1878 g_assert (thr_ret == 0);
1880 _wapi_handle_check_share_by_pid (share_info);
1882 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1883 _wapi_handle_unlock_shared_handles ();
1885 #endif
1887 void _wapi_handle_dump (void)
1889 struct _WapiHandleUnshared *handle_data;
1890 guint32 i, k;
1891 int thr_ret;
1893 thr_ret = mono_mutex_lock (&scan_mutex);
1894 g_assert (thr_ret == 0);
1896 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1897 if (_wapi_private_handles [i]) {
1898 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1899 handle_data = &_wapi_private_handles [i][k];
1901 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1902 continue;
1905 g_print ("%3x [%7s] %s %d ",
1906 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1907 _wapi_handle_typename[handle_data->type],
1908 handle_data->signalled?"Sg":"Un",
1909 handle_data->ref);
1910 handle_details[handle_data->type](&handle_data->u);
1911 g_print ("\n");
1916 thr_ret = mono_mutex_unlock (&scan_mutex);
1917 g_assert (thr_ret == 0);
1920 static void _wapi_shared_details (gpointer handle_info)
1922 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1924 g_print ("offset: 0x%x", shared->offset);
1927 void _wapi_handle_update_refs (void)
1929 guint32 i, k;
1930 int thr_ret;
1931 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1933 thr_ret = _wapi_handle_lock_shared_handles ();
1934 g_assert (thr_ret == 0);
1936 /* Prevent file share entries racing with us */
1937 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1938 g_assert(thr_ret == 0);
1940 thr_ret = mono_mutex_lock (&scan_mutex);
1942 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1943 if (_wapi_private_handles [i]) {
1944 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1945 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1947 if (_WAPI_SHARED_HANDLE(handle->type)) {
1948 struct _WapiHandleShared *shared_data;
1950 DEBUG ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1952 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
1954 DEBUG ("%s: (%d) Updating timestamp of handle 0x%x", __func__, _wapi_getpid (), handle->u.shared.offset);
1956 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
1957 } else if (handle->type == WAPI_HANDLE_FILE) {
1958 struct _WapiHandle_file *file_handle = &handle->u.file;
1960 DEBUG ("%s: (%d) handle 0x%x is FILE", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1962 g_assert (file_handle->share_info != NULL);
1964 DEBUG ("%s: (%d) Inc refs on fileshare 0x%x", __func__, _wapi_getpid (), (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1966 InterlockedExchange ((gint32 *)&file_handle->share_info->timestamp, now);
1972 thr_ret = mono_mutex_unlock (&scan_mutex);
1973 g_assert (thr_ret == 0);
1975 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1977 _wapi_handle_unlock_shared_handles ();