2006-02-17 Dick Porter <dick@ximian.com>
[mono.git] / mono / io-layer / handles.c
1 /*
2  * handles.c:  Generic and internal operations on handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
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 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <sys/mman.h>
20 #include <dirent.h>
21 #include <sys/stat.h>
22
23 #include <mono/os/gc_wrapper.h>
24
25 #include <mono/io-layer/wapi.h>
26 #include <mono/io-layer/wapi-private.h>
27 #include <mono/io-layer/handles-private.h>
28 #include <mono/io-layer/mono-mutex.h>
29 #include <mono/io-layer/misc-private.h>
30 #include <mono/io-layer/shared.h>
31 #include <mono/io-layer/collection.h>
32
33 #undef DEBUG
34 #undef DEBUG_REFS
35
36 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer);
37
38 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
39 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
40         NULL,
41         &_wapi_file_ops,
42         &_wapi_console_ops,
43         &_wapi_thread_ops,
44         &_wapi_sem_ops,
45         &_wapi_mutex_ops,
46         &_wapi_event_ops,
47         &_wapi_socket_ops,
48         &_wapi_find_ops,
49         &_wapi_process_ops,
50         &_wapi_pipe_ops,
51         &_wapi_namedmutex_ops,
52         &_wapi_namedsem_ops,
53         &_wapi_namedevent_ops,
54 };
55
56 static void _wapi_shared_details (gpointer handle_info);
57
58 static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = {
59         NULL,
60         _wapi_file_details,
61         _wapi_console_details,
62         _wapi_shared_details,   /* thread */
63         _wapi_sem_details,
64         _wapi_mutex_details,
65         _wapi_event_details,
66         NULL,                   /* Nothing useful to see in a socket handle */
67         NULL,                   /* Nothing useful to see in a find handle */
68         _wapi_shared_details,   /* process */
69         _wapi_pipe_details,
70         _wapi_shared_details,   /* namedmutex */
71         _wapi_shared_details,   /* namedsem */
72         _wapi_shared_details,   /* namedevent */
73 };
74
75 const char *_wapi_handle_typename[] = {
76         "Unused",
77         "File",
78         "Console",
79         "Thread",
80         "Sem",
81         "Mutex",
82         "Event",
83         "Socket",
84         "Find",
85         "Process",
86         "Pipe",
87         "N.Mutex",
88         "N.Sem",
89         "N.Event",
90         "Error!!"
91 };
92
93 /*
94  * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles.
95  * If 4M handles are not enough... Oh, well... we will crash.
96  */
97 #define SLOT_INDEX(x)   (x / _WAPI_HANDLE_INITIAL_COUNT)
98 #define SLOT_OFFSET(x)  (x % _WAPI_HANDLE_INITIAL_COUNT)
99
100 struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS];
101 static guint32 _wapi_private_handle_count = 0;
102
103 struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL;
104 struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL;
105
106 guint32 _wapi_fd_reserve;
107
108 mono_mutex_t _wapi_global_signal_mutex;
109 pthread_cond_t _wapi_global_signal_cond;
110
111 int _wapi_sem_id;
112
113 static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER;
114
115 static mono_once_t shared_init_once = MONO_ONCE_INIT;
116 static void shared_init (void)
117 {
118         int thr_ret;
119         int idx = 0;
120         
121         g_assert ((sizeof (handle_ops) / sizeof (handle_ops[0]))
122                   == WAPI_HANDLE_COUNT);
123         
124         _wapi_fd_reserve = getdtablesize();
125
126         do {
127                 _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared,
128                                                         _WAPI_HANDLE_INITIAL_COUNT);
129
130                 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
131         } while(_wapi_fd_reserve > _wapi_private_handle_count);
132         
133         _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA);
134         g_assert (_wapi_shared_layout != NULL);
135
136         _wapi_shm_semaphores_init ();
137         
138         _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE);
139         g_assert (_wapi_fileshare_layout != NULL);
140         
141         _wapi_collection_init ();
142         
143         thr_ret = pthread_cond_init(&_wapi_global_signal_cond, NULL);
144         g_assert (thr_ret == 0);
145         
146         thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL);
147         g_assert (thr_ret == 0);
148 }
149
150 static void _wapi_handle_init_shared (struct _WapiHandleShared *handle,
151                                       WapiHandleType type,
152                                       gpointer handle_specific)
153 {
154         handle->type = type;
155         handle->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF);
156         handle->signalled = FALSE;
157         handle->handle_refs = 1;
158         
159         if (handle_specific != NULL) {
160                 memcpy (&handle->u, handle_specific, sizeof (handle->u));
161         }
162 }
163
164 static void _wapi_handle_init (struct _WapiHandleUnshared *handle,
165                                WapiHandleType type, gpointer handle_specific)
166 {
167         int thr_ret;
168         
169         handle->type = type;
170         handle->signalled = FALSE;
171         handle->ref = 1;
172         
173         if (!_WAPI_SHARED_HANDLE(type)) {
174                 thr_ret = pthread_cond_init (&handle->signal_cond, NULL);
175                 g_assert (thr_ret == 0);
176                                 
177                 thr_ret = mono_mutex_init (&handle->signal_mutex, NULL);
178                 g_assert (thr_ret == 0);
179
180                 if (handle_specific != NULL) {
181                         memcpy (&handle->u, handle_specific,
182                                 sizeof (handle->u));
183                 }
184         }
185 }
186
187 static guint32 _wapi_handle_new_shared (WapiHandleType type,
188                                         gpointer handle_specific)
189 {
190         guint32 offset;
191         static guint32 last = 1;
192         int thr_ret;
193         
194         /* Leave the first slot empty as a guard */
195 again:
196         /* FIXME: expandable array */
197         for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT; offset++) {
198                 struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset];
199                 
200                 if(handle->type == WAPI_HANDLE_UNUSED) {
201                         thr_ret = _wapi_handle_lock_shared_handles ();
202                         g_assert (thr_ret == 0);
203                         
204                         if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) {
205                                 last = offset + 1;
206                         
207                                 _wapi_handle_init_shared (handle, type,
208                                                           handle_specific);
209
210                                 _wapi_handle_unlock_shared_handles ();
211                                 
212                                 return(offset);
213                         } else {
214                                 /* Someone else beat us to it, just
215                                  * continue looking
216                                  */
217                         }
218
219                         _wapi_handle_unlock_shared_handles ();
220                 }
221         }
222
223         if(last > 1) {
224                 /* Try again from the beginning */
225                 last = 1;
226                 goto again;
227         }
228
229         /* Will need to expand the array.  The caller will sort it out */
230
231         return(0);
232 }
233
234 /*
235  * _wapi_handle_new_internal:
236  * @type: Init handle to this type
237  *
238  * Search for a free handle and initialize it. Return the handle on
239  * success and 0 on failure.  This is only called from
240  * _wapi_handle_new, and scan_mutex must be held.
241  */
242 static guint32 _wapi_handle_new_internal (WapiHandleType type,
243                                           gpointer handle_specific)
244 {
245         guint32 i, k, count;
246         static guint32 last = 0;
247         gboolean retry = FALSE;
248         
249         /* A linear scan should be fast enough.  Start from the last
250          * allocation, assuming that handles are allocated more often
251          * than they're freed. Leave the space reserved for file
252          * descriptors
253          */
254         
255         if (last < _wapi_fd_reserve) {
256                 last = _wapi_fd_reserve;
257         } else {
258                 retry = TRUE;
259         }
260
261 again:
262         count = last;
263         for(i = SLOT_INDEX (count); _wapi_private_handles [i] != NULL; i++) {
264                 for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
265                         struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
266
267                         if(handle->type == WAPI_HANDLE_UNUSED) {
268                                 last = count + 1;
269                         
270                                 _wapi_handle_init (handle, type, handle_specific);
271                                 return (count);
272                         }
273                         count++;
274                 }
275         }
276
277         if(retry && last > _wapi_fd_reserve) {
278                 /* Try again from the beginning */
279                 last = _wapi_fd_reserve;
280                 goto again;
281         }
282
283         /* Will need to expand the array.  The caller will sort it out */
284
285         return(0);
286 }
287
288 gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific)
289 {
290         guint32 handle_idx = 0;
291         gpointer handle;
292         int thr_ret;
293         
294         mono_once (&shared_init_once, shared_init);
295         
296 #ifdef DEBUG
297         g_message ("%s: Creating new handle of type %s", __func__,
298                    _wapi_handle_typename[type]);
299 #endif
300
301         g_assert(!_WAPI_FD_HANDLE(type));
302         
303         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
304                               (void *)&scan_mutex);
305         thr_ret = mono_mutex_lock (&scan_mutex);
306         g_assert (thr_ret == 0);
307                 
308         while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) {
309                 /* Try and expand the array, and have another go */
310                 int idx = SLOT_INDEX (_wapi_private_handle_count);
311                 if (idx >= _WAPI_PRIVATE_MAX_SLOTS) {
312                         break;
313                 }
314                 
315                 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
316                                                 _WAPI_HANDLE_INITIAL_COUNT);
317
318                 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
319         }
320         
321         thr_ret = mono_mutex_unlock (&scan_mutex);
322         g_assert (thr_ret == 0);
323         pthread_cleanup_pop (0);
324
325         if (handle_idx == 0) {
326                 /* We ran out of slots */
327                 handle = _WAPI_HANDLE_INVALID;
328                 goto done;
329         }
330                 
331         /* Make sure we left the space for fd mappings */
332         g_assert (handle_idx >= _wapi_fd_reserve);
333         
334         handle = GUINT_TO_POINTER (handle_idx);
335
336 #ifdef DEBUG
337         g_message ("%s: Allocated new handle %p", __func__, handle);
338 #endif
339         
340         if (_WAPI_SHARED_HANDLE(type)) {
341                 /* Add the shared section too */
342                 guint32 ref;
343                 
344                 ref = _wapi_handle_new_shared (type, handle_specific);
345                 if (ref == 0) {
346                         _wapi_handle_collect ();
347                         ref = _wapi_handle_new_shared (type, handle_specific);
348                         if (ref == 0) {
349                                 /* FIXME: grow the arrays */
350                                 handle = _WAPI_HANDLE_INVALID;
351                                 goto done;
352                         }
353                 }
354                 
355                 _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref;
356 #ifdef DEBUG
357                 g_message ("%s: New shared handle at offset 0x%x", __func__,
358                            ref);
359 #endif
360         }
361                 
362 done:
363         return(handle);
364 }
365
366 gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset,
367                                        gboolean timestamp)
368 {
369         guint32 handle_idx = 0;
370         gpointer handle = INVALID_HANDLE_VALUE;
371         int thr_ret, i, k;
372         struct _WapiHandleShared *shared;
373         guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
374         
375         mono_once (&shared_init_once, shared_init);
376
377 #ifdef DEBUG
378         g_message ("%s: Creating new handle of type %s to offset %d", __func__,
379                    _wapi_handle_typename[type], offset);
380 #endif
381
382         g_assert(!_WAPI_FD_HANDLE(type));
383         g_assert(_WAPI_SHARED_HANDLE(type));
384         g_assert(offset != 0);
385
386         shared = &_wapi_shared_layout->handles[offset];
387         if (timestamp) {
388                 /* Bump up the timestamp for this offset */
389                 InterlockedExchange (&shared->timestamp, now);
390         }
391                 
392         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
393                               (void *)&scan_mutex);
394         thr_ret = mono_mutex_lock (&scan_mutex);
395         g_assert (thr_ret == 0);
396
397         for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
398                 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
399                         struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k];
400                 
401                         if (handle_data->type == type &&
402                             handle_data->u.shared.offset == offset) {
403                                 handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
404                                 goto first_pass_done;
405                         }
406                 }
407         }
408
409 first_pass_done:
410         thr_ret = mono_mutex_unlock (&scan_mutex);
411         g_assert (thr_ret == 0);
412         pthread_cleanup_pop (0);
413
414         if (handle != INVALID_HANDLE_VALUE) {
415                 _wapi_handle_ref (handle);
416
417 #ifdef DEBUG
418                 g_message ("%s: Returning old handle %p referencing 0x%x",
419                            __func__, handle, offset);
420 #endif
421                 return (handle);
422         }
423
424         /* Prevent entries expiring under us as we search */
425         thr_ret = _wapi_handle_lock_shared_handles ();
426         g_assert (thr_ret == 0);
427         
428         if (shared->type == WAPI_HANDLE_UNUSED) {
429                 /* Someone deleted this handle while we were working */
430                 goto done;
431         }
432         
433         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
434                               (void *)&scan_mutex);
435         thr_ret = mono_mutex_lock (&scan_mutex);
436         g_assert (thr_ret == 0);
437         
438         while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) {
439                 /* Try and expand the array, and have another go */
440                 int idx = SLOT_INDEX (_wapi_private_handle_count);
441                 _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared,
442                                                 _WAPI_HANDLE_INITIAL_COUNT);
443
444                 _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT;
445         }
446                 
447         thr_ret = mono_mutex_unlock (&scan_mutex);
448         g_assert (thr_ret == 0);
449         pthread_cleanup_pop (0);
450                 
451         /* Make sure we left the space for fd mappings */
452         g_assert (handle_idx >= _wapi_fd_reserve);
453         
454         handle = GUINT_TO_POINTER (handle_idx);
455                 
456         _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset;
457         InterlockedIncrement (&shared->handle_refs);
458         
459 #ifdef DEBUG
460         g_message ("%s: Allocated new handle %p referencing 0x%x", __func__,
461                    handle, offset);
462 #endif
463         
464 done:
465         _wapi_handle_unlock_shared_handles ();
466
467         return(handle);
468 }
469
470 gpointer _wapi_handle_new_fd (WapiHandleType type, int fd,
471                               gpointer handle_specific)
472 {
473         struct _WapiHandleUnshared *handle;
474         int thr_ret;
475         
476         mono_once (&shared_init_once, shared_init);
477         
478 #ifdef DEBUG
479         g_message ("%s: Creating new handle of type %s", __func__,
480                    _wapi_handle_typename[type]);
481 #endif
482         
483         g_assert(_WAPI_FD_HANDLE(type));
484         g_assert(!_WAPI_SHARED_HANDLE(type));
485         
486         if (fd >= _wapi_fd_reserve) {
487 #ifdef DEBUG
488                 g_message ("%s: fd %d is too big", __func__, fd);
489 #endif
490
491                 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
492         }
493
494         handle = &_WAPI_PRIVATE_HANDLES(fd);
495         
496         if (handle->type != WAPI_HANDLE_UNUSED) {
497 #ifdef DEBUG
498                 g_message ("%s: fd %d is already in use!", __func__, fd);
499 #endif
500                 /* FIXME: clean up this handle?  We can't do anything
501                  * with the fd, cos thats the new one
502                  */
503         }
504
505 #ifdef DEBUG
506         g_message ("%s: Assigning new fd handle %d", __func__, fd);
507 #endif
508
509         /* Prevent file share entries racing with us, when the file
510          * handle is only half initialised
511          */
512         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
513         g_assert(thr_ret == 0);
514
515         _wapi_handle_init (handle, type, handle_specific);
516
517         thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
518
519         return(GUINT_TO_POINTER(fd));
520 }
521
522 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
523                               gpointer *handle_specific)
524 {
525         struct _WapiHandleUnshared *handle_data;
526         guint32 handle_idx = GPOINTER_TO_UINT(handle);
527
528         if (!_WAPI_PRIVATE_VALID_SLOT (handle_idx)) {
529                 return(FALSE);
530         }
531         
532         handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx);
533         
534         if (handle_data->type != type) {
535                 return(FALSE);
536         }
537
538         if (handle_specific == NULL) {
539                 return(FALSE);
540         }
541         
542         if (_WAPI_SHARED_HANDLE(type)) {
543                 struct _WapiHandle_shared_ref *ref;
544                 struct _WapiHandleShared *shared_handle_data;
545                         
546                 ref = &handle_data->u.shared;
547                 shared_handle_data = &_wapi_shared_layout->handles[ref->offset];
548                 
549                 if (shared_handle_data->type != type) {
550                         /* The handle must have been deleted on us
551                          */
552                         return (FALSE);
553                 }
554                 
555                 *handle_specific = &shared_handle_data->u;
556         } else {
557                 *handle_specific = &handle_data->u;
558         }
559         
560         return(TRUE);
561 }
562
563 void
564 _wapi_handle_foreach (WapiHandleType type,
565                         gboolean (*on_each)(gpointer test, gpointer user),
566                         gpointer user_data)
567 {
568         struct _WapiHandleUnshared *handle_data = NULL;
569         gpointer ret = NULL;
570         guint32 i, k;
571         int thr_ret;
572
573         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
574                               (void *)&scan_mutex);
575         thr_ret = mono_mutex_lock (&scan_mutex);
576         g_assert (thr_ret == 0);
577
578         for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
579                 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
580                         handle_data = &_wapi_private_handles [i][k];
581                 
582                         if (handle_data->type == type) {
583                                 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
584                                 if (on_each (ret, user_data) == TRUE)
585                                         break;
586                         }
587                 }
588         }
589
590         thr_ret = mono_mutex_unlock (&scan_mutex);
591         g_assert (thr_ret == 0);
592         pthread_cleanup_pop (0);
593 }
594
595 /* This might list some shared handles twice if they are already
596  * opened by this process, and the check function returns FALSE the
597  * first time.  Shared handles that are created during the search are
598  * unreffed if the check function returns FALSE, so callers must not
599  * rely on the handle persisting (unless the check function returns
600  * TRUE)
601  */
602 gpointer _wapi_search_handle (WapiHandleType type,
603                               gboolean (*check)(gpointer test, gpointer user),
604                               gpointer user_data,
605                               gpointer *handle_specific)
606 {
607         struct _WapiHandleUnshared *handle_data = NULL;
608         struct _WapiHandleShared *shared;
609         gpointer ret = NULL;
610         guint32 i, k;
611         gboolean found = FALSE;
612         int thr_ret;
613
614         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
615                               (void *)&scan_mutex);
616         thr_ret = mono_mutex_lock (&scan_mutex);
617         g_assert (thr_ret == 0);
618         
619         for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) {
620                 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
621                         handle_data = &_wapi_private_handles [i][k];
622                 
623                         if (handle_data->type == type) {
624                                 ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k);
625                                 if (check (ret, user_data) == TRUE) {
626                                         found = TRUE;
627                                         break;
628                                 }
629                         }
630                 }
631         }
632
633         thr_ret = mono_mutex_unlock (&scan_mutex);
634         g_assert (thr_ret == 0);
635         pthread_cleanup_pop (0);
636
637         if (!found && _WAPI_SHARED_HANDLE (type)) {
638                 /* Not found yet, so search the shared memory too */
639 #ifdef DEBUG
640                 g_message ("%s: Looking at other shared handles...", __func__);
641 #endif
642
643                 for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
644                         shared = &_wapi_shared_layout->handles[i];
645                         
646                         if (shared->type == type) {
647                                 /* Tell new_from_offset to not
648                                  * timestamp this handle, because
649                                  * otherwise it will ping every handle
650                                  * in the list and they will never
651                                  * expire
652                                  */
653                                 ret = _wapi_handle_new_from_offset (type, i,
654                                                                     FALSE);
655                                 if (ret == INVALID_HANDLE_VALUE) {
656                                         /* This handle was deleted
657                                          * while we were looking at it
658                                          */
659                                         continue;
660                                 }
661                                 
662 #ifdef DEBUG
663                                 g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], i);
664 #endif
665
666                                 /* It's possible that the shared part
667                                  * of this handle has now been blown
668                                  * away (after new_from_offset
669                                  * successfully opened it,) if its
670                                  * timestamp is too old.  The check
671                                  * function needs to be aware of this,
672                                  * and cope if the handle has
673                                  * vanished.
674                                  */
675                                 if (check (ret, user_data) == TRUE) {
676                                         /* Timestamp this handle, but make
677                                          * sure it still exists first
678                                          */
679                                         thr_ret = _wapi_handle_lock_shared_handles ();
680                                         g_assert (thr_ret == 0);
681                                         
682                                         if (shared->type == type) {
683                                                 guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
684                                                 InterlockedExchange (&shared->timestamp, now);
685
686                                                 found = TRUE;
687                                                 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret));
688                                         
689                                                 _wapi_handle_unlock_shared_handles ();
690                                                 break;
691                                         } else {
692                                                 /* It's been deleted,
693                                                  * so just keep
694                                                  * looking
695                                                  */
696                                                 _wapi_handle_unlock_shared_handles ();
697                                         }
698                                 }
699                                 
700                                 /* This isn't the handle we're looking
701                                  * for, so drop the reference we took
702                                  * in _wapi_handle_new_from_offset ()
703                                  */
704                                 _wapi_handle_unref (ret);
705                         }
706                 }
707         }
708         
709         if (!found) {
710                 ret = NULL;
711                 goto done;
712         }
713         
714         if(handle_specific != NULL) {
715                 if (_WAPI_SHARED_HANDLE(type)) {
716                         g_assert(shared->type == type);
717                         
718                         *handle_specific = &shared->u;
719                 } else {
720                         *handle_specific = &handle_data->u;
721                 }
722         }
723
724 done:
725         return(ret);
726 }
727
728 /* Returns the offset of the metadata array, or -1 on error, or 0 for
729  * not found (0 is not a valid offset)
730  */
731 gint32 _wapi_search_handle_namespace (WapiHandleType type,
732                                       gchar *utf8_name)
733 {
734         struct _WapiHandleShared *shared_handle_data;
735         guint32 i;
736         gint32 ret = 0;
737         int thr_ret;
738         
739         g_assert(_WAPI_SHARED_HANDLE(type));
740         
741 #ifdef DEBUG
742         g_message ("%s: Lookup for handle named [%s] type %s", __func__,
743                    utf8_name, _wapi_handle_typename[type]);
744 #endif
745
746         /* Do a handle collection before starting to look, so that any
747          * stale cruft gets removed
748          */
749         _wapi_handle_collect ();
750         
751         thr_ret = _wapi_handle_lock_shared_handles ();
752         g_assert (thr_ret == 0);
753         
754         for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
755                 WapiSharedNamespace *sharedns;
756                 
757                 shared_handle_data = &_wapi_shared_layout->handles[i];
758
759                 /* Check mutex, event, semaphore, timer, job and
760                  * file-mapping object names.  So far only mutex,
761                  * semaphore and event are implemented.
762                  */
763                 if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) {
764                         continue;
765                 }
766
767 #ifdef DEBUG
768                 g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
769 #endif
770
771                 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
772                         
773 #ifdef DEBUG
774                 g_message ("%s: name is [%s]", __func__, sharedns->name);
775 #endif
776
777                 if (strcmp (sharedns->name, utf8_name) == 0) {
778                         if (shared_handle_data->type != type) {
779                                 /* Its the wrong type, so fail now */
780 #ifdef DEBUG
781                                 g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]);
782 #endif
783                                 ret = -1;
784                                 goto done;
785                         } else {
786 #ifdef DEBUG
787                                 g_message ("%s: handle 0x%x matches name and type", __func__, i);
788 #endif
789                                 ret = i;
790                                 goto done;
791                         }
792                 }
793         }
794
795 done:
796         _wapi_handle_unlock_shared_handles ();
797         
798         return(ret);
799 }
800
801 void _wapi_handle_ref (gpointer handle)
802 {
803         guint32 idx = GPOINTER_TO_UINT(handle);
804         guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
805         struct _WapiHandleUnshared *handle_data;
806
807         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
808                 return;
809         }
810         
811         if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
812                 g_warning ("%s: Attempting to ref unused handle %p", __func__,
813                            handle);
814                 return;
815         }
816
817         handle_data = &_WAPI_PRIVATE_HANDLES(idx);
818         
819         InterlockedIncrement (&handle_data->ref);
820
821         /* It's possible for processes to exit before getting around
822          * to updating timestamps in the collection thread, so if a
823          * shared handle is reffed do the timestamp here as well just
824          * to make sure.
825          */
826         if (_WAPI_SHARED_HANDLE(handle_data->type)) {
827                 struct _WapiHandleShared *shared_data = &_wapi_shared_layout->handles[handle_data->u.shared.offset];
828                 
829                 InterlockedExchange (&shared_data->timestamp, now);
830         }
831         
832 #ifdef DEBUG_REFS
833         g_message ("%s: %s handle %p ref now %d", __func__, 
834                    _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
835                    handle,
836                    _WAPI_PRIVATE_HANDLES(idx).ref);
837 #endif
838 }
839
840 /* The handle must not be locked on entry to this function */
841 void _wapi_handle_unref (gpointer handle)
842 {
843         guint32 idx = GPOINTER_TO_UINT(handle);
844         gboolean destroy = FALSE;
845         int thr_ret;
846
847         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
848                 return;
849         }
850         
851         if (_wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
852                 g_warning ("%s: Attempting to unref unused handle %p",
853                            __func__, handle);
854                 return;
855         }
856
857         /* Possible race condition here if another thread refs the
858          * handle between here and setting the type to UNUSED.  I
859          * could lock a mutex, but I'm not sure that allowing a handle
860          * reference to reach 0 isn't an application bug anyway.
861          */
862         destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0);
863         
864 #ifdef DEBUG_REFS
865         g_message ("%s: %s handle %p ref now %d (destroy %s)", __func__,
866                    _wapi_handle_typename[_WAPI_PRIVATE_HANDLES (idx).type],
867                    handle,
868                    _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE");
869 #endif
870         
871         if(destroy==TRUE) {
872                 /* Need to copy the handle info, reset the slot in the
873                  * array, and _only then_ call the close function to
874                  * avoid race conditions (eg file descriptors being
875                  * closed, and another file being opened getting the
876                  * same fd racing the memset())
877                  */
878                 struct _WapiHandleUnshared handle_data;
879                 struct _WapiHandleShared shared_handle_data;
880                 WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type;
881                 void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type);
882                 gboolean is_shared = _WAPI_SHARED_HANDLE(type);
883
884                 if (is_shared) {
885                         /* If this is a shared handle we need to take
886                          * the shared lock outside of the scan_mutex
887                          * lock to avoid deadlocks
888                          */
889                         thr_ret = _wapi_handle_lock_shared_handles ();
890                         g_assert (thr_ret == 0);
891                 }
892                 
893                 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, (void *)&scan_mutex);
894                 thr_ret = mono_mutex_lock (&scan_mutex);
895
896 #ifdef DEBUG
897                 g_message ("%s: Destroying handle %p", __func__, handle);
898 #endif
899                 
900                 memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx),
901                         sizeof (struct _WapiHandleUnshared));
902
903                 memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0',
904                         sizeof(_WAPI_PRIVATE_HANDLES(idx).u));
905
906                 _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED;
907                 
908                 if (!is_shared) {
909                         /* Destroy the mutex and cond var.  We hope nobody
910                          * tried to grab them between the handle unlock and
911                          * now, but pthreads doesn't have a
912                          * "unlock_and_destroy" atomic function.
913                          */
914                         thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex);
915                         g_assert (thr_ret == 0);
916                                 
917                         thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond);
918                         g_assert (thr_ret == 0);
919                 } else {
920                         struct _WapiHandleShared *shared = &_wapi_shared_layout->handles[handle_data.u.shared.offset];
921
922                         memcpy (&shared_handle_data, shared,
923                                 sizeof (struct _WapiHandleShared));
924                         
925                         /* It's possible that this handle is already
926                          * pointing at a deleted shared section
927                          */
928                         if (shared->handle_refs > 0) {
929                                 shared->handle_refs--;
930                                 if (shared->handle_refs == 0) {
931                                         memset (shared, '\0', sizeof (struct _WapiHandleShared));
932                                 }
933                         }
934                 }
935
936                 thr_ret = mono_mutex_unlock (&scan_mutex);
937                 g_assert (thr_ret == 0);
938                 pthread_cleanup_pop (0);
939
940                 if (is_shared) {
941                         _wapi_handle_unlock_shared_handles ();
942                 }
943                 
944                 if (close_func != NULL) {
945                         if (is_shared) {
946                                 close_func (handle, &shared_handle_data.u);
947                         } else {
948                                 close_func (handle, &handle_data.u);
949                         }
950                 }
951         }
952 }
953
954 void _wapi_handle_register_capabilities (WapiHandleType type,
955                                          WapiHandleCapability caps)
956 {
957         handle_caps[type] = caps;
958 }
959
960 gboolean _wapi_handle_test_capabilities (gpointer handle,
961                                          WapiHandleCapability caps)
962 {
963         guint32 idx = GPOINTER_TO_UINT(handle);
964         WapiHandleType type;
965
966         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
967                 return(FALSE);
968         }
969         
970         type = _WAPI_PRIVATE_HANDLES(idx).type;
971
972 #ifdef DEBUG
973         g_message ("%s: testing 0x%x against 0x%x (%d)", __func__,
974                    handle_caps[type], caps, handle_caps[type] & caps);
975 #endif
976         
977         return((handle_caps[type] & caps) != 0);
978 }
979
980 static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer)
981 {
982         if (handle_ops[type] != NULL &&
983             handle_ops[type]->close != NULL) {
984                 return (handle_ops[type]->close);
985         }
986
987         return (NULL);
988 }
989
990 void _wapi_handle_ops_close (gpointer handle, gpointer data)
991 {
992         guint32 idx = GPOINTER_TO_UINT(handle);
993         WapiHandleType type;
994
995         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
996                 return;
997         }
998         
999         type = _WAPI_PRIVATE_HANDLES(idx).type;
1000
1001         if (handle_ops[type] != NULL &&
1002             handle_ops[type]->close != NULL) {
1003                 handle_ops[type]->close (handle, data);
1004         }
1005 }
1006
1007 void _wapi_handle_ops_signal (gpointer handle)
1008 {
1009         guint32 idx = GPOINTER_TO_UINT(handle);
1010         WapiHandleType type;
1011
1012         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1013                 return;
1014         }
1015         
1016         type = _WAPI_PRIVATE_HANDLES(idx).type;
1017
1018         if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) {
1019                 handle_ops[type]->signal (handle);
1020         }
1021 }
1022
1023 gboolean _wapi_handle_ops_own (gpointer handle)
1024 {
1025         guint32 idx = GPOINTER_TO_UINT(handle);
1026         WapiHandleType type;
1027         
1028         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1029                 return(FALSE);
1030         }
1031         
1032         type = _WAPI_PRIVATE_HANDLES(idx).type;
1033
1034         if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) {
1035                 return(handle_ops[type]->own_handle (handle));
1036         } else {
1037                 return(FALSE);
1038         }
1039 }
1040
1041 gboolean _wapi_handle_ops_isowned (gpointer handle)
1042 {
1043         guint32 idx = GPOINTER_TO_UINT(handle);
1044         WapiHandleType type;
1045
1046         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1047                 return(FALSE);
1048         }
1049         
1050         type = _WAPI_PRIVATE_HANDLES(idx).type;
1051
1052         if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) {
1053                 return(handle_ops[type]->is_owned (handle));
1054         } else {
1055                 return(FALSE);
1056         }
1057 }
1058
1059 guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout)
1060 {
1061         guint32 idx = GPOINTER_TO_UINT(handle);
1062         WapiHandleType type;
1063         
1064         if (!_WAPI_PRIVATE_VALID_SLOT (idx)) {
1065                 return(WAIT_FAILED);
1066         }
1067         
1068         type = _WAPI_PRIVATE_HANDLES(idx).type;
1069         
1070         if (handle_ops[type] != NULL &&
1071             handle_ops[type]->special_wait != NULL) {
1072                 return(handle_ops[type]->special_wait (handle, timeout));
1073         } else {
1074                 return(WAIT_FAILED);
1075         }
1076 }
1077
1078
1079 /**
1080  * CloseHandle:
1081  * @handle: The handle to release
1082  *
1083  * Closes and invalidates @handle, releasing any resources it
1084  * consumes.  When the last handle to a temporary or non-persistent
1085  * object is closed, that object can be deleted.  Closing the same
1086  * handle twice is an error.
1087  *
1088  * Return value: %TRUE on success, %FALSE otherwise.
1089  */
1090 gboolean CloseHandle(gpointer handle)
1091 {
1092         _wapi_handle_unref (handle);
1093         
1094         return(TRUE);
1095 }
1096
1097 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1098                                                gpointer *handles,
1099                                                gboolean waitall,
1100                                                guint32 *retcount,
1101                                                guint32 *lowest)
1102 {
1103         guint32 count, i, iter=0;
1104         gboolean ret;
1105         int thr_ret;
1106         WapiHandleType type;
1107         
1108         /* Lock all the handles, with backoff */
1109 again:
1110         thr_ret = _wapi_handle_lock_shared_handles ();
1111         g_assert (thr_ret == 0);
1112         
1113         for(i=0; i<numhandles; i++) {
1114                 gpointer handle = handles[i];
1115                 guint32 idx = GPOINTER_TO_UINT(handle);
1116
1117 #ifdef DEBUG
1118                 g_message ("%s: attempting to lock %p", __func__, handle);
1119 #endif
1120
1121                 type = _WAPI_PRIVATE_HANDLES(idx).type;
1122
1123                 thr_ret = _wapi_handle_trylock_handle (handle);
1124                 
1125                 if (thr_ret != 0) {
1126                         /* Bummer */
1127                         
1128 #ifdef DEBUG
1129                         g_message ("%s: attempt failed for %p: %s", __func__,
1130                                    handle, strerror (thr_ret));
1131 #endif
1132
1133                         thr_ret = _wapi_handle_unlock_shared_handles ();
1134                         g_assert (thr_ret == 0);
1135                         
1136                         while (i--) {
1137                                 handle = handles[i];
1138                                 idx = GPOINTER_TO_UINT(handle);
1139
1140                                 thr_ret = _wapi_handle_unlock_handle (handle);
1141                                 g_assert (thr_ret == 0);
1142                         }
1143
1144                         /* If iter ever reaches 100 the nanosleep will
1145                          * return EINVAL immediately, but we have a
1146                          * design flaw if that happens.
1147                          */
1148                         iter++;
1149                         if(iter==100) {
1150                                 g_warning ("%s: iteration overflow!",
1151                                            __func__);
1152                                 iter=1;
1153                         }
1154                         
1155 #ifdef DEBUG
1156                         g_message ("%s: Backing off for %d ms", __func__,
1157                                    iter*10);
1158 #endif
1159                         _wapi_handle_spin (10 * iter);
1160                         
1161                         goto again;
1162                 }
1163         }
1164         
1165 #ifdef DEBUG
1166         g_message ("%s: Locked all handles", __func__);
1167 #endif
1168
1169         count=0;
1170         *lowest=numhandles;
1171         
1172         for(i=0; i<numhandles; i++) {
1173                 gpointer handle = handles[i];
1174                 guint32 idx = GPOINTER_TO_UINT(handle);
1175                 
1176                 type = _WAPI_PRIVATE_HANDLES(idx).type;
1177
1178                 _wapi_handle_ref (handle);
1179                 
1180 #ifdef DEBUG
1181                 g_message ("%s: Checking handle %p", __func__, handle);
1182 #endif
1183
1184                 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1185                     (_wapi_handle_ops_isowned (handle) == TRUE)) ||
1186                    (_WAPI_SHARED_HANDLE(type) &&
1187                     WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) ||
1188                    (!_WAPI_SHARED_HANDLE(type) &&
1189                     _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) {
1190                         count++;
1191                         
1192 #ifdef DEBUG
1193                         g_message ("%s: Handle %p signalled", __func__,
1194                                    handle);
1195 #endif
1196                         if(*lowest>i) {
1197                                 *lowest=i;
1198                         }
1199                 }
1200         }
1201         
1202 #ifdef DEBUG
1203         g_message ("%s: %d event handles signalled", __func__, count);
1204 #endif
1205
1206         if ((waitall == TRUE && count == numhandles) ||
1207             (waitall == FALSE && count > 0)) {
1208                 ret=TRUE;
1209         } else {
1210                 ret=FALSE;
1211         }
1212         
1213 #ifdef DEBUG
1214         g_message ("%s: Returning %d", __func__, ret);
1215 #endif
1216
1217         *retcount=count;
1218         
1219         return(ret);
1220 }
1221
1222 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1223 {
1224         guint32 i;
1225         int thr_ret;
1226         
1227         thr_ret = _wapi_handle_unlock_shared_handles ();
1228         g_assert (thr_ret == 0);
1229         
1230         for(i=0; i<numhandles; i++) {
1231                 gpointer handle = handles[i];
1232                 
1233 #ifdef DEBUG
1234                 g_message ("%s: unlocking handle %p", __func__, handle);
1235 #endif
1236
1237                 thr_ret = _wapi_handle_unlock_handle (handle);
1238                 g_assert (thr_ret == 0);
1239         }
1240 }
1241
1242 static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout)
1243 {
1244         struct timespec fake_timeout;
1245         int ret;
1246         
1247         _wapi_calc_timeout (&fake_timeout, 100);
1248         
1249         if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) ||
1250            (fake_timeout.tv_sec == timeout->tv_sec &&
1251                 fake_timeout.tv_nsec > timeout->tv_nsec))) {
1252                 /* Real timeout is less than 100ms time */
1253                 ret=mono_cond_timedwait (cond, mutex, timeout);
1254         } else {
1255                 ret=mono_cond_timedwait (cond, mutex, &fake_timeout);
1256
1257                 /* Mask the fake timeout, this will cause
1258                  * another poll if the cond was not really signaled
1259                  */
1260                 if (ret==ETIMEDOUT) {
1261                         ret=0;
1262                 }
1263         }
1264         
1265         return(ret);
1266 }
1267
1268 int _wapi_handle_wait_signal (void)
1269 {
1270         return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL);
1271 }
1272
1273 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1274 {
1275         return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout);
1276 }
1277
1278 int _wapi_handle_wait_signal_handle (gpointer handle)
1279 {
1280 #ifdef DEBUG
1281         g_message ("%s: waiting for %p", __func__, handle);
1282 #endif
1283         
1284         return _wapi_handle_timedwait_signal_handle (handle, NULL);
1285 }
1286
1287 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1288                                           struct timespec *timeout)
1289 {
1290 #ifdef DEBUG
1291         g_message ("%s: waiting for %p (type %s)", __func__, handle,
1292                    _wapi_handle_typename[_wapi_handle_type (handle)]);
1293 #endif
1294         
1295         if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
1296                 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1297                         return (0);
1298                 }
1299                 if (timeout != NULL) {
1300                         struct timespec fake_timeout;
1301                         _wapi_calc_timeout (&fake_timeout, 100);
1302                 
1303                         if ((fake_timeout.tv_sec > timeout->tv_sec) ||
1304                                 (fake_timeout.tv_sec == timeout->tv_sec &&
1305                                  fake_timeout.tv_nsec > timeout->tv_nsec)) {
1306                                 /* FIXME: Real timeout is less than
1307                                  * 100ms time, but is it really worth
1308                                  * calculating to the exact ms?
1309                                  */
1310                                 _wapi_handle_spin (100);
1311
1312                                 if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
1313                                         return (0);
1314                                 } else {
1315                                         return (ETIMEDOUT);
1316                                 }
1317                         }
1318                 }
1319                 _wapi_handle_spin (100);
1320                 return (0);
1321                 
1322         } else {
1323                 guint32 idx = GPOINTER_TO_UINT(handle);
1324                 return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout);
1325         }
1326 }
1327
1328 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1329                                         guint32 new_sharemode,
1330                                         guint32 new_access,
1331                                         guint32 *old_sharemode,
1332                                         guint32 *old_access,
1333                                         struct _WapiFileShare **share_info)
1334 {
1335         struct _WapiFileShare *file_share;
1336         guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF);
1337         int thr_ret, i, first_unused = -1;
1338         gboolean exists = FALSE;
1339         
1340         /* Prevents entries from expiring under us as we search
1341          */
1342         thr_ret = _wapi_handle_lock_shared_handles ();
1343         g_assert (thr_ret == 0);
1344         
1345         /* Prevent new entries racing with us */
1346         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1347         g_assert (thr_ret == 0);
1348         
1349         /* If a linear scan gets too slow we'll have to fit a hash
1350          * table onto the shared mem backing store
1351          */
1352         *share_info = NULL;
1353         for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) {
1354                 file_share = &_wapi_fileshare_layout->share_info[i];
1355
1356                 /* Make a note of an unused slot, in case we need to
1357                  * store share info
1358                  */
1359                 if (first_unused == -1 && file_share->handle_refs == 0) {
1360                         first_unused = i;
1361                         continue;
1362                 }
1363                 
1364                 if (file_share->handle_refs == 0) {
1365                         continue;
1366                 }
1367                 
1368                 if (file_share->device == device &&
1369                     file_share->inode == inode) {
1370                         *old_sharemode = file_share->sharemode;
1371                         *old_access = file_share->access;
1372                         *share_info = file_share;
1373                         
1374                         /* Increment the reference count while we
1375                          * still have sole access to the shared area.
1376                          * This makes the increment atomic wrt
1377                          * collections
1378                          */
1379                         InterlockedIncrement (&file_share->handle_refs);
1380                         
1381                         exists = TRUE;
1382                         break;
1383                 }
1384         }
1385         
1386         if (!exists) {
1387                 if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) {
1388                         /* No more space */
1389                 } else {
1390                         if (first_unused == -1) {
1391                                 file_share = &_wapi_fileshare_layout->share_info[++i];
1392                                 _wapi_fileshare_layout->hwm = i;
1393                         } else {
1394                                 file_share = &_wapi_fileshare_layout->share_info[first_unused];
1395                         }
1396                         
1397                         file_share->device = device;
1398                         file_share->inode = inode;
1399                         file_share->opened_by_pid = getpid ();
1400                         file_share->sharemode = new_sharemode;
1401                         file_share->access = new_access;
1402                         file_share->handle_refs = 1;
1403                         *share_info = file_share;
1404                 }
1405         }
1406
1407         if (*share_info != NULL) {
1408                 InterlockedExchange (&(*share_info)->timestamp, now);
1409         }
1410         
1411         thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1412
1413         _wapi_handle_unlock_shared_handles ();
1414
1415         return(exists);
1416 }
1417
1418 /* If we don't have the info in /proc, check if the process that
1419  * opened this share info is still there (it's not a perfect method,
1420  * due to pid reuse)
1421  */
1422 static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info)
1423 {
1424         if (kill (share_info->opened_by_pid, 0) == -1 &&
1425             (errno == ESRCH ||
1426              errno == EPERM)) {
1427                 /* It's gone completely (or there's a new process
1428                  * owned by someone else) so mark this share info as
1429                  * dead
1430                  */
1431 #ifdef DEBUG
1432                 g_message ("%s: Didn't find it, destroying entry", __func__);
1433 #endif
1434
1435                 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1436         }
1437 }
1438
1439 /* Scan /proc/<pids>/fd/ for open file descriptors to the file in
1440  * question.  If there are none, reset the share info.
1441  *
1442  * This implementation is Linux-specific; legacy systems will have to
1443  * implement their own ways of finding out if a particular file is
1444  * open by a process.
1445  */
1446 void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd)
1447 {
1448         gboolean found = FALSE, proc_fds = FALSE;
1449         pid_t self = getpid();
1450         int pid;
1451         int thr_ret, i;
1452         
1453         /* Prevents entries from expiring under us if we remove this
1454          * one
1455          */
1456         thr_ret = _wapi_handle_lock_shared_handles ();
1457         g_assert (thr_ret == 0);
1458         
1459         /* Prevent new entries racing with us */
1460         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1461         g_assert (thr_ret == 0);
1462         
1463         /* If there is no /proc, there's nothing more we can do here */
1464         if (access ("/proc", F_OK) == -1) {
1465                 _wapi_handle_check_share_by_pid (share_info);
1466                 goto done;
1467         }
1468
1469         /* If there's another handle that thinks it owns this fd, then even
1470          * if the fd has been closed behind our back consider it still owned.
1471          * See bugs 75764 and 75891
1472          */
1473         for (i = 0; i < _wapi_fd_reserve; i++) {
1474                 struct _WapiHandleUnshared *handle = &_WAPI_PRIVATE_HANDLES(i);
1475
1476                 if (i != fd &&
1477                     handle->type == WAPI_HANDLE_FILE) {
1478                         struct _WapiHandle_file *file_handle = &handle->u.file;
1479
1480                         if (file_handle->share_info == share_info) {
1481 #ifdef DEBUG
1482                                 g_message ("%s: handle 0x%x has this file open!",
1483                                            __func__, i);
1484 #endif
1485
1486                                 goto done;
1487                         }
1488                 }
1489         }
1490
1491         for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) {
1492                 struct _WapiHandleShared *shared;
1493                 struct _WapiHandle_process *process_handle;
1494
1495                 shared = &_wapi_shared_layout->handles[i];
1496                 
1497                 if (shared->type == WAPI_HANDLE_PROCESS) {
1498                         DIR *fd_dir;
1499                         struct dirent *fd_entry;
1500                         char subdir[_POSIX_PATH_MAX];
1501
1502                         process_handle = &shared->u.process;
1503                         pid = process_handle->id;
1504                 
1505                         /* Look in /proc/<pid>/fd/ but ignore
1506                          * /proc/<our pid>/fd/<fd>, as we have the
1507                          * file open too
1508                          */
1509                         g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd",
1510                                     pid);
1511                         
1512                         fd_dir = opendir (subdir);
1513                         if (fd_dir == NULL) {
1514                                 continue;
1515                         }
1516
1517 #ifdef DEBUG
1518                         g_message ("%s: Looking in %s", __func__, subdir);
1519 #endif
1520                         
1521                         proc_fds = TRUE;
1522                         
1523                         while ((fd_entry = readdir (fd_dir)) != NULL) {
1524                                 char path[_POSIX_PATH_MAX];
1525                                 struct stat link_stat;
1526                                 
1527                                 if (!strcmp (fd_entry->d_name, ".") ||
1528                                     !strcmp (fd_entry->d_name, "..") ||
1529                                     (pid == self &&
1530                                      fd == atoi (fd_entry->d_name))) {
1531                                         continue;
1532                                 }
1533
1534                                 g_snprintf (path, _POSIX_PATH_MAX,
1535                                             "/proc/%d/fd/%s", pid,
1536                                             fd_entry->d_name);
1537                                 
1538                                 stat (path, &link_stat);
1539                                 if (link_stat.st_dev == share_info->device &&
1540                                     link_stat.st_ino == share_info->inode) {
1541 #ifdef DEBUG
1542                                         g_message ("%s:  Found it at %s",
1543                                                    __func__, path);
1544 #endif
1545
1546                                         found = TRUE;
1547                                 }
1548                         }
1549                         
1550                         closedir (fd_dir);
1551                 }
1552         }
1553
1554         if (proc_fds == FALSE) {
1555                 _wapi_handle_check_share_by_pid (share_info);
1556         } else if (found == FALSE) {
1557                 /* Blank out this entry, as it is stale */
1558 #ifdef DEBUG
1559                 g_message ("%s: Didn't find it, destroying entry", __func__);
1560 #endif
1561
1562                 memset (share_info, '\0', sizeof(struct _WapiFileShare));
1563         }
1564
1565 done:
1566         thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1567
1568         _wapi_handle_unlock_shared_handles ();
1569 }
1570
1571 void _wapi_handle_dump (void)
1572 {
1573         struct _WapiHandleUnshared *handle_data;
1574         guint32 i, k;
1575         int thr_ret;
1576         
1577         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1578                               (void *)&scan_mutex);
1579         thr_ret = mono_mutex_lock (&scan_mutex);
1580         g_assert (thr_ret == 0);
1581         
1582         for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1583                 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1584                         handle_data = &_wapi_private_handles [i][k];
1585
1586                         if (handle_data->type == WAPI_HANDLE_UNUSED) {
1587                                 continue;
1588                         }
1589                 
1590                         g_print ("%3x [%7s] %s %d ",
1591                                  i * _WAPI_HANDLE_INITIAL_COUNT + k,
1592                                  _wapi_handle_typename[handle_data->type],
1593                                  handle_data->signalled?"Sg":"Un",
1594                                  handle_data->ref);
1595                         handle_details[handle_data->type](&handle_data->u);
1596                         g_print ("\n");
1597                 }
1598         }
1599
1600         thr_ret = mono_mutex_unlock (&scan_mutex);
1601         g_assert (thr_ret == 0);
1602         pthread_cleanup_pop (0);
1603 }
1604
1605 static void _wapi_shared_details (gpointer handle_info)
1606 {
1607         struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info;
1608         
1609         g_print ("offset: 0x%x", shared->offset);
1610 }
1611
1612 void _wapi_handle_update_refs (void)
1613 {
1614         guint32 i, k;
1615         int thr_ret;
1616         guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF);
1617         
1618         thr_ret = _wapi_handle_lock_shared_handles ();
1619         g_assert (thr_ret == 0);
1620
1621         /* Prevent file share entries racing with us */
1622         thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_FILESHARE);
1623         g_assert(thr_ret == 0);
1624
1625         pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
1626                               (void *)&scan_mutex);
1627         thr_ret = mono_mutex_lock (&scan_mutex);
1628         
1629         for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) {
1630                 for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) {
1631                         struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k];
1632
1633                         if (_WAPI_SHARED_HANDLE(handle->type)) {
1634                                 struct _WapiHandleShared *shared_data;
1635                                 
1636 #ifdef DEBUG
1637                                 g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__,
1638                                            getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]);
1639 #endif
1640
1641                                 shared_data = &_wapi_shared_layout->handles[handle->u.shared.offset];
1642
1643 #ifdef DEBUG
1644                                 g_message ("%s: (%d) Updating timestamp of handle 0x%x",
1645                                            __func__, getpid(),
1646                                            handle->u.shared.offset);
1647 #endif
1648
1649                                 InterlockedExchange (&shared_data->timestamp,
1650                                                      now);
1651                         } else if (handle->type == WAPI_HANDLE_FILE) {
1652                                 struct _WapiHandle_file *file_handle = &handle->u.file;
1653                                 
1654 #ifdef DEBUG
1655                                 g_message ("%s: (%d) handle 0x%x is FILE", __func__,
1656                                            getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k);
1657 #endif
1658                                 
1659                                 g_assert (file_handle->share_info != NULL);
1660
1661 #ifdef DEBUG
1662                                 g_message ("%s: (%d) Inc refs on fileshare 0x%x",
1663                                            __func__, getpid(),
1664                                            (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare));
1665 #endif
1666
1667                                 InterlockedExchange (&file_handle->share_info->timestamp, now);
1668                         }
1669                 }
1670         }
1671
1672         thr_ret = mono_mutex_unlock (&scan_mutex);
1673         g_assert (thr_ret == 0);
1674         pthread_cleanup_pop (0);
1675         
1676         thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_FILESHARE);
1677
1678         _wapi_handle_unlock_shared_handles ();
1679 }