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