2010-02-13 Jb Evain <jbevain@novell.com>
[mono-project.git] / mono / io-layer / handles.c
blobd103db010bf18bcc306d8998847a2190c3373b67
1 /*
2 * handles.c: Generic and internal operations on handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2006 Novell, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #ifdef HAVE_SYS_SOCKET_H
18 # include <sys/socket.h>
19 #endif
20 #ifdef HAVE_SYS_UN_H
21 # include <sys/un.h>
22 #endif
23 #ifdef HAVE_SYS_MMAN_H
24 # include <sys/mman.h>
25 #endif
26 #ifdef HAVE_DIRENT_H
27 # include <dirent.h>
28 #endif
29 #include <sys/stat.h>
31 #include <mono/utils/gc_wrapper.h>
33 #include <mono/io-layer/wapi.h>
34 #include <mono/io-layer/wapi-private.h>
35 #include <mono/io-layer/handles-private.h>
36 #include <mono/io-layer/mono-mutex.h>
37 #include <mono/io-layer/misc-private.h>
38 #include <mono/io-layer/shared.h>
39 #include <mono/io-layer/collection.h>
40 #include <mono/io-layer/process-private.h>
41 #include <mono/io-layer/critical-section-private.h>
43 #undef DEBUG
44 #undef DEBUG_REFS
46 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
48 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
49 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
50 NULL,
51 &_wapi_file_ops,
52 &_wapi_console_ops,
53 &_wapi_thread_ops,
54 &_wapi_sem_ops,
55 &_wapi_mutex_ops,
56 &_wapi_event_ops,
57 #ifndef DISABLE_SOCKETS
58 &_wapi_socket_ops,
59 #endif
60 &_wapi_find_ops,
61 &_wapi_process_ops,
62 &_wapi_pipe_ops,
63 &_wapi_namedmutex_ops,
64 &_wapi_namedsem_ops,
65 &_wapi_namedevent_ops,
68 static void _wapi_shared_details (gpointer handle_info);
70 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
71 NULL,
72 _wapi_file_details,
73 _wapi_console_details,
74 _wapi_shared_details, /* thread */
75 _wapi_sem_details,
76 _wapi_mutex_details,
77 _wapi_event_details,
78 NULL, /* Nothing useful to see in a socket handle */
79 NULL, /* Nothing useful to see in a find handle */
80 _wapi_shared_details, /* process */
81 _wapi_pipe_details,
82 _wapi_shared_details, /* namedmutex */
83 _wapi_shared_details, /* namedsem */
84 _wapi_shared_details, /* namedevent */
87 const char *_wapi_handle_typename[] = {
88 "Unused",
89 "File",
90 "Console",
91 "Thread",
92 "Sem",
93 "Mutex",
94 "Event",
95 "Socket",
96 "Find",
97 "Process",
98 "Pipe",
99 "N.Mutex",
100 "N.Sem",
101 "N.Event",
102 "Error!!"
106 * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
107 * If 4M handles are not enough... Oh, well... we will crash.
109 #define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT)
110 #define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT)
112 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
113 static guint32 _wapi_private_handle_count = 0;
114 static guint32 _wapi_private_handle_slot_count = 0;
116 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
117 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
119 guint32 _wapi_fd_reserve;
122 * This is an internal handle which is used for handling waiting for multiple handles.
123 * Threads which wait for multiple handles wait on this one handle, and when a handle
124 * is signalled, this handle is signalled too.
126 static gpointer _wapi_global_signal_handle;
128 /* Point to the mutex/cond inside _wapi_global_signal_handle */
129 mono_mutex_t *_wapi_global_signal_mutex;
130 pthread_cond_t *_wapi_global_signal_cond;
132 int _wapi_sem_id;
133 gboolean _wapi_has_shut_down = FALSE;
135 /* Use this instead of getpid(), to cope with linuxthreads. It's a
136 * function rather than a variable lookup because we need to get at
137 * this before share_init() might have been called.
139 static pid_t _wapi_pid;
140 static mono_once_t pid_init_once = MONO_ONCE_INIT;
142 static gpointer _wapi_handle_real_new (WapiHandleType type, gpointer handle_specific);
144 static void pid_init (void)
146 _wapi_pid = getpid ();
149 pid_t _wapi_getpid (void)
151 mono_once (&pid_init_once, pid_init);
153 return(_wapi_pid);
157 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
159 static void handle_cleanup (void)
161 int i, j, k;
163 _wapi_process_signal_self ();
165 /* Every shared handle we were using ought really to be closed
166 * by now, but to make sure just blow them all away. The
167 * exiting finalizer thread in particular races us to the
168 * program exit and doesn't always win, so it can be left
169 * cluttering up the shared file. Anything else left over is
170 * really a bug.
172 for(i = SLOT_INDEX (0); _wapi_private_handles[i] != NULL; i++) {
173 for(j = SLOT_OFFSET (0); j < _WAPI_HANDLE_INITIAL_COUNT; j++) {
174 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles[i][j];
175 int type = handle_data->type;
178 if (_WAPI_SHARED_HANDLE (type)) {
179 gpointer handle = GINT_TO_POINTER (i*_WAPI_HANDLE_INITIAL_COUNT+j);
181 if (type == WAPI_HANDLE_THREAD) {
182 /* Special-case thread handles
183 * because they need extra
184 * cleanup. This also avoids
185 * a race condition between
186 * the application exit and
187 * the finalizer thread - if
188 * it finishes up between now
189 * and actual app termination
190 * it will find all its handle
191 * details have been blown
192 * away, so this sets those
193 * anyway.
195 _wapi_thread_set_termination_details (handle, 0);
198 for(k = handle_data->ref; k > 0; k--) {
199 #ifdef DEBUG
200 g_message ("%s: unreffing %s handle %p", __func__, _wapi_handle_typename[type], handle);
201 #endif
203 _wapi_handle_unref (handle);
209 _wapi_shm_semaphores_remove ();
212 void _wapi_cleanup ()
214 g_assert (_wapi_has_shut_down == FALSE);
216 _wapi_has_shut_down = TRUE;
218 _wapi_critical_section_cleanup ();
219 _wapi_error_cleanup ();
220 _wapi_thread_cleanup ();
223 static mono_once_t shared_init_once = MONO_ONCE_INIT;
224 static void shared_init (void)
226 g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
227 == WAPI_HANDLE_COUNT);
229 _wapi_fd_reserve = getdtablesize();
231 /* This is needed by the code in _wapi_handle_new_internal */
232 _wapi_fd_reserve = (_wapi_fd_reserve + (_WAPI_HANDLE_INITIAL_COUNT - 1)) & ~(_WAPI_HANDLE_INITIAL_COUNT - 1);
234 do {
236 * The entries in _wapi_private_handles reserved for fds are allocated lazily to
237 * save memory.
240 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
241 _WAPI_HANDLE_INITIAL_COUNT);
244 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
245 _wapi_private_handle_slot_count ++;
246 } while(_wapi_fd_reserve > _wapi_private_handle_count);
248 _wapi_shm_semaphores_init ();
250 _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
251 g_assert (_wapi_shared_layout != NULL);
253 _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
254 g_assert (_wapi_fileshare_layout != NULL);
256 #if !defined (DISABLE_SHARED_HANDLES)
257 if (!g_getenv ("MONO_DISABLE_SHM"))
258 _wapi_collection_init ();
259 #endif
261 /* Can't call wapi_handle_new as it calls us recursively */
262 _wapi_global_signal_handle = _wapi_handle_real_new (WAPI_HANDLE_EVENT, NULL);
264 _wapi_global_signal_cond = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_cond;
265 _wapi_global_signal_mutex = &_WAPI_PRIVATE_HANDLES (GPOINTER_TO_UINT (_wapi_global_signal_handle)).signal_mutex;
267 /* Using g_atexit here instead of an explicit function call in
268 * a cleanup routine lets us cope when a third-party library
269 * calls exit (eg if an X client loses the connection to its
270 * server.)
272 g_atexit (handle_cleanup);
275 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
276 WapiHandleType type,
277 gpointer handle_specific)
279 g_assert (_wapi_has_shut_down == FALSE);
281 handle->type = type;
282 handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
283 handle->signalled = FALSE;
284 handle->handle_refs = 1;
286 if (handle_specific != NULL) {
287 memcpy (&handle->u, handle_specific, sizeof (handle->u));
291 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
292 WapiHandleType type, gpointer handle_specific)
294 int thr_ret;
296 g_assert (_wapi_has_shut_down == FALSE);
298 handle->type = type;
299 handle->signalled = FALSE;
300 handle->ref = 1;
302 if (!_WAPI_SHARED_HANDLE(type)) {
303 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
304 g_assert (thr_ret == 0);
306 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
307 g_assert (thr_ret == 0);
309 if (handle_specific != NULL) {
310 memcpy (&handle->u, handle_specific,
311 sizeof (handle->u));
316 static guint32 _wapi_handle_new_shared (WapiHandleType type,
317 gpointer handle_specific)
319 guint32 offset;
320 static guint32 last = 1;
321 int thr_ret;
323 g_assert (_wapi_has_shut_down == FALSE);
325 /* Leave the first slot empty as a guard */
326 again:
327 /* FIXME: expandable array */
328 for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
329 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
331 if(handle->type == WAPI_HANDLE_UNUSED) {
332 thr_ret = _wapi_handle_lock_shared_handles ();
333 g_assert (thr_ret == 0);
335 if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
336 last = offset + 1;
338 _wapi_handle_init_shared (handle, type,
339 handle_specific);
341 _wapi_handle_unlock_shared_handles ();
343 return(offset);
344 } else {
345 /* Someone else beat us to it, just
346 * continue looking
350 _wapi_handle_unlock_shared_handles ();
354 if(last > 1) {
355 /* Try again from the beginning */
356 last = 1;
357 goto again;
360 /* Will need to expand the array. The caller will sort it out */
362 return(0);
366 * _wapi_handle_new_internal:
367 * @type: Init handle to this type
369 * Search for a free handle and initialize it. Return the handle on
370 * success and 0 on failure. This is only called from
371 * _wapi_handle_new, and scan_mutex must be held.
373 static guint32 _wapi_handle_new_internal (WapiHandleType type,
374 gpointer handle_specific)
376 guint32 i, k, count;
377 static guint32 last = 0;
378 gboolean retry = FALSE;
380 g_assert (_wapi_has_shut_down == FALSE);
382 /* A linear scan should be fast enough. Start from the last
383 * allocation, assuming that handles are allocated more often
384 * than they're freed. Leave the space reserved for file
385 * descriptors
388 if (last < _wapi_fd_reserve) {
389 last = _wapi_fd_reserve;
390 } else {
391 retry = TRUE;
394 again:
395 count = last;
396 for(i = SLOT_INDEX (count); i < _wapi_private_handle_slot_count; i++) {
397 if (_wapi_private_handles [i]) {
398 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
399 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
401 if(handle->type == WAPI_HANDLE_UNUSED) {
402 last = count + 1;
404 _wapi_handle_init (handle, type, handle_specific);
405 return (count);
407 count++;
412 if(retry && last > _wapi_fd_reserve) {
413 /* Try again from the beginning */
414 last = _wapi_fd_reserve;
415 goto again;
418 /* Will need to expand the array. The caller will sort it out */
420 return(0);
423 static gpointer _wapi_handle_real_new (WapiHandleType type, gpointer handle_specific)
425 guint32 handle_idx = 0;
426 gpointer handle;
427 int thr_ret;
429 #ifdef DEBUG
430 g_message ("%s: Creating new handle of type %s", __func__,
431 _wapi_handle_typename[type]);
432 #endif
434 g_assert(!_WAPI_FD_HANDLE(type));
436 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
437 (void *)&scan_mutex);
438 thr_ret = mono_mutex_lock (&scan_mutex);
439 g_assert (thr_ret == 0);
441 while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
442 /* Try and expand the array, and have another go */
443 int idx = SLOT_INDEX (_wapi_private_handle_count);
444 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
445 break;
448 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
449 _WAPI_HANDLE_INITIAL_COUNT);
451 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
452 _wapi_private_handle_slot_count ++;
455 thr_ret = mono_mutex_unlock (&scan_mutex);
456 g_assert (thr_ret == 0);
457 pthread_cleanup_pop (0);
459 if (handle_idx == 0) {
460 /* We ran out of slots */
461 handle = _WAPI_HANDLE_INVALID;
462 goto done;
465 /* Make sure we left the space for fd mappings */
466 g_assert (handle_idx >= _wapi_fd_reserve);
468 handle = GUINT_TO_POINTER (handle_idx);
470 #ifdef DEBUG
471 g_message ("%s: Allocated new handle %p", __func__, handle);
472 #endif
474 if (_WAPI_SHARED_HANDLE(type)) {
475 /* Add the shared section too */
476 guint32 ref;
478 ref = _wapi_handle_new_shared (type, handle_specific);
479 if (ref == 0) {
480 _wapi_handle_collect ();
481 _wapi_process_reap ();
482 ref = _wapi_handle_new_shared (type, handle_specific);
483 if (ref == 0) {
484 /* FIXME: grow the arrays */
485 handle = _WAPI_HANDLE_INVALID;
486 goto done;
490 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
491 #ifdef DEBUG
492 g_message ("%s: New shared handle at offset 0x%x", __func__,
493 ref);
494 #endif
497 done:
498 return(handle);
501 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
503 g_assert (_wapi_has_shut_down == FALSE);
505 mono_once (&shared_init_once, shared_init);
507 return _wapi_handle_real_new (type, handle_specific);
510 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
511 gboolean timestamp)
513 guint32 handle_idx = 0;
514 gpointer handle = INVALID_HANDLE_VALUE;
515 int thr_ret, i, k;
516 struct _WapiHandleShared *shared;
517 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
519 g_assert (_wapi_has_shut_down == FALSE);
521 mono_once (&shared_init_once, shared_init);
523 #ifdef DEBUG
524 g_message ("%s: Creating new handle of type %s to offset %d", __func__,
525 _wapi_handle_typename[type], offset);
526 #endif
528 g_assert(!_WAPI_FD_HANDLE(type));
529 g_assert(_WAPI_SHARED_HANDLE(type));
530 g_assert(offset != 0);
532 shared = &_wapi_shared_layout->handles[offset];
533 if (timestamp) {
534 /* Bump up the timestamp for this offset */
535 InterlockedExchange ((gint32 *)&shared->timestamp, now);
538 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
539 (void *)&scan_mutex);
540 thr_ret = mono_mutex_lock (&scan_mutex);
541 g_assert (thr_ret == 0);
543 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
544 if (_wapi_private_handles [i]) {
545 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
546 struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
548 if (handle_data->type == type &&
549 handle_data->u.shared.offset == offset) {
550 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
551 goto first_pass_done;
557 first_pass_done:
558 thr_ret = mono_mutex_unlock (&scan_mutex);
559 g_assert (thr_ret == 0);
560 pthread_cleanup_pop (0);
562 if (handle != INVALID_HANDLE_VALUE) {
563 _wapi_handle_ref (handle);
565 #ifdef DEBUG
566 g_message ("%s: Returning old handle %p referencing 0x%x",
567 __func__, handle, offset);
568 #endif
569 return (handle);
572 /* Prevent entries expiring under us as we search */
573 thr_ret = _wapi_handle_lock_shared_handles ();
574 g_assert (thr_ret == 0);
576 if (shared->type == WAPI_HANDLE_UNUSED) {
577 /* Someone deleted this handle while we were working */
578 #ifdef DEBUG
579 g_message ("%s: Handle at 0x%x unused", __func__, offset);
580 #endif
581 goto done;
584 if (shared->type != type) {
585 #ifdef DEBUG
586 g_message ("%s: Wrong type at %d 0x%x! Found %s wanted %s",
587 __func__, offset, offset,
588 _wapi_handle_typename[shared->type],
589 _wapi_handle_typename[type]);
590 #endif
591 goto done;
594 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
595 (void *)&scan_mutex);
596 thr_ret = mono_mutex_lock (&scan_mutex);
597 g_assert (thr_ret == 0);
599 while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
600 /* Try and expand the array, and have another go */
601 int idx = SLOT_INDEX (_wapi_private_handle_count);
602 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
603 _WAPI_HANDLE_INITIAL_COUNT);
605 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
606 _wapi_private_handle_slot_count ++;
609 thr_ret = mono_mutex_unlock (&scan_mutex);
610 g_assert (thr_ret == 0);
611 pthread_cleanup_pop (0);
613 /* Make sure we left the space for fd mappings */
614 g_assert (handle_idx >= _wapi_fd_reserve);
616 handle = GUINT_TO_POINTER (handle_idx);
618 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
619 InterlockedIncrement ((gint32 *)&shared->handle_refs);
621 #ifdef DEBUG
622 g_message ("%s: Allocated new handle %p referencing 0x%x (shared refs %d)", __func__, handle, offset, shared->handle_refs);
623 #endif
625 done:
626 _wapi_handle_unlock_shared_handles ();
628 return(handle);
631 static void
632 init_handles_slot (int idx)
634 int thr_ret;
636 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
637 (void *)&scan_mutex);
638 thr_ret = mono_mutex_lock (&scan_mutex);
639 g_assert (thr_ret == 0);
641 if (_wapi_private_handles [idx] == NULL) {
642 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
643 _WAPI_HANDLE_INITIAL_COUNT);
644 g_assert (_wapi_private_handles [idx]);
647 thr_ret = mono_mutex_unlock (&scan_mutex);
648 g_assert (thr_ret == 0);
649 pthread_cleanup_pop (0);
652 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
653 gpointer handle_specific)
655 struct _WapiHandleUnshared *handle;
656 int thr_ret;
658 g_assert (_wapi_has_shut_down == FALSE);
660 mono_once (&shared_init_once, shared_init);
662 #ifdef DEBUG
663 g_message ("%s: Creating new handle of type %s", __func__,
664 _wapi_handle_typename[type]);
665 #endif
667 g_assert(_WAPI_FD_HANDLE(type));
668 g_assert(!_WAPI_SHARED_HANDLE(type));
670 if (fd >= _wapi_fd_reserve) {
671 #ifdef DEBUG
672 g_message ("%s: fd %d is too big", __func__, fd);
673 #endif
675 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
678 /* Initialize the array entries on demand */
679 if (_wapi_private_handles [SLOT_INDEX (fd)] == NULL)
680 init_handles_slot (SLOT_INDEX (fd));
682 handle = &_WAPI_PRIVATE_HANDLES(fd);
684 if (handle->type != WAPI_HANDLE_UNUSED) {
685 #ifdef DEBUG
686 g_message ("%s: fd %d is already in use!", __func__, fd);
687 #endif
688 /* FIXME: clean up this handle? We can't do anything
689 * with the fd, cos thats the new one
693 #ifdef DEBUG
694 g_message ("%s: Assigning new fd handle %d", __func__, fd);
695 #endif
697 /* Prevent file share entries racing with us, when the file
698 * handle is only half initialised
700 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
701 g_assert(thr_ret == 0);
703 _wapi_handle_init (handle, type, handle_specific);
705 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
707 return(GUINT_TO_POINTER(fd));
710 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
711 gpointer *handle_specific)
713 struct _WapiHandleUnshared *handle_data;
714 guint32 handle_idx = GPOINTER_TO_UINT(handle);
716 if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
717 return(FALSE);
720 /* Initialize the array entries on demand */
721 if (_wapi_private_handles [SLOT_INDEX (handle_idx)] == NULL)
722 init_handles_slot (SLOT_INDEX (handle_idx));
724 handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
726 if (handle_data->type != type) {
727 return(FALSE);
730 if (handle_specific == NULL) {
731 return(FALSE);
734 if (_WAPI_SHARED_HANDLE(type)) {
735 struct _WapiHandle_shared_ref *ref;
736 struct _WapiHandleShared *shared_handle_data;
738 ref = &handle_data->u.shared;
739 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
741 if (shared_handle_data->type != type) {
742 /* The handle must have been deleted on us
744 return (FALSE);
747 *handle_specific = &shared_handle_data->u;
748 } else {
749 *handle_specific = &handle_data->u;
752 return(TRUE);
755 void
756 _wapi_handle_foreach (WapiHandleType type,
757 gboolean (*on_each)(gpointer test, gpointer user),
758 gpointer user_data)
760 struct _WapiHandleUnshared *handle_data = NULL;
761 gpointer ret = NULL;
762 guint32 i, k;
763 int thr_ret;
765 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
766 (void *)&scan_mutex);
767 thr_ret = mono_mutex_lock (&scan_mutex);
768 g_assert (thr_ret == 0);
770 for (i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
771 if (_wapi_private_handles [i]) {
772 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
773 handle_data = &_wapi_private_handles [i][k];
775 if (handle_data->type == type) {
776 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
777 if (on_each (ret, user_data) == TRUE)
778 break;
784 thr_ret = mono_mutex_unlock (&scan_mutex);
785 g_assert (thr_ret == 0);
786 pthread_cleanup_pop (0);
789 /* This might list some shared handles twice if they are already
790 * opened by this process, and the check function returns FALSE the
791 * first time. Shared handles that are created during the search are
792 * unreffed if the check function returns FALSE, so callers must not
793 * rely on the handle persisting (unless the check function returns
794 * TRUE)
796 gpointer _wapi_search_handle (WapiHandleType type,
797 gboolean (*check)(gpointer test, gpointer user),
798 gpointer user_data,
799 gpointer *handle_specific,
800 gboolean search_shared)
802 struct _WapiHandleUnshared *handle_data = NULL;
803 struct _WapiHandleShared *shared = NULL;
804 gpointer ret = NULL;
805 guint32 i, k;
806 gboolean found = FALSE;
807 int thr_ret;
809 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
810 (void *)&scan_mutex);
811 thr_ret = mono_mutex_lock (&scan_mutex);
812 g_assert (thr_ret == 0);
814 for (i = SLOT_INDEX (0); !found && i < _wapi_private_handle_slot_count; i++) {
815 if (_wapi_private_handles [i]) {
816 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
817 handle_data = &_wapi_private_handles [i][k];
819 if (handle_data->type == type) {
820 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
821 if (check (ret, user_data) == TRUE) {
822 _wapi_handle_ref (ret);
823 found = TRUE;
825 if (_WAPI_SHARED_HANDLE (type)) {
826 shared = &_wapi_shared_layout->handles[i];
829 break;
836 thr_ret = mono_mutex_unlock (&scan_mutex);
837 g_assert (thr_ret == 0);
838 pthread_cleanup_pop (0);
840 if (!found && search_shared && _WAPI_SHARED_HANDLE (type)) {
841 /* Not found yet, so search the shared memory too */
842 #ifdef DEBUG
843 g_message ("%s: Looking at other shared handles...", __func__);
844 #endif
846 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
847 shared = &_wapi_shared_layout->handles[i];
849 if (shared->type == type) {
850 /* Tell new_from_offset to not
851 * timestamp this handle, because
852 * otherwise it will ping every handle
853 * in the list and they will never
854 * expire
856 ret = _wapi_handle_new_from_offset (type, i,
857 FALSE);
858 if (ret == INVALID_HANDLE_VALUE) {
859 /* This handle was deleted
860 * while we were looking at it
862 continue;
865 #ifdef DEBUG
866 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
867 #endif
869 /* It's possible that the shared part
870 * of this handle has now been blown
871 * away (after new_from_offset
872 * successfully opened it,) if its
873 * timestamp is too old. The check
874 * function needs to be aware of this,
875 * and cope if the handle has
876 * vanished.
878 if (check (ret, user_data) == TRUE) {
879 /* Timestamp this handle, but make
880 * sure it still exists first
882 thr_ret = _wapi_handle_lock_shared_handles ();
883 g_assert (thr_ret == 0);
885 if (shared->type == type) {
886 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
887 InterlockedExchange ((gint32 *)&shared->timestamp, now);
889 found = TRUE;
890 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
892 _wapi_handle_unlock_shared_handles ();
893 break;
894 } else {
895 /* It's been deleted,
896 * so just keep
897 * looking
899 _wapi_handle_unlock_shared_handles ();
903 /* This isn't the handle we're looking
904 * for, so drop the reference we took
905 * in _wapi_handle_new_from_offset ()
907 _wapi_handle_unref (ret);
912 if (!found) {
913 ret = NULL;
914 goto done;
917 if(handle_specific != NULL) {
918 if (_WAPI_SHARED_HANDLE(type)) {
919 g_assert(shared->type == type);
921 *handle_specific = &shared->u;
922 } else {
923 *handle_specific = &handle_data->u;
927 done:
928 return(ret);
931 /* Returns the offset of the metadata array, or -1 on error, or 0 for
932 * not found (0 is not a valid offset)
934 gint32 _wapi_search_handle_namespace (WapiHandleType type,
935 gchar *utf8_name)
937 struct _WapiHandleShared *shared_handle_data;
938 guint32 i;
939 gint32 ret = 0;
940 int thr_ret;
942 g_assert(_WAPI_SHARED_HANDLE(type));
944 #ifdef DEBUG
945 g_message ("%s: Lookup for handle named [%s] type %s", __func__,
946 utf8_name, _wapi_handle_typename[type]);
947 #endif
949 /* Do a handle collection before starting to look, so that any
950 * stale cruft gets removed
952 _wapi_handle_collect ();
954 thr_ret = _wapi_handle_lock_shared_handles ();
955 g_assert (thr_ret == 0);
957 for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
958 WapiSharedNamespace *sharedns;
960 shared_handle_data = &_wapi_shared_layout->handles[i];
962 /* Check mutex, event, semaphore, timer, job and
963 * file-mapping object names. So far only mutex,
964 * semaphore and event are implemented.
966 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
967 continue;
970 #ifdef DEBUG
971 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
972 #endif
974 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
976 #ifdef DEBUG
977 g_message ("%s: name is [%s]", __func__, sharedns->name);
978 #endif
980 if (strcmp (sharedns->name, utf8_name) == 0) {
981 if (shared_handle_data->type != type) {
982 /* Its the wrong type, so fail now */
983 #ifdef DEBUG
984 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
985 #endif
986 ret = -1;
987 goto done;
988 } else {
989 #ifdef DEBUG
990 g_message ("%s: handle 0x%x matches name and type", __func__, i);
991 #endif
992 ret = i;
993 goto done;
998 done:
999 _wapi_handle_unlock_shared_handles ();
1001 return(ret);
1004 void _wapi_handle_ref (gpointer handle)
1006 guint32 idx = GPOINTER_TO_UINT(handle);
1007 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1008 struct _WapiHandleUnshared *handle_data;
1010 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1011 return;
1014 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1015 g_warning ("%s: Attempting to ref unused handle %p", __func__,
1016 handle);
1017 return;
1020 handle_data = &_WAPI_PRIVATE_HANDLES(idx);
1022 InterlockedIncrement ((gint32 *)&handle_data->ref);
1024 /* It's possible for processes to exit before getting around
1025 * to updating timestamps in the collection thread, so if a
1026 * shared handle is reffed do the timestamp here as well just
1027 * to make sure.
1029 if (_WAPI_SHARED_HANDLE(handle_data->type)) {
1030 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
1032 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
1035 #ifdef DEBUG_REFS
1036 g_message ("%s: %s handle %p ref now %d", __func__,
1037 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1038 handle,
1039 _WAPI_PRIVATE_HANDLES(idx).ref);
1040 #endif
1043 /* The handle must not be locked on entry to this function */
1044 void _wapi_handle_unref (gpointer handle)
1046 guint32 idx = GPOINTER_TO_UINT(handle);
1047 gboolean destroy = FALSE;
1048 int thr_ret;
1050 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1051 return;
1054 if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
1055 g_warning ("%s: Attempting to unref unused handle %p",
1056 __func__, handle);
1057 return;
1060 /* Possible race condition here if another thread refs the
1061 * handle between here and setting the type to UNUSED. I
1062 * could lock a mutex, but I'm not sure that allowing a handle
1063 * reference to reach 0 isn't an application bug anyway.
1065 destroy = (InterlockedDecrement ((gint32 *)&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
1067 #ifdef DEBUG_REFS
1068 g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
1069 _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
1070 handle,
1071 _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
1072 #endif
1074 if(destroy==TRUE) {
1075 /* Need to copy the handle info, reset the slot in the
1076 * array, and _only then_ call the close function to
1077 * avoid race conditions (eg file descriptors being
1078 * closed, and another file being opened getting the
1079 * same fd racing the memset())
1081 struct _WapiHandleUnshared handle_data;
1082 struct _WapiHandleShared shared_handle_data;
1083 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
1084 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
1085 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
1087 if (is_shared) {
1088 /* If this is a shared handle we need to take
1089 * the shared lock outside of the scan_mutex
1090 * lock to avoid deadlocks
1092 thr_ret = _wapi_handle_lock_shared_handles ();
1093 g_assert (thr_ret == 0);
1096 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
1097 thr_ret = mono_mutex_lock (&scan_mutex);
1099 #ifdef DEBUG
1100 g_message ("%s: Destroying handle %p", __func__, handle);
1101 #endif
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 g_assert (thr_ret == 0);
1120 thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
1121 g_assert (thr_ret == 0);
1122 } else {
1123 struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
1125 memcpy (&shared_handle_data, shared,
1126 sizeof (struct _WapiHandleShared));
1128 /* It's possible that this handle is already
1129 * pointing at a deleted shared section
1131 #ifdef DEBUG_REFS
1132 g_message ("%s: %s handle %p shared refs before dec %d", __func__, _wapi_handle_typename[type], handle, shared->handle_refs);
1133 #endif
1135 if (shared->handle_refs > 0) {
1136 shared->handle_refs--;
1137 if (shared->handle_refs == 0) {
1138 memset (shared, '\0', sizeof (struct _WapiHandleShared));
1143 thr_ret = mono_mutex_unlock (&scan_mutex);
1144 g_assert (thr_ret == 0);
1145 pthread_cleanup_pop (0);
1147 if (is_shared) {
1148 _wapi_handle_unlock_shared_handles ();
1151 if (close_func != NULL) {
1152 if (is_shared) {
1153 close_func (handle, &shared_handle_data.u);
1154 } else {
1155 close_func (handle, &handle_data.u);
1161 void _wapi_handle_register_capabilities (WapiHandleType type,
1162 WapiHandleCapability caps)
1164 handle_caps[type] = caps;
1167 gboolean _wapi_handle_test_capabilities (gpointer handle,
1168 WapiHandleCapability caps)
1170 guint32 idx = GPOINTER_TO_UINT(handle);
1171 WapiHandleType type;
1173 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1174 return(FALSE);
1177 type = _WAPI_PRIVATE_HANDLES(idx).type;
1179 #ifdef DEBUG
1180 g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
1181 handle_caps[type], caps, handle_caps[type] & caps);
1182 #endif
1184 return((handle_caps[type] & caps) != 0);
1187 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
1189 if (handle_ops[type] != NULL &&
1190 handle_ops[type]->close != NULL) {
1191 return (handle_ops[type]->close);
1194 return (NULL);
1197 void _wapi_handle_ops_close (gpointer handle, gpointer data)
1199 guint32 idx = GPOINTER_TO_UINT(handle);
1200 WapiHandleType type;
1202 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1203 return;
1206 type = _WAPI_PRIVATE_HANDLES(idx).type;
1208 if (handle_ops[type] != NULL &&
1209 handle_ops[type]->close != NULL) {
1210 handle_ops[type]->close (handle, data);
1214 void _wapi_handle_ops_signal (gpointer handle)
1216 guint32 idx = GPOINTER_TO_UINT(handle);
1217 WapiHandleType type;
1219 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1220 return;
1223 type = _WAPI_PRIVATE_HANDLES(idx).type;
1225 if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1226 handle_ops[type]->signal (handle);
1230 gboolean _wapi_handle_ops_own (gpointer handle)
1232 guint32 idx = GPOINTER_TO_UINT(handle);
1233 WapiHandleType type;
1235 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1236 return(FALSE);
1239 type = _WAPI_PRIVATE_HANDLES(idx).type;
1241 if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1242 return(handle_ops[type]->own_handle (handle));
1243 } else {
1244 return(FALSE);
1248 gboolean _wapi_handle_ops_isowned (gpointer handle)
1250 guint32 idx = GPOINTER_TO_UINT(handle);
1251 WapiHandleType type;
1253 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1254 return(FALSE);
1257 type = _WAPI_PRIVATE_HANDLES(idx).type;
1259 if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1260 return(handle_ops[type]->is_owned (handle));
1261 } else {
1262 return(FALSE);
1266 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1268 guint32 idx = GPOINTER_TO_UINT(handle);
1269 WapiHandleType type;
1271 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1272 return(WAIT_FAILED);
1275 type = _WAPI_PRIVATE_HANDLES(idx).type;
1277 if (handle_ops[type] != NULL &&
1278 handle_ops[type]->special_wait != NULL) {
1279 return(handle_ops[type]->special_wait (handle, timeout));
1280 } else {
1281 return(WAIT_FAILED);
1285 void _wapi_handle_ops_prewait (gpointer handle)
1287 guint32 idx = GPOINTER_TO_UINT (handle);
1288 WapiHandleType type;
1290 if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1291 return;
1294 type = _WAPI_PRIVATE_HANDLES (idx).type;
1296 if (handle_ops[type] != NULL &&
1297 handle_ops[type]->prewait != NULL) {
1298 handle_ops[type]->prewait (handle);
1304 * CloseHandle:
1305 * @handle: The handle to release
1307 * Closes and invalidates @handle, releasing any resources it
1308 * consumes. When the last handle to a temporary or non-persistent
1309 * object is closed, that object can be deleted. Closing the same
1310 * handle twice is an error.
1312 * Return value: %TRUE on success, %FALSE otherwise.
1314 gboolean CloseHandle(gpointer handle)
1316 if (handle == NULL) {
1317 /* Problem: because we map file descriptors to the
1318 * same-numbered handle we can't tell the difference
1319 * between a bogus handle and the handle to stdin.
1320 * Assume that it's the console handle if that handle
1321 * exists...
1323 if (_WAPI_PRIVATE_HANDLES (0).type != WAPI_HANDLE_CONSOLE) {
1324 SetLastError (ERROR_INVALID_PARAMETER);
1325 return(FALSE);
1328 if (handle == _WAPI_HANDLE_INVALID){
1329 SetLastError (ERROR_INVALID_PARAMETER);
1330 return(FALSE);
1333 _wapi_handle_unref (handle);
1335 return(TRUE);
1338 /* Lots more to implement here, but this is all we need at the moment */
1339 gboolean DuplicateHandle (gpointer srcprocess, gpointer src,
1340 gpointer targetprocess, gpointer *target,
1341 guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 options G_GNUC_UNUSED)
1343 if (srcprocess != _WAPI_PROCESS_CURRENT ||
1344 targetprocess != _WAPI_PROCESS_CURRENT) {
1345 /* Duplicating other process's handles is not supported */
1346 SetLastError (ERROR_INVALID_HANDLE);
1347 return(FALSE);
1350 if (src == _WAPI_PROCESS_CURRENT) {
1351 *target = _wapi_process_duplicate ();
1352 } else if (src == _WAPI_THREAD_CURRENT) {
1353 *target = _wapi_thread_duplicate ();
1354 } else {
1355 _wapi_handle_ref (src);
1356 *target = src;
1359 return(TRUE);
1362 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1363 gpointer *handles,
1364 gboolean waitall,
1365 guint32 *retcount,
1366 guint32 *lowest)
1368 guint32 count, i, iter=0;
1369 gboolean ret;
1370 int thr_ret;
1371 WapiHandleType type;
1373 /* Lock all the handles, with backoff */
1374 again:
1375 thr_ret = _wapi_handle_lock_shared_handles ();
1376 g_assert (thr_ret == 0);
1378 for(i=0; i<numhandles; i++) {
1379 gpointer handle = handles[i];
1380 guint32 idx = GPOINTER_TO_UINT(handle);
1382 #ifdef DEBUG
1383 g_message ("%s: attempting to lock %p", __func__, handle);
1384 #endif
1386 type = _WAPI_PRIVATE_HANDLES(idx).type;
1388 thr_ret = _wapi_handle_trylock_handle (handle);
1390 if (thr_ret != 0) {
1391 /* Bummer */
1393 #ifdef DEBUG
1394 g_message ("%s: attempt failed for %p: %s", __func__,
1395 handle, strerror (thr_ret));
1396 #endif
1398 thr_ret = _wapi_handle_unlock_shared_handles ();
1399 g_assert (thr_ret == 0);
1401 while (i--) {
1402 handle = handles[i];
1403 idx = GPOINTER_TO_UINT(handle);
1405 thr_ret = _wapi_handle_unlock_handle (handle);
1406 g_assert (thr_ret == 0);
1409 /* If iter ever reaches 100 the nanosleep will
1410 * return EINVAL immediately, but we have a
1411 * design flaw if that happens.
1413 iter++;
1414 if(iter==100) {
1415 g_warning ("%s: iteration overflow!",
1416 __func__);
1417 iter=1;
1420 #ifdef DEBUG
1421 g_message ("%s: Backing off for %d ms", __func__,
1422 iter*10);
1423 #endif
1424 _wapi_handle_spin (10 * iter);
1426 goto again;
1430 #ifdef DEBUG
1431 g_message ("%s: Locked all handles", __func__);
1432 #endif
1434 count=0;
1435 *lowest=numhandles;
1437 for(i=0; i<numhandles; i++) {
1438 gpointer handle = handles[i];
1439 guint32 idx = GPOINTER_TO_UINT(handle);
1441 type = _WAPI_PRIVATE_HANDLES(idx).type;
1443 #ifdef DEBUG
1444 g_message ("%s: Checking handle %p", __func__, handle);
1445 #endif
1447 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1448 (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1449 (_WAPI_SHARED_HANDLE(type) &&
1450 WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1451 (!_WAPI_SHARED_HANDLE(type) &&
1452 _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1453 count++;
1455 #ifdef DEBUG
1456 g_message ("%s: Handle %p signalled", __func__,
1457 handle);
1458 #endif
1459 if(*lowest>i) {
1460 *lowest=i;
1465 #ifdef DEBUG
1466 g_message ("%s: %d event handles signalled", __func__, count);
1467 #endif
1469 if ((waitall == TRUE && count == numhandles) ||
1470 (waitall == FALSE && count > 0)) {
1471 ret=TRUE;
1472 } else {
1473 ret=FALSE;
1476 #ifdef DEBUG
1477 g_message ("%s: Returning %d", __func__, ret);
1478 #endif
1480 *retcount=count;
1482 return(ret);
1485 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1487 guint32 i;
1488 int thr_ret;
1490 thr_ret = _wapi_handle_unlock_shared_handles ();
1491 g_assert (thr_ret == 0);
1493 for(i=0; i<numhandles; i++) {
1494 gpointer handle = handles[i];
1496 #ifdef DEBUG
1497 g_message ("%s: unlocking handle %p", __func__, handle);
1498 #endif
1500 thr_ret = _wapi_handle_unlock_handle (handle);
1501 g_assert (thr_ret == 0);
1505 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout, gboolean alertable)
1507 struct timespec fake_timeout;
1508 int ret;
1510 if (!alertable) {
1511 if (timeout)
1512 ret=mono_cond_timedwait (cond, mutex, timeout);
1513 else
1514 ret=mono_cond_wait (cond, mutex);
1515 } else {
1516 _wapi_calc_timeout (&fake_timeout, 100);
1518 if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1519 (fake_timeout.tv_sec == timeout->tv_sec &&
1520 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1521 /* Real timeout is less than 100ms time */
1522 ret=mono_cond_timedwait (cond, mutex, timeout);
1523 } else {
1524 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1526 /* Mask the fake timeout, this will cause
1527 * another poll if the cond was not really signaled
1529 if (ret==ETIMEDOUT) {
1530 ret=0;
1535 return(ret);
1538 int _wapi_handle_wait_signal (gboolean poll)
1540 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
1543 int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
1545 return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
1548 int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
1550 #ifdef DEBUG
1551 g_message ("%s: waiting for %p", __func__, handle);
1552 #endif
1554 return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
1557 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1558 struct timespec *timeout, gboolean alertable, gboolean poll)
1560 #ifdef DEBUG
1561 g_message ("%s: waiting for %p (type %s)", __func__, handle,
1562 _wapi_handle_typename[_wapi_handle_type (handle)]);
1563 #endif
1565 if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1566 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1567 return (0);
1569 if (timeout != NULL) {
1570 struct timespec fake_timeout;
1571 _wapi_calc_timeout (&fake_timeout, 100);
1573 if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1574 (fake_timeout.tv_sec == timeout->tv_sec &&
1575 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1576 /* FIXME: Real timeout is less than
1577 * 100ms time, but is it really worth
1578 * calculating to the exact ms?
1580 _wapi_handle_spin (100);
1582 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1583 return (0);
1584 } else {
1585 return (ETIMEDOUT);
1589 _wapi_handle_spin (100);
1590 return (0);
1592 } else {
1593 guint32 idx = GPOINTER_TO_UINT(handle);
1594 int res;
1595 pthread_cond_t *cond;
1596 mono_mutex_t *mutex;
1598 if (alertable && !wapi_thread_set_wait_handle (handle))
1599 return 0;
1601 cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
1602 mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
1604 if (poll) {
1605 /* This is needed when waiting for process handles */
1606 res = timedwait_signal_poll_cond (cond, mutex, timeout, alertable);
1607 } else {
1608 if (timeout)
1609 res = mono_cond_timedwait (cond, mutex, timeout);
1610 else
1611 res = mono_cond_wait (cond, mutex);
1614 if (alertable)
1615 wapi_thread_clear_wait_handle (handle);
1617 return res;
1621 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1622 guint32 new_sharemode,
1623 guint32 new_access,
1624 guint32 *old_sharemode,
1625 guint32 *old_access,
1626 struct _WapiFileShare **share_info)
1628 struct _WapiFileShare *file_share;
1629 guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1630 int thr_ret, i, first_unused = -1;
1631 gboolean exists = FALSE;
1633 /* Prevents entries from expiring under us as we search
1635 thr_ret = _wapi_handle_lock_shared_handles ();
1636 g_assert (thr_ret == 0);
1638 /* Prevent new entries racing with us */
1639 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1640 g_assert (thr_ret == 0);
1642 /* If a linear scan gets too slow we'll have to fit a hash
1643 * table onto the shared mem backing store
1645 *share_info = NULL;
1646 for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1647 file_share = &_wapi_fileshare_layout->share_info[i];
1649 /* Make a note of an unused slot, in case we need to
1650 * store share info
1652 if (first_unused == -1 && file_share->handle_refs == 0) {
1653 first_unused = i;
1654 continue;
1657 if (file_share->handle_refs == 0) {
1658 continue;
1661 if (file_share->device == device &&
1662 file_share->inode == inode) {
1663 *old_sharemode = file_share->sharemode;
1664 *old_access = file_share->access;
1665 *share_info = file_share;
1667 /* Increment the reference count while we
1668 * still have sole access to the shared area.
1669 * This makes the increment atomic wrt
1670 * collections
1672 InterlockedIncrement ((gint32 *)&file_share->handle_refs);
1674 exists = TRUE;
1675 break;
1679 if (!exists) {
1680 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1681 /* No more space */
1682 } else {
1683 if (first_unused == -1) {
1684 file_share = &_wapi_fileshare_layout->share_info[++i];
1685 _wapi_fileshare_layout->hwm = i;
1686 } else {
1687 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1690 file_share->device = device;
1691 file_share->inode = inode;
1692 file_share->opened_by_pid = _wapi_getpid ();
1693 file_share->sharemode = new_sharemode;
1694 file_share->access = new_access;
1695 file_share->handle_refs = 1;
1696 *share_info = file_share;
1700 if (*share_info != NULL) {
1701 InterlockedExchange ((gint32 *)&(*share_info)->timestamp, now);
1704 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1706 _wapi_handle_unlock_shared_handles ();
1708 return(exists);
1711 /* If we don't have the info in /proc, check if the process that
1712 * opened this share info is still there (it's not a perfect method,
1713 * due to pid reuse)
1715 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1717 if (kill (share_info->opened_by_pid, 0) == -1 &&
1718 (errno == ESRCH ||
1719 errno == EPERM)) {
1720 /* It's gone completely (or there's a new process
1721 * owned by someone else) so mark this share info as
1722 * dead
1724 #ifdef DEBUG
1725 g_message ("%s: Didn't find it, destroying entry", __func__);
1726 #endif
1728 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1732 #ifdef __linux__
1733 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1734 * question. If there are none, reset the share info.
1736 * This implementation is Linux-specific; legacy systems will have to
1737 * implement their own ways of finding out if a particular file is
1738 * open by a process.
1740 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1742 gboolean found = FALSE, proc_fds = FALSE;
1743 pid_t self = _wapi_getpid ();
1744 int pid;
1745 int thr_ret, i;
1747 /* Prevents entries from expiring under us if we remove this
1748 * one
1750 thr_ret = _wapi_handle_lock_shared_handles ();
1751 g_assert (thr_ret == 0);
1753 /* Prevent new entries racing with us */
1754 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1755 g_assert (thr_ret == 0);
1757 /* If there is no /proc, there's nothing more we can do here */
1758 if (access ("/proc", F_OK) == -1) {
1759 _wapi_handle_check_share_by_pid (share_info);
1760 goto done;
1763 /* If there's another handle that thinks it owns this fd, then even
1764 * if the fd has been closed behind our back consider it still owned.
1765 * See bugs 75764 and 75891
1767 for (i = 0; i < _wapi_fd_reserve; i++) {
1768 if (_wapi_private_handles [SLOT_INDEX (i)]) {
1769 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1771 if (i != fd &&
1772 handle->type == WAPI_HANDLE_FILE) {
1773 struct _WapiHandle_file *file_handle = &handle->u.file;
1775 if (file_handle->share_info == share_info) {
1776 #ifdef DEBUG
1777 g_message ("%s: handle 0x%x has this file open!",
1778 __func__, i);
1779 #endif
1781 goto done;
1787 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1788 struct _WapiHandleShared *shared;
1789 struct _WapiHandle_process *process_handle;
1791 shared = &_wapi_shared_layout->handles[i];
1793 if (shared->type == WAPI_HANDLE_PROCESS) {
1794 DIR *fd_dir;
1795 struct dirent *fd_entry;
1796 char subdir[_POSIX_PATH_MAX];
1798 process_handle = &shared->u.process;
1799 pid = process_handle->id;
1801 /* Look in /proc/<pid>/fd/ but ignore
1802 * /proc/<our pid>/fd/<fd>, as we have the
1803 * file open too
1805 g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1806 pid);
1808 fd_dir = opendir (subdir);
1809 if (fd_dir == NULL) {
1810 continue;
1813 #ifdef DEBUG
1814 g_message ("%s: Looking in %s", __func__, subdir);
1815 #endif
1817 proc_fds = TRUE;
1819 while ((fd_entry = readdir (fd_dir)) != NULL) {
1820 char path[_POSIX_PATH_MAX];
1821 struct stat link_stat;
1823 if (!strcmp (fd_entry->d_name, ".") ||
1824 !strcmp (fd_entry->d_name, "..") ||
1825 (pid == self &&
1826 fd == atoi (fd_entry->d_name))) {
1827 continue;
1830 g_snprintf (path, _POSIX_PATH_MAX,
1831 "/proc/%d/fd/%s", pid,
1832 fd_entry->d_name);
1834 stat (path, &link_stat);
1835 if (link_stat.st_dev == share_info->device &&
1836 link_stat.st_ino == share_info->inode) {
1837 #ifdef DEBUG
1838 g_message ("%s: Found it at %s",
1839 __func__, path);
1840 #endif
1842 found = TRUE;
1846 closedir (fd_dir);
1850 if (proc_fds == FALSE) {
1851 _wapi_handle_check_share_by_pid (share_info);
1852 } else if (found == FALSE) {
1853 /* Blank out this entry, as it is stale */
1854 #ifdef DEBUG
1855 g_message ("%s: Didn't find it, destroying entry", __func__);
1856 #endif
1858 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1861 done:
1862 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1864 _wapi_handle_unlock_shared_handles ();
1866 #else
1868 // Other implementations (non-Linux)
1870 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1872 int thr_ret;
1874 /* Prevents entries from expiring under us if we remove this
1875 * one */
1876 thr_ret = _wapi_handle_lock_shared_handles ();
1877 g_assert (thr_ret == 0);
1879 /* Prevent new entries racing with us */
1880 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1881 g_assert (thr_ret == 0);
1883 _wapi_handle_check_share_by_pid (share_info);
1885 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1886 _wapi_handle_unlock_shared_handles ();
1888 #endif
1890 void _wapi_handle_dump (void)
1892 struct _WapiHandleUnshared *handle_data;
1893 guint32 i, k;
1894 int thr_ret;
1896 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1897 (void *)&scan_mutex);
1898 thr_ret = mono_mutex_lock (&scan_mutex);
1899 g_assert (thr_ret == 0);
1901 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1902 if (_wapi_private_handles [i]) {
1903 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1904 handle_data = &_wapi_private_handles [i][k];
1906 if (handle_data->type == WAPI_HANDLE_UNUSED) {
1907 continue;
1910 g_print ("%3x [%7s] %s %d ",
1911 i * _WAPI_HANDLE_INITIAL_COUNT + k,
1912 _wapi_handle_typename[handle_data->type],
1913 handle_data->signalled?"Sg":"Un",
1914 handle_data->ref);
1915 handle_details[handle_data->type](&handle_data->u);
1916 g_print ("\n");
1921 thr_ret = mono_mutex_unlock (&scan_mutex);
1922 g_assert (thr_ret == 0);
1923 pthread_cleanup_pop (0);
1926 static void _wapi_shared_details (gpointer handle_info)
1928 struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1930 g_print ("offset: 0x%x", shared->offset);
1933 void _wapi_handle_update_refs (void)
1935 guint32 i, k;
1936 int thr_ret;
1937 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1939 thr_ret = _wapi_handle_lock_shared_handles ();
1940 g_assert (thr_ret == 0);
1942 /* Prevent file share entries racing with us */
1943 thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1944 g_assert(thr_ret == 0);
1946 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1947 (void *)&scan_mutex);
1948 thr_ret = mono_mutex_lock (&scan_mutex);
1950 for(i = SLOT_INDEX (0); i < _wapi_private_handle_slot_count; i++) {
1951 if (_wapi_private_handles [i]) {
1952 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1953 struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1955 if (_WAPI_SHARED_HANDLE(handle->type)) {
1956 struct _WapiHandleShared *shared_data;
1958 #ifdef DEBUG
1959 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1960 #endif
1962 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
1964 #ifdef DEBUG
1965 g_message ("%s: (%d) Updating timestamp of handle 0x%x", __func__, _wapi_getpid (), handle->u.shared.offset);
1966 #endif
1968 InterlockedExchange ((gint32 *)&shared_data->timestamp, now);
1969 } else if (handle->type == WAPI_HANDLE_FILE) {
1970 struct _WapiHandle_file *file_handle = &handle->u.file;
1972 #ifdef DEBUG
1973 g_message ("%s: (%d) handle 0x%x is FILE", __func__, _wapi_getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1974 #endif
1976 g_assert (file_handle->share_info != NULL);
1978 #ifdef DEBUG
1979 g_message ("%s: (%d) Inc refs on fileshare 0x%x", __func__, _wapi_getpid (), (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1980 #endif
1982 InterlockedExchange ((gint32 *)&file_handle->share_info->timestamp, now);
1988 thr_ret = mono_mutex_unlock (&scan_mutex);
1989 g_assert (thr_ret == 0);
1990 pthread_cleanup_pop (0);
1992 thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1994 _wapi_handle_unlock_shared_handles ();