2 * handles.c: Generic and internal operations on handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
15 #include <sys/types.h>
16 #include <sys/socket.h>
20 #include <mono/os/gc_wrapper.h>
22 #include <mono/io-layer/wapi.h>
23 #include <mono/io-layer/wapi-private.h>
24 #include <mono/io-layer/handles-private.h>
25 #include <mono/io-layer/mono-mutex.h>
26 #include <mono/io-layer/shared.h>
27 #include <mono/io-layer/misc-private.h>
28 #include <mono/io-layer/daemon-messages.h>
31 #undef HEAVY_DEBUG /* This will print handle counts on every handle created */
33 /* Shared threads don't seem to work yet */
34 #undef _POSIX_THREAD_PROCESS_SHARED
37 * This flag _MUST_ remain set to FALSE in the daemon process. When
38 * we exec()d a standalone daemon, that happened because shared_init()
39 * didnt get called in the daemon process. Now we just fork() without
40 * exec(), we need to ensure that the fork() happens when shared is
43 * This is further complicated by the second attempt to start the
44 * daemon if the connect() fails.
46 static gboolean shared=FALSE;
48 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
49 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
63 static int daemon_sock;
65 static pthread_mutexattr_t mutex_shared_attr;
66 static pthread_condattr_t cond_shared_attr;
68 struct _WapiHandleShared_list **_wapi_shared_data=NULL;
69 struct _WapiHandleScratch *_wapi_shared_scratch=NULL;
70 struct _WapiHandlePrivate_list **_wapi_private_data=NULL;
71 pthread_mutex_t _wapi_shared_mutex=PTHREAD_MUTEX_INITIALIZER;
73 /* This holds the length of the _wapi_shared_data and
74 * _wapi_private_data arrays, so we know if a segment is off the end
75 * of the array, requiring a realloc
77 guint32 _wapi_shm_mapped_segments;
79 static void shared_init (void)
81 struct sockaddr_un shared_socket_address;
82 gboolean tried_once=FALSE;
85 _wapi_shared_data=g_new0 (struct _WapiHandleShared_list *, 1);
86 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list *, 1);
90 #ifndef DISABLE_SHARED_HANDLES
91 if(getenv ("MONO_DISABLE_SHM"))
95 #ifndef DISABLE_SHARED_HANDLES
97 /* Ensure that shared==FALSE while _wapi_shm_attach()
102 shared=_wapi_shm_attach (&_wapi_shared_data[0],
103 &_wapi_shared_scratch);
105 g_warning ("Failed to attach shared memory! "
106 "Falling back to non-shared handles");
108 #endif /* DISABLE_SHARED_HANDLES */
113 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
114 shared_socket_address.sun_family=AF_UNIX;
115 memcpy (shared_socket_address.sun_path,
116 _wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH);
117 ret=connect (daemon_sock,
118 (struct sockaddr *)&shared_socket_address,
119 sizeof(struct sockaddr_un));
121 if(tried_once==TRUE) {
122 g_warning (G_GNUC_PRETTY_FUNCTION
123 ": connect to daemon failed: %s",
125 /* Fall back to private handles */
128 /* It's possible that the daemon
129 * crashed without destroying the
130 * shared memory segment (thus fooling
131 * subsequent processes into thinking
132 * the daemon is still active).
134 * Destroy the shared memory segment
135 * and try once more. This won't
136 * break running apps, but no new apps
137 * will be able to see the current
138 * shared memory segment.
141 _wapi_shm_destroy ();
150 g_message (G_GNUC_PRETTY_FUNCTION
151 ": Using process-private handles");
153 _wapi_shared_data[0]=g_new0 (struct _WapiHandleShared_list, 1);
154 _wapi_shared_data[0]->num_segments=1;
156 _wapi_shared_scratch=g_new0 (struct _WapiHandleScratch, 1);
158 _wapi_private_data[0]=g_new0 (struct _WapiHandlePrivate_list, 1);
159 _wapi_shm_mapped_segments=1;
161 pthread_mutexattr_init (&mutex_shared_attr);
162 pthread_condattr_init (&cond_shared_attr);
164 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
165 pthread_mutexattr_setpshared (&mutex_shared_attr,
166 PTHREAD_PROCESS_SHARED);
167 pthread_condattr_setpshared (&cond_shared_attr,
168 PTHREAD_PROCESS_SHARED);
174 print_handle_count (gint mask)
176 gint *count, num_handles;
178 static const gchar *names [] = {"WAPI_HANDLE_UNUSED",
180 "WAPI_HANDLE_CONSOLE",
181 "WAPI_HANDLE_THREAD",
185 "WAPI_HANDLE_SOCKET",
187 "WAPI_HANDLE_PROCESS",
192 num_handles=_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT;
193 count=g_new0 (gint, num_handles);
195 for (i = 1; i < num_handles; i++) {
196 struct _WapiHandleShared *shared;
197 guint32 segment, idx;
199 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
200 _wapi_handle_ensure_mapped (segment);
202 shared = &_wapi_handle_get_shared_segment (segment)->handles[idx];
203 count [shared->type]++;
206 for (i = 0; i < num_handles; i++)
207 if ((i & mask) == i) /* Always prints the UNUSED count */
208 g_print ("%s: %d\n", names [i], count [i]);
212 #endif /* HEAVY_DEBUG */
215 * _wapi_handle_new_internal:
216 * @type: Init handle to this type
218 * Search for a free handle and initialize it. Return the handle on
219 * success and 0 on failure.
221 guint32 _wapi_handle_new_internal (WapiHandleType type)
223 guint32 segment, idx;
225 static guint32 last=1;
227 /* A linear scan should be fast enough. Start from the last
228 * allocation, assuming that handles are allocated more often
229 * than they're freed. Leave 0 (NULL) as a guard
232 print_handle_count (0xFFF);
235 _wapi_handle_segment (GUINT_TO_POINTER (last), &segment, &idx);
236 for(i=segment; i<_wapi_handle_get_shared_segment (0)->num_segments;
242 for(j=idx; j<_WAPI_HANDLES_PER_SEGMENT; j++) {
243 struct _WapiHandleShared *shared;
245 /* Make sure we dont try and assign handle 0 */
250 shared=&_wapi_handle_get_shared_segment (i)->handles[j];
252 if(shared->type==WAPI_HANDLE_UNUSED) {
253 last=(_wapi_handle_index (i, j)+1) % (_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT);
255 shared->signalled=FALSE;
256 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
257 mono_mutex_init (&shared.signal_mutex, &mutex_shared_attr);
258 pthread_cond_init (&shared.signal_cond, &cond_shared_attr);
261 return(_wapi_handle_index (i, j));
267 /* Try again from the beginning */
272 /* Will need a new segment. The caller will sort it out */
277 gpointer _wapi_handle_new (WapiHandleType type)
279 static mono_once_t shared_init_once = MONO_ONCE_INIT;
280 static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
281 guint32 handle_idx, idx, segment;
283 WapiHandleRequest new={0};
284 WapiHandleResponse new_resp={0};
286 gboolean tried_collect=FALSE;
289 mono_once (&shared_init_once, shared_init);
293 new.type=WapiHandleRequestType_New;
296 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
298 if (new_resp.type==WapiHandleResponseType_New) {
299 handle_idx=new_resp.u.new.handle;
301 g_warning (G_GNUC_PRETTY_FUNCTION
302 ": bogus daemon response, type %d",
304 g_assert_not_reached ();
307 pthread_mutex_lock (&scan_mutex);
308 handle_idx=_wapi_handle_new_internal (type);
310 /* Try and get a new segment, and have another go */
311 segment=_wapi_handle_get_shared_segment (0)->num_segments;
312 _wapi_handle_ensure_mapped (segment);
314 if(_wapi_handle_get_shared_segment (segment)!=NULL) {
315 /* Got a new segment */
316 _wapi_handle_get_shared_segment (0)->num_segments++;
317 handle_idx=_wapi_handle_new_internal (type);
319 /* Map failed. Just return 0 meaning
325 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
326 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
327 pthread_mutex_unlock (&scan_mutex);
331 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
334 /* See if we can reclaim some handles by forcing a GC
337 if(tried_collect==FALSE) {
338 g_warning (G_GNUC_PRETTY_FUNCTION
339 ": Seeing if GC collection helps...");
340 GC_gcollect (); /* FIXME: we should wait for finalizers to be called */
344 g_warning (G_GNUC_PRETTY_FUNCTION
345 ": didn't help, returning error");
349 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
352 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
353 _wapi_handle_ensure_mapped (segment);
355 if(_wapi_private_data!=NULL) {
356 _wapi_handle_get_private_segment (segment)->handles[idx].type=type;
359 #if !defined(_POSIX_THREAD_PROCESS_SHARED) || _POSIX_THREAD_PROCESS_SHARED == -1
360 mono_mutex_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex, &mutex_shared_attr);
361 pthread_cond_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond, &cond_shared_attr);
363 handle=GUINT_TO_POINTER (handle_idx);
366 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
372 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
373 gpointer *shared, gpointer *private)
375 struct _WapiHandleShared *shared_handle_data;
376 struct _WapiHandlePrivate *private_handle_data;
380 _wapi_handle_segment (handle, &segment, &idx);
381 _wapi_handle_ensure_mapped (segment);
383 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
386 *shared=&shared_handle_data->u;
390 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
392 *private=&private_handle_data->u;
395 if(shared_handle_data->type!=type) {
396 /* If shared type is UNUSED, see if the private type
397 * matches what we are looking for - this can happen
398 * when the handle is being destroyed and the
399 * close_private function is looking up the private
402 if(shared_handle_data->type==WAPI_HANDLE_UNUSED &&
403 (private!=NULL && private_handle_data->type==type)) {
413 gpointer _wapi_search_handle (WapiHandleType type,
414 gboolean (*check)(gpointer test, gpointer user),
416 gpointer *shared, gpointer *private)
418 struct _WapiHandleShared *shared_handle_data;
419 struct _WapiHandlePrivate *private_handle_data;
420 guint32 i, segment, idx;
422 for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
423 struct _WapiHandleShared *shared;
425 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
426 _wapi_handle_ensure_mapped (segment);
428 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
430 if(shared->type==type) {
431 if(check (GUINT_TO_POINTER (i), user_data)==TRUE) {
437 if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
438 return(GUINT_TO_POINTER (0));
442 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
444 *shared=&shared_handle_data->u;
448 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
450 *private=&private_handle_data->u;
453 return(GUINT_TO_POINTER (i));
456 void _wapi_handle_ref (gpointer handle)
459 WapiHandleRequest req={0};
460 WapiHandleResponse resp={0};
462 req.type=WapiHandleRequestType_Open;
463 req.u.open.handle=GPOINTER_TO_UINT (handle);
465 _wapi_daemon_request_response (daemon_sock, &req, &resp);
466 if(resp.type!=WapiHandleResponseType_Open) {
467 g_warning (G_GNUC_PRETTY_FUNCTION
468 ": bogus daemon response, type %d",
470 g_assert_not_reached ();
473 guint32 idx, segment;
475 _wapi_handle_segment (handle, &segment, &idx);
477 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
480 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
482 _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
487 void _wapi_handle_unref (gpointer handle)
489 guint32 idx, segment;
492 _wapi_handle_segment (handle, &segment, &idx);
495 WapiHandleRequest req={0};
496 WapiHandleResponse resp={0};
498 req.type=WapiHandleRequestType_Close;
499 req.u.close.handle=GPOINTER_TO_UINT (handle);
501 _wapi_daemon_request_response (daemon_sock, &req, &resp);
502 if(resp.type!=WapiHandleResponseType_Close) {
503 g_warning (G_GNUC_PRETTY_FUNCTION
504 ": bogus daemon response, type %d",
506 g_assert_not_reached ();
508 destroy=resp.u.close.destroy;
511 _wapi_handle_get_shared_segment (segment)->handles[idx].ref--;
514 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", handle, _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
517 /* Possible race condition here if another thread refs
518 * the handle between here and setting the type to
519 * UNUSED. I could lock a mutex, but I'm not sure
520 * that allowing a handle reference to reach 0 isn't
521 * an application bug anyway.
523 destroy=(_wapi_handle_get_shared_segment (segment)->handles[idx].ref==0);
528 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
533 _wapi_handle_ops_close_shared (handle);
535 mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
536 pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
537 memset (&_wapi_handle_get_shared_segment (segment)->handles[idx].u, '\0', sizeof(_wapi_handle_get_shared_segment (segment)->handles[idx].u));
540 #if !defined(_POSIX_THREAD_PROCESS_SHARED) || _POSIX_THREAD_PROCESS_SHARED == -1
542 mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
543 pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
547 _wapi_handle_ops_close_private (handle);
548 _wapi_handle_get_shared_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
552 #define HDRSIZE sizeof(struct _WapiScratchHeader)
554 static pthread_mutex_t _wapi_scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
556 /* _wapi_scratch_mutex must be held when this function is called in
557 * the non-shared case
559 static void _wapi_handle_scratch_expand (void)
561 guint32 old_len, new_len;
563 old_len=sizeof(struct _WapiHandleScratch) +
564 _wapi_shared_scratch->data_len;
565 new_len=old_len+_WAPI_SHM_SCRATCH_SIZE;
567 if(_wapi_shared_scratch->is_shared==TRUE) {
568 /* expand via mmap() */
569 _wapi_shared_scratch=_wapi_shm_file_expand (_wapi_shared_scratch, WAPI_SHM_SCRATCH, 0, old_len, new_len);
571 _wapi_shared_scratch=_wapi_g_renew0 (_wapi_shared_scratch, old_len, new_len);
573 _wapi_shared_scratch->data_len+=_WAPI_SHM_SCRATCH_SIZE;
576 /* _wapi_scratch_mutex must be held when this function is called in
577 * the non-shared case
579 static guint32 _wapi_handle_scratch_locate_space (guint32 bytes)
581 guint32 idx=0, last_idx=0;
582 struct _WapiScratchHeader *hdr, *last_hdr;
583 gboolean last_was_free=FALSE;
584 guchar *storage=_wapi_shared_scratch->scratch_data;
587 g_message (G_GNUC_PRETTY_FUNCTION
588 ": looking for %d bytes of scratch space (%d bytes total)",
589 bytes, _wapi_shared_scratch->data_len);
592 while(idx< _wapi_shared_scratch->data_len) {
593 hdr=(struct _WapiScratchHeader *)&storage[idx];
595 /* Do a simple first-fit allocation, coalescing
596 * adjacent free blocks as we progress through the
599 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
600 hdr->length >= bytes + HDRSIZE) {
602 guint32 old_length=hdr->length;
604 g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
607 hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
611 /* Put a new header in at the end of the used
614 hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
615 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
616 hdr->length = old_length-bytes-HDRSIZE;
619 g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
623 * It was memset(0..) when free/made so no need to do it here
627 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
628 last_was_free == FALSE) {
630 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
633 /* save this point in case we can coalesce it with
634 * the next block, if that is free.
639 idx+=(hdr->length+HDRSIZE);
640 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
641 last_was_free == TRUE) {
643 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
646 /* This block and the previous are both free,
649 last_hdr->length += (hdr->length + HDRSIZE);
651 /* If the new block is now big enough, use it
652 * (next time round the loop)
654 if(last_hdr->length >= bytes + HDRSIZE) {
657 /* leave the last free info as it is,
658 * in case the next block is also free
659 * and can be coalesced too
661 idx=last_idx+last_hdr->length+HDRSIZE;
665 g_message (G_GNUC_PRETTY_FUNCTION
666 ": found used block at %d, length %d", idx,
670 /* must be used, try next chunk */
671 idx+=(hdr->length+HDRSIZE);
673 /* Don't let the coalescing blow away this block */
676 /* But remember where the last block started */
681 /* Not enough free space. last_idx points to the last block.
682 * If it's free, just tack on more space and update the
683 * length. If it's allocated, it must have fit right into the
684 * available space, so add more space and add a new header
687 _wapi_handle_scratch_expand ();
688 storage=_wapi_shared_scratch->scratch_data;
690 hdr=(struct _WapiScratchHeader *)&storage[last_idx];
691 if(hdr->flags & WAPI_SHM_SCRATCH_FREE) {
692 hdr->length+=_WAPI_SHM_SCRATCH_SIZE;
694 idx=(hdr->length+HDRSIZE);
695 hdr=(struct _WapiScratchHeader *)&storage[idx];
696 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
697 hdr->length = _WAPI_SHM_SCRATCH_SIZE-HDRSIZE;
700 /* The caller will try again */
705 * _wapi_handle_scratch_store_internal:
706 * @bytes: Allocate no. bytes
708 * Like malloc(3) except its for the shared memory segment's scratch
709 * part. Memory block returned is zeroed out.
711 guint32 _wapi_handle_scratch_store_internal (guint32 bytes, gboolean *remap)
715 struct _WapiScratchHeader *hdr;
718 g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
723 if(_wapi_shared_scratch->data_len==0) {
724 /* Need to expand the data array for the first use */
726 g_message (G_GNUC_PRETTY_FUNCTION
727 ": setting up scratch space");
730 _wapi_handle_scratch_expand ();
734 storage=_wapi_shared_scratch->scratch_data;
735 hdr=(struct _WapiScratchHeader *)&storage[0];
736 if(hdr->flags==0 && hdr->length==0) {
737 /* Need to initialise scratch data */
738 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
739 hdr->length = _wapi_shared_scratch->data_len - HDRSIZE;
742 idx=_wapi_handle_scratch_locate_space (bytes);
744 /* Some more space will have been allocated, so try again */
746 g_message (G_GNUC_PRETTY_FUNCTION ": trying again");
749 idx=_wapi_handle_scratch_locate_space (bytes);
756 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
758 guint32 idx, store_bytes;
762 g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
765 /* No point storing no data */
770 /* Align bytes to 32 bits (needed for sparc at least) */
771 store_bytes = (((bytes) + 3) & (~3));
773 pthread_mutex_lock (&_wapi_scratch_mutex);
776 WapiHandleRequest scratch={0};
777 WapiHandleResponse scratch_resp={0};
778 guint32 old_len=sizeof(struct _WapiHandleScratch) +
779 _wapi_shared_scratch->data_len;
781 scratch.type=WapiHandleRequestType_Scratch;
782 scratch.u.scratch.length=store_bytes;
784 _wapi_daemon_request_response (daemon_sock, &scratch,
787 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
788 idx=scratch_resp.u.scratch.idx;
789 remap=scratch_resp.u.scratch.remap;
791 g_warning (G_GNUC_PRETTY_FUNCTION
792 ": bogus daemon response, type %d",
794 g_assert_not_reached ();
798 munmap (_wapi_shared_scratch, old_len);
799 _wapi_shared_scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, NULL);
802 idx=_wapi_handle_scratch_store_internal (store_bytes, &remap);
804 /* Failed to allocate space */
805 pthread_mutex_unlock (&_wapi_scratch_mutex);
811 g_message (G_GNUC_PRETTY_FUNCTION
812 ": stored [%s] at %d (len %d, aligned len %d)",
813 (char *)data, idx, bytes, store_bytes);
816 memcpy (&_wapi_shared_scratch->scratch_data[idx], data, bytes);
818 pthread_mutex_unlock (&_wapi_scratch_mutex);
823 guint32 _wapi_handle_scratch_store_string_array (gchar **data)
825 guint32 *stored_strings, count=0, i, idx;
828 /* No point storing no data */
834 while(*strings!=NULL) {
840 g_message (G_GNUC_PRETTY_FUNCTION ": %d strings to store", count);
847 /* stored_strings[0] is the count */
848 stored_strings=g_new0 (guint32, count+1);
849 stored_strings[0]=count;
852 for(i=0; i<count; i++) {
853 stored_strings[i+1]=_wapi_handle_scratch_store (strings[i], strlen (strings[i]));
856 idx=_wapi_handle_scratch_store (stored_strings,
857 sizeof(guint32)*(count+1));
862 gpointer _wapi_handle_scratch_lookup (guint32 idx)
864 struct _WapiScratchHeader *hdr;
868 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
872 pthread_mutex_lock (&_wapi_scratch_mutex);
874 storage=_wapi_shared_scratch->scratch_data;
876 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
877 ret=g_malloc0 (hdr->length+1);
878 memcpy (ret, &storage[idx], hdr->length);
880 pthread_mutex_unlock (&_wapi_scratch_mutex);
885 gchar **_wapi_handle_scratch_lookup_string_array (guint32 idx)
888 guint32 *stored_strings;
891 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
895 stored_strings=_wapi_handle_scratch_lookup (idx);
896 if(stored_strings==NULL) {
900 /* stored_strings[0] is the number of strings, the index of
901 * each string follows
903 count=stored_strings[0];
906 g_message (G_GNUC_PRETTY_FUNCTION
907 ": looking up an array of %d strings", count);
910 /* NULL-terminate the array */
911 strings=g_new0 (gchar *, count+1);
913 for(i=0; i<count; i++) {
914 strings[i]=_wapi_handle_scratch_lookup (stored_strings[i+1]);
917 g_message (G_GNUC_PRETTY_FUNCTION ": string %d is [%s]", i,
922 g_free (stored_strings);
928 * _wapi_handle_scratch_delete_internal:
929 * @idx: Index to free block
931 * Like free(3) except its for the shared memory segment's scratch
934 void _wapi_handle_scratch_delete_internal (guint32 idx)
936 struct _WapiScratchHeader *hdr;
939 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
943 pthread_mutex_lock (&_wapi_scratch_mutex);
945 storage=_wapi_shared_scratch->scratch_data;
947 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
948 memset (&storage[idx], '\0', hdr->length);
949 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
951 /* We could coalesce forwards here if the next block is also
952 * free, but the _store() function will do that anyway.
955 pthread_mutex_unlock (&_wapi_scratch_mutex);
958 void _wapi_handle_scratch_delete (guint32 idx)
961 WapiHandleRequest scratch_free={0};
962 WapiHandleResponse scratch_free_resp={0};
964 scratch_free.type=WapiHandleRequestType_ScratchFree;
965 scratch_free.u.scratch_free.idx=idx;
967 _wapi_daemon_request_response (daemon_sock, &scratch_free,
970 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
971 g_warning (G_GNUC_PRETTY_FUNCTION
972 ": bogus daemon response, type %d",
973 scratch_free_resp.type);
974 g_assert_not_reached ();
977 _wapi_handle_scratch_delete_internal (idx);
981 void _wapi_handle_scratch_delete_string_array (guint32 idx)
983 guint32 *stored_strings;
986 stored_strings=_wapi_handle_scratch_lookup (idx);
987 if(stored_strings==NULL) {
991 /* stored_strings[0] is the number of strings, the index of
992 * each string follows
994 count=stored_strings[0];
997 g_message (G_GNUC_PRETTY_FUNCTION ": deleting an array of %d strings",
1001 for(i=1; i<count; i++) {
1002 _wapi_handle_scratch_delete (stored_strings[i]);
1005 _wapi_handle_scratch_delete (idx);
1007 g_free (stored_strings);
1010 void _wapi_handle_register_capabilities (WapiHandleType type,
1011 WapiHandleCapability caps)
1013 handle_caps[type]=caps;
1016 gboolean _wapi_handle_test_capabilities (gpointer handle,
1017 WapiHandleCapability caps)
1019 guint32 idx, segment;
1020 WapiHandleType type;
1022 _wapi_handle_segment (handle, &segment, &idx);
1024 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1027 g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
1028 handle_caps[type], caps, handle_caps[type] & caps);
1031 return((handle_caps[type] & caps)!=0);
1034 void _wapi_handle_ops_close_shared (gpointer handle)
1036 guint32 idx, segment;
1037 WapiHandleType type;
1039 _wapi_handle_segment (handle, &segment, &idx);
1041 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1043 if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
1044 handle_ops[type]->close_shared (handle);
1048 void _wapi_handle_ops_close_private (gpointer handle)
1050 guint32 idx, segment;
1051 WapiHandleType type;
1053 _wapi_handle_segment (handle, &segment, &idx);
1055 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1057 /* When a handle in the process of being destroyed the shared
1058 * type has already been set to UNUSED
1060 if(type==WAPI_HANDLE_UNUSED && _wapi_private_data!=NULL) {
1061 type=_wapi_handle_get_private_segment (segment)->handles[idx].type;
1064 if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
1065 handle_ops[type]->close_private (handle);
1069 void _wapi_handle_ops_signal (gpointer handle)
1071 guint32 idx, segment;
1072 WapiHandleType type;
1074 _wapi_handle_segment (handle, &segment, &idx);
1076 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1078 if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
1079 handle_ops[type]->signal (handle);
1083 void _wapi_handle_ops_own (gpointer handle)
1085 guint32 idx, segment;
1086 WapiHandleType type;
1088 _wapi_handle_segment (handle, &segment, &idx);
1090 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1092 if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
1093 handle_ops[type]->own_handle (handle);
1097 gboolean _wapi_handle_ops_isowned (gpointer handle)
1099 guint32 idx, segment;
1100 WapiHandleType type;
1102 _wapi_handle_segment (handle, &segment, &idx);
1104 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1106 if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
1107 return(handle_ops[type]->is_owned (handle));
1115 * @handle: The handle to release
1117 * Closes and invalidates @handle, releasing any resources it
1118 * consumes. When the last handle to a temporary or non-persistent
1119 * object is closed, that object can be deleted. Closing the same
1120 * handle twice is an error.
1122 * Return value: %TRUE on success, %FALSE otherwise.
1124 gboolean CloseHandle(gpointer handle)
1126 _wapi_handle_unref (handle);
1131 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1137 guint32 count, i, iter=0;
1140 /* Lock all the handles, with backoff */
1142 for(i=0; i<numhandles; i++) {
1143 guint32 idx, segment;
1145 _wapi_handle_segment (handles[i], &segment, &idx);
1148 g_message (G_GNUC_PRETTY_FUNCTION ": attempting to lock %p",
1152 ret=mono_mutex_trylock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1155 struct timespec sleepytime;
1158 g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p", handles[i]);
1162 _wapi_handle_segment (handles[i], &segment, &idx);
1163 mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1166 /* If iter ever reaches 100 the nanosleep will
1167 * return EINVAL immediately, but we have a
1168 * design flaw if that happens.
1172 g_warning (G_GNUC_PRETTY_FUNCTION
1173 ": iteration overflow!");
1177 sleepytime.tv_sec=0;
1178 sleepytime.tv_nsec=10000000 * iter; /* 10ms*iter */
1181 g_message (G_GNUC_PRETTY_FUNCTION
1182 ": Backing off for %d ms", iter*10);
1184 nanosleep (&sleepytime, NULL);
1191 g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
1197 for(i=0; i<numhandles; i++) {
1198 guint32 idx, segment;
1200 _wapi_handle_segment (handles[i], &segment, &idx);
1203 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
1207 if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
1208 (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
1209 (_wapi_handle_get_shared_segment (segment)->handles[idx].signalled==TRUE)) {
1213 g_message (G_GNUC_PRETTY_FUNCTION
1214 ": Handle %p signalled", handles[i]);
1223 g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
1227 if((waitall==TRUE && count==numhandles) ||
1228 (waitall==FALSE && count>0)) {
1235 g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
1243 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1247 for(i=0; i<numhandles; i++) {
1248 guint32 idx, segment;
1250 _wapi_handle_segment (handles[i], &segment, &idx);
1253 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p",
1257 mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1261 /* Process-shared handles (currently only process and thread handles
1262 * are allowed, and they only work because once signalled they can't
1263 * become unsignalled) are waited for by one process and signalled by
1264 * another. Without process-shared conditions, the waiting process
1265 * will block forever. To get around this, the four handle waiting
1266 * functions use a short timeout when _POSIX_THREAD_PROCESS_SHARED is
1267 * not available. They also return "success" if the fake timeout
1268 * expired, and let the caller check signal status.
1270 int _wapi_handle_wait_signal (void)
1272 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1273 return(mono_cond_wait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1274 &_wapi_handle_get_shared_segment (0)->signal_mutex));
1276 struct timespec fake_timeout;
1279 _wapi_calc_timeout (&fake_timeout, 100);
1281 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1282 &_wapi_handle_get_private_segment (0)->signal_mutex,
1284 if(ret==ETIMEDOUT) {
1289 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1292 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1294 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1295 return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1296 &_wapi_handle_get_shared_segment (0)->signal_mutex,
1299 struct timespec fake_timeout;
1302 _wapi_calc_timeout (&fake_timeout, 100);
1304 if((fake_timeout.tv_sec>timeout->tv_sec) ||
1305 (fake_timeout.tv_sec==timeout->tv_sec &&
1306 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1307 /* Real timeout is less than 100ms time */
1308 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1309 &_wapi_handle_get_private_segment (0)->signal_mutex,
1312 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1313 &_wapi_handle_get_private_segment (0)->signal_mutex,
1315 if(ret==ETIMEDOUT) {
1321 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1324 int _wapi_handle_wait_signal_handle (gpointer handle)
1326 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1327 guint32 idx, segment;
1329 _wapi_handle_segment (handle, &segment, &idx);
1331 return(mono_cond_wait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1332 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex));
1334 guint32 idx, segment;
1335 struct timespec fake_timeout;
1338 _wapi_handle_segment (handle, &segment, &idx);
1339 _wapi_calc_timeout (&fake_timeout, 100);
1341 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1342 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1344 if(ret==ETIMEDOUT) {
1349 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1352 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1353 struct timespec *timeout)
1355 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1356 guint32 idx, segment;
1358 _wapi_handle_segment (handle, &segment, &idx);
1360 return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1361 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1364 guint32 idx, segment;
1365 struct timespec fake_timeout;
1368 _wapi_handle_segment (handle, &segment, &idx);
1369 _wapi_calc_timeout (&fake_timeout, 100);
1371 if((fake_timeout.tv_sec>timeout->tv_sec) ||
1372 (fake_timeout.tv_sec==timeout->tv_sec &&
1373 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1374 /* Real timeout is less than 100ms time */
1375 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1376 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1379 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1380 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1382 if(ret==ETIMEDOUT) {
1388 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1391 gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, guint32 dir,
1392 gboolean inherit, guint32 flags,
1393 gpointer stdin_handle,
1394 gpointer stdout_handle,
1395 gpointer stderr_handle,
1396 gpointer *process_handle,
1397 gpointer *thread_handle, guint32 *pid,
1400 WapiHandleRequest fork_proc={0};
1401 WapiHandleResponse fork_proc_resp={0};
1402 int in_fd, out_fd, err_fd;
1408 fork_proc.type=WapiHandleRequestType_ProcessFork;
1409 fork_proc.u.process_fork.cmd=cmd;
1410 fork_proc.u.process_fork.env=env;
1411 fork_proc.u.process_fork.dir=dir;
1412 fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
1413 fork_proc.u.process_fork.stdout_handle=GPOINTER_TO_UINT (stdout_handle);
1414 fork_proc.u.process_fork.stderr_handle=GPOINTER_TO_UINT (stderr_handle);
1415 fork_proc.u.process_fork.inherit=inherit;
1416 fork_proc.u.process_fork.flags=flags;
1418 in_fd=_wapi_file_handle_to_fd (stdin_handle);
1419 out_fd=_wapi_file_handle_to_fd (stdout_handle);
1420 err_fd=_wapi_file_handle_to_fd (stderr_handle);
1422 if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
1423 /* We were given duff handles */
1424 /* FIXME: error code */
1428 _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
1429 &fork_proc_resp, in_fd,
1431 if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
1432 *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
1433 *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
1434 *pid=fork_proc_resp.u.process_fork.pid;
1435 *tid=fork_proc_resp.u.process_fork.tid;
1437 /* If there was an internal error, the handles will be
1438 * 0. If there was an error forking or execing, the
1439 * handles will have values, and process_handle's
1440 * exec_errno will be set, and the handle will be
1441 * signalled immediately.
1443 if(process_handle==0 || thread_handle==0) {
1449 g_warning (G_GNUC_PRETTY_FUNCTION
1450 ": bogus daemon response, type %d",
1451 fork_proc_resp.type);
1452 g_assert_not_reached ();