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