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