2 * handles.c: Generic and internal operations on handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
16 #include <sys/types.h>
17 #include <sys/socket.h>
21 #include <mono/os/gc_wrapper.h>
23 #include <mono/io-layer/wapi.h>
24 #include <mono/io-layer/wapi-private.h>
25 #include <mono/io-layer/handles-private.h>
26 #include <mono/io-layer/mono-mutex.h>
27 #include <mono/io-layer/shared.h>
28 #include <mono/io-layer/misc-private.h>
29 #include <mono/io-layer/daemon-messages.h>
32 #undef HEAVY_DEBUG /* This will print handle counts on every handle created */
34 /* Shared threads don't seem to work yet */
35 #undef _POSIX_THREAD_PROCESS_SHARED
38 * This flag _MUST_ remain set to FALSE in the daemon process. When
39 * we exec()d a standalone daemon, that happened because shared_init()
40 * didnt get called in the daemon process. Now we just fork() without
41 * exec(), we need to ensure that the fork() happens when shared is
44 * This is further complicated by the second attempt to start the
45 * daemon if the connect() fails.
47 static gboolean shared=FALSE;
49 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
50 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
64 static int daemon_sock;
66 static pthread_mutexattr_t mutex_shared_attr;
67 static pthread_condattr_t cond_shared_attr;
69 struct _WapiHandleShared_list **_wapi_shared_data=NULL;
70 struct _WapiHandleScratch *_wapi_shared_scratch=NULL;
71 struct _WapiHandlePrivate_list **_wapi_private_data=NULL;
72 pthread_mutex_t _wapi_shared_mutex=PTHREAD_MUTEX_INITIALIZER;
74 /* This holds the length of the _wapi_shared_data and
75 * _wapi_private_data arrays, so we know if a segment is off the end
76 * of the array, requiring a realloc
78 guint32 _wapi_shm_mapped_segments;
80 static void shared_init (void)
82 struct sockaddr_un shared_socket_address;
83 gboolean tried_once=FALSE;
86 _wapi_shared_data=g_new0 (struct _WapiHandleShared_list *, 1);
87 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list *, 1);
91 #ifndef DISABLE_SHARED_HANDLES
92 if(getenv ("MONO_DISABLE_SHM"))
96 #ifndef DISABLE_SHARED_HANDLES
98 /* Ensure that shared==FALSE while _wapi_shm_attach()
103 shared=_wapi_shm_attach (&_wapi_shared_data[0],
104 &_wapi_shared_scratch);
106 g_warning ("Failed to attach shared memory! "
107 "Falling back to non-shared handles");
109 #endif /* DISABLE_SHARED_HANDLES */
114 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
115 shared_socket_address.sun_family=AF_UNIX;
116 memcpy (shared_socket_address.sun_path,
117 _wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH);
118 ret=connect (daemon_sock,
119 (struct sockaddr *)&shared_socket_address,
120 sizeof(struct sockaddr_un));
122 if(tried_once==TRUE) {
123 g_warning (G_GNUC_PRETTY_FUNCTION
124 ": connect to daemon failed: %s",
126 /* Fall back to private handles */
129 /* It's possible that the daemon
130 * crashed without destroying the
131 * shared memory segment (thus fooling
132 * subsequent processes into thinking
133 * the daemon is still active).
135 * Destroy the shared memory segment
136 * and try once more. This won't
137 * break running apps, but no new apps
138 * will be able to see the current
139 * shared memory segment.
142 _wapi_shm_destroy ();
151 g_message (G_GNUC_PRETTY_FUNCTION
152 ": Using process-private handles");
154 _wapi_shared_data[0]=g_new0 (struct _WapiHandleShared_list, 1);
155 _wapi_shared_data[0]->num_segments=1;
157 _wapi_shared_scratch=g_new0 (struct _WapiHandleScratch, 1);
159 _wapi_private_data[0]=g_new0 (struct _WapiHandlePrivate_list, 1);
160 _wapi_shm_mapped_segments=1;
162 pthread_mutexattr_init (&mutex_shared_attr);
163 pthread_condattr_init (&cond_shared_attr);
165 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
166 pthread_mutexattr_setpshared (&mutex_shared_attr,
167 PTHREAD_PROCESS_SHARED);
168 pthread_condattr_setpshared (&cond_shared_attr,
169 PTHREAD_PROCESS_SHARED);
171 pthread_cond_init(&_wapi_private_data[0]->signal_cond, NULL);
172 mono_mutex_init(&_wapi_private_data[0]->signal_mutex, NULL);
178 print_handle_count (gint mask)
180 gint *count, num_handles;
182 static const gchar *names [] = {"WAPI_HANDLE_UNUSED",
184 "WAPI_HANDLE_CONSOLE",
185 "WAPI_HANDLE_THREAD",
189 "WAPI_HANDLE_SOCKET",
191 "WAPI_HANDLE_PROCESS",
196 num_handles=_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT;
197 count=g_new0 (gint, num_handles);
199 for (i = 1; i < num_handles; i++) {
200 struct _WapiHandleShared *shared;
201 guint32 segment, idx;
203 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
204 _wapi_handle_ensure_mapped (segment);
206 shared = &_wapi_handle_get_shared_segment (segment)->handles[idx];
207 count [shared->type]++;
210 for (i = 0; i < num_handles; i++)
211 if ((i & mask) == i) /* Always prints the UNUSED count */
212 g_print ("%s: %d\n", names [i], count [i]);
216 #endif /* HEAVY_DEBUG */
219 * _wapi_handle_new_internal:
220 * @type: Init handle to this type
222 * Search for a free handle and initialize it. Return the handle on
223 * success and 0 on failure.
225 guint32 _wapi_handle_new_internal (WapiHandleType type)
227 guint32 segment, idx;
229 static guint32 last=1;
231 /* A linear scan should be fast enough. Start from the last
232 * allocation, assuming that handles are allocated more often
233 * than they're freed. Leave 0 (NULL) as a guard
236 print_handle_count (0xFFF);
239 _wapi_handle_segment (GUINT_TO_POINTER (last), &segment, &idx);
240 for(i=segment; i<_wapi_handle_get_shared_segment (0)->num_segments;
246 for(j=idx; j<_WAPI_HANDLES_PER_SEGMENT; j++) {
247 struct _WapiHandleShared *shared;
249 /* Make sure we dont try and assign handle 0 */
254 shared=&_wapi_handle_get_shared_segment (i)->handles[j];
256 if(shared->type==WAPI_HANDLE_UNUSED) {
257 last=(_wapi_handle_index (i, j)+1) % (_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT);
259 shared->signalled=FALSE;
260 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
261 mono_mutex_init (&shared->signal_mutex, &mutex_shared_attr);
262 pthread_cond_init (&shared->signal_cond, &cond_shared_attr);
264 pthread_cond_init(&shared->signal_cond, NULL);
265 mono_mutex_init(&shared->signal_mutex, NULL);
268 return(_wapi_handle_index (i, j));
274 /* Try again from the beginning */
279 /* Will need a new segment. The caller will sort it out */
284 gpointer _wapi_handle_new (WapiHandleType type)
286 static mono_once_t shared_init_once = MONO_ONCE_INIT;
287 static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
288 guint32 handle_idx, idx, segment;
290 WapiHandleRequest new={0};
291 WapiHandleResponse new_resp={0};
293 gboolean tried_collect=FALSE;
296 mono_once (&shared_init_once, shared_init);
300 new.type=WapiHandleRequestType_New;
303 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
305 if (new_resp.type==WapiHandleResponseType_New) {
306 handle_idx=new_resp.u.new.handle;
308 g_warning (G_GNUC_PRETTY_FUNCTION
309 ": bogus daemon response, type %d",
311 g_assert_not_reached ();
314 pthread_mutex_lock (&scan_mutex);
315 handle_idx=_wapi_handle_new_internal (type);
317 /* Try and get a new segment, and have another go */
318 segment=_wapi_handle_get_shared_segment (0)->num_segments;
319 _wapi_handle_ensure_mapped (segment);
321 if(_wapi_handle_get_shared_segment (segment)!=NULL) {
322 /* Got a new segment */
323 _wapi_handle_get_shared_segment (0)->num_segments++;
324 handle_idx=_wapi_handle_new_internal (type);
326 /* Map failed. Just return 0 meaning
332 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
333 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
334 pthread_mutex_unlock (&scan_mutex);
338 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
341 /* See if we can reclaim some handles by forcing a GC
344 if(tried_collect==FALSE) {
345 g_warning (G_GNUC_PRETTY_FUNCTION
346 ": Seeing if GC collection helps...");
347 GC_gcollect (); /* FIXME: we should wait for finalizers to be called */
351 g_warning (G_GNUC_PRETTY_FUNCTION
352 ": didn't help, returning error");
356 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
359 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
360 _wapi_handle_ensure_mapped (segment);
362 if(_wapi_private_data!=NULL) {
363 _wapi_handle_get_private_segment (segment)->handles[idx].type=type;
366 #if !defined(_POSIX_THREAD_PROCESS_SHARED) || _POSIX_THREAD_PROCESS_SHARED == -1
367 mono_mutex_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex, &mutex_shared_attr);
368 pthread_cond_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond, &cond_shared_attr);
370 handle=GUINT_TO_POINTER (handle_idx);
373 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
379 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
380 gpointer *shared, gpointer *private)
382 struct _WapiHandleShared *shared_handle_data;
383 struct _WapiHandlePrivate *private_handle_data;
387 _wapi_handle_segment (handle, &segment, &idx);
388 _wapi_handle_ensure_mapped (segment);
390 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
393 *shared=&shared_handle_data->u;
397 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
399 *private=&private_handle_data->u;
402 if(shared_handle_data->type!=type) {
403 /* If shared type is UNUSED, see if the private type
404 * matches what we are looking for - this can happen
405 * when the handle is being destroyed and the
406 * close_private function is looking up the private
409 if(shared_handle_data->type==WAPI_HANDLE_UNUSED &&
410 (private!=NULL && private_handle_data->type==type)) {
420 gpointer _wapi_search_handle (WapiHandleType type,
421 gboolean (*check)(gpointer test, gpointer user),
423 gpointer *shared, gpointer *private)
425 struct _WapiHandleShared *shared_handle_data;
426 struct _WapiHandlePrivate *private_handle_data;
427 guint32 i, segment, idx;
429 for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
430 struct _WapiHandleShared *shared;
432 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
433 _wapi_handle_ensure_mapped (segment);
435 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
437 if(shared->type==type) {
438 if(check (GUINT_TO_POINTER (i), user_data)==TRUE) {
444 if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
445 return(GUINT_TO_POINTER (0));
449 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
451 *shared=&shared_handle_data->u;
455 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
457 *private=&private_handle_data->u;
460 return(GUINT_TO_POINTER (i));
463 gpointer _wapi_search_handle_namespace (WapiHandleType type,
464 gchar *utf8_name, gpointer *shared,
467 struct _WapiHandleShared *shared_handle_data;
468 struct _WapiHandlePrivate *private_handle_data;
469 guint32 i, segment, idx;
472 g_message (G_GNUC_PRETTY_FUNCTION
473 ": Lookup for handle named [%s] type %d", utf8_name, type);
476 for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
477 struct _WapiHandleShared *shared;
479 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
480 _wapi_handle_ensure_mapped (segment);
482 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
484 /* Check mutex, event, semaphore, timer, job and file-mapping
485 * object names. So far only mutex is implemented.
487 if(_WAPI_SHARED_NAMESPACE (shared->type)) {
489 WapiSharedNamespace *sharedns;
492 g_message (G_GNUC_PRETTY_FUNCTION ": found a shared namespace handle at 0x%x (type %d)", i, shared->type);
495 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
496 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
500 lookup_name=_wapi_handle_scratch_lookup (
504 g_message (G_GNUC_PRETTY_FUNCTION
505 ": handle 0x%x is unnamed", i);
510 if(lookup_name==NULL) {
512 g_message (G_GNUC_PRETTY_FUNCTION
513 ": couldn't find handle 0x%x name",
520 g_message (G_GNUC_PRETTY_FUNCTION ": name is [%s]",
524 if(strcmp (lookup_name, utf8_name)==0) {
525 if(shared->type!=type) {
526 /* Its the wrong type, so fail now */
528 g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name but is wrong type: %d", i, shared->type);
530 return(_WAPI_HANDLE_INVALID);
532 /* fall through so we can fill
536 g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name and type", i);
544 if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
545 return(GUINT_TO_POINTER (0));
549 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
551 *shared=&shared_handle_data->u;
555 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
557 *private=&private_handle_data->u;
560 return(GUINT_TO_POINTER (i));
563 void _wapi_handle_ref (gpointer handle)
566 WapiHandleRequest req={0};
567 WapiHandleResponse resp={0};
569 req.type=WapiHandleRequestType_Open;
570 req.u.open.handle=GPOINTER_TO_UINT (handle);
572 _wapi_daemon_request_response (daemon_sock, &req, &resp);
573 if(resp.type!=WapiHandleResponseType_Open) {
574 g_warning (G_GNUC_PRETTY_FUNCTION
575 ": bogus daemon response, type %d",
577 g_assert_not_reached ();
580 guint32 idx, segment;
582 _wapi_handle_segment (handle, &segment, &idx);
584 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
587 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
589 _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
594 void _wapi_handle_unref (gpointer handle)
596 guint32 idx, segment;
599 _wapi_handle_segment (handle, &segment, &idx);
602 WapiHandleRequest req={0};
603 WapiHandleResponse resp={0};
605 req.type=WapiHandleRequestType_Close;
606 req.u.close.handle=GPOINTER_TO_UINT (handle);
608 _wapi_daemon_request_response (daemon_sock, &req, &resp);
609 if(resp.type!=WapiHandleResponseType_Close) {
610 g_warning (G_GNUC_PRETTY_FUNCTION
611 ": bogus daemon response, type %d",
613 g_assert_not_reached ();
615 destroy=resp.u.close.destroy;
618 _wapi_handle_get_shared_segment (segment)->handles[idx].ref--;
621 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", handle, _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
624 /* Possible race condition here if another thread refs
625 * the handle between here and setting the type to
626 * UNUSED. I could lock a mutex, but I'm not sure
627 * that allowing a handle reference to reach 0 isn't
628 * an application bug anyway.
630 destroy=(_wapi_handle_get_shared_segment (segment)->handles[idx].ref==0);
635 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
640 _wapi_handle_ops_close_shared (handle);
642 mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
643 pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
644 memset (&_wapi_handle_get_shared_segment (segment)->handles[idx].u, '\0', sizeof(_wapi_handle_get_shared_segment (segment)->handles[idx].u));
648 mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
649 pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
652 _wapi_handle_ops_close_private (handle);
653 _wapi_handle_get_shared_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
657 #define HDRSIZE sizeof(struct _WapiScratchHeader)
659 static pthread_mutex_t _wapi_scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
661 /* _wapi_scratch_mutex must be held when this function is called in
662 * the non-shared case
664 static void _wapi_handle_scratch_expand (void)
666 guint32 old_len, new_len;
668 old_len=sizeof(struct _WapiHandleScratch) +
669 _wapi_shared_scratch->data_len;
670 new_len=old_len+_WAPI_SHM_SCRATCH_SIZE;
672 if(_wapi_shared_scratch->is_shared==TRUE) {
673 /* expand via mmap() */
674 _wapi_shared_scratch=_wapi_shm_file_expand (_wapi_shared_scratch, WAPI_SHM_SCRATCH, 0, old_len, new_len);
676 _wapi_shared_scratch=_wapi_g_renew0 (_wapi_shared_scratch, old_len, new_len);
678 _wapi_shared_scratch->data_len+=_WAPI_SHM_SCRATCH_SIZE;
681 /* _wapi_scratch_mutex must be held when this function is called in
682 * the non-shared case
684 static guint32 _wapi_handle_scratch_locate_space (guint32 bytes)
686 guint32 idx=0, last_idx=0;
687 struct _WapiScratchHeader *hdr, *last_hdr;
688 gboolean last_was_free=FALSE;
689 guchar *storage=_wapi_shared_scratch->scratch_data;
692 g_message (G_GNUC_PRETTY_FUNCTION
693 ": looking for %d bytes of scratch space (%d bytes total)",
694 bytes, _wapi_shared_scratch->data_len);
697 while(idx< _wapi_shared_scratch->data_len) {
698 hdr=(struct _WapiScratchHeader *)&storage[idx];
700 /* Do a simple first-fit allocation, coalescing
701 * adjacent free blocks as we progress through the
704 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
705 hdr->length >= bytes + HDRSIZE) {
707 guint32 old_length=hdr->length;
709 g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
712 hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
716 /* Put a new header in at the end of the used
719 hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
720 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
721 hdr->length = old_length-bytes-HDRSIZE;
724 g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
728 * It was memset(0..) when free/made so no need to do it here
732 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
733 last_was_free == FALSE) {
735 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
738 /* save this point in case we can coalesce it with
739 * the next block, if that is free.
744 idx+=(hdr->length+HDRSIZE);
745 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
746 last_was_free == TRUE) {
748 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
751 /* This block and the previous are both free,
754 last_hdr->length += (hdr->length + HDRSIZE);
756 /* If the new block is now big enough, use it
757 * (next time round the loop)
759 if(last_hdr->length >= bytes + HDRSIZE) {
762 /* leave the last free info as it is,
763 * in case the next block is also free
764 * and can be coalesced too
766 idx=last_idx+last_hdr->length+HDRSIZE;
770 g_message (G_GNUC_PRETTY_FUNCTION
771 ": found used block at %d, length %d", idx,
775 /* must be used, try next chunk */
776 idx+=(hdr->length+HDRSIZE);
778 /* Don't let the coalescing blow away this block */
781 /* But remember where the last block started */
786 /* Not enough free space. last_idx points to the last block.
787 * If it's free, just tack on more space and update the
788 * length. If it's allocated, it must have fit right into the
789 * available space, so add more space and add a new header
792 _wapi_handle_scratch_expand ();
793 storage=_wapi_shared_scratch->scratch_data;
795 hdr=(struct _WapiScratchHeader *)&storage[last_idx];
796 if(hdr->flags & WAPI_SHM_SCRATCH_FREE) {
797 hdr->length+=_WAPI_SHM_SCRATCH_SIZE;
799 idx=(hdr->length+HDRSIZE);
800 hdr=(struct _WapiScratchHeader *)&storage[idx];
801 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
802 hdr->length = _WAPI_SHM_SCRATCH_SIZE-HDRSIZE;
805 /* The caller will try again */
810 * _wapi_handle_scratch_store_internal:
811 * @bytes: Allocate no. bytes
813 * Like malloc(3) except its for the shared memory segment's scratch
814 * part. Memory block returned is zeroed out.
816 guint32 _wapi_handle_scratch_store_internal (guint32 bytes, gboolean *remap)
820 struct _WapiScratchHeader *hdr;
823 g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
828 if(_wapi_shared_scratch->data_len==0) {
829 /* Need to expand the data array for the first use */
831 g_message (G_GNUC_PRETTY_FUNCTION
832 ": setting up scratch space");
835 _wapi_handle_scratch_expand ();
839 storage=_wapi_shared_scratch->scratch_data;
840 hdr=(struct _WapiScratchHeader *)&storage[0];
841 if(hdr->flags==0 && hdr->length==0) {
842 /* Need to initialise scratch data */
843 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
844 hdr->length = _wapi_shared_scratch->data_len - HDRSIZE;
847 idx=_wapi_handle_scratch_locate_space (bytes);
849 /* Some more space will have been allocated, so try again */
851 g_message (G_GNUC_PRETTY_FUNCTION ": trying again");
854 idx=_wapi_handle_scratch_locate_space (bytes);
861 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
863 guint32 idx, store_bytes;
867 g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
870 /* No point storing no data */
875 /* Align bytes to 32 bits (needed for sparc at least) */
876 store_bytes = (((bytes) + 3) & (~3));
878 pthread_mutex_lock (&_wapi_scratch_mutex);
881 WapiHandleRequest scratch={0};
882 WapiHandleResponse scratch_resp={0};
883 guint32 old_len=sizeof(struct _WapiHandleScratch) +
884 _wapi_shared_scratch->data_len;
886 scratch.type=WapiHandleRequestType_Scratch;
887 scratch.u.scratch.length=store_bytes;
889 _wapi_daemon_request_response (daemon_sock, &scratch,
892 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
893 idx=scratch_resp.u.scratch.idx;
894 remap=scratch_resp.u.scratch.remap;
896 g_warning (G_GNUC_PRETTY_FUNCTION
897 ": bogus daemon response, type %d",
899 g_assert_not_reached ();
903 munmap (_wapi_shared_scratch, old_len);
904 _wapi_shared_scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, NULL, NULL);
907 idx=_wapi_handle_scratch_store_internal (store_bytes, &remap);
909 /* Failed to allocate space */
910 pthread_mutex_unlock (&_wapi_scratch_mutex);
916 g_message (G_GNUC_PRETTY_FUNCTION
917 ": stored [%s] at %d (len %d, aligned len %d)",
918 (char *)data, idx, bytes, store_bytes);
921 memcpy (&_wapi_shared_scratch->scratch_data[idx], data, bytes);
923 pthread_mutex_unlock (&_wapi_scratch_mutex);
928 guint32 _wapi_handle_scratch_store_string_array (gchar **data)
930 guint32 *stored_strings, count=0, i, idx;
933 /* No point storing no data */
939 while(*strings!=NULL) {
945 g_message (G_GNUC_PRETTY_FUNCTION ": %d strings to store", count);
952 /* stored_strings[0] is the count */
953 stored_strings=g_new0 (guint32, count+1);
954 stored_strings[0]=count;
957 for(i=0; i<count; i++) {
958 stored_strings[i+1]=_wapi_handle_scratch_store (strings[i], strlen (strings[i]));
961 idx=_wapi_handle_scratch_store (stored_strings,
962 sizeof(guint32)*(count+1));
967 gpointer _wapi_handle_scratch_lookup (guint32 idx)
969 struct _WapiScratchHeader *hdr;
973 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
977 pthread_mutex_lock (&_wapi_scratch_mutex);
979 storage=_wapi_shared_scratch->scratch_data;
981 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
982 ret=g_malloc0 (hdr->length+1);
983 memcpy (ret, &storage[idx], hdr->length);
985 pthread_mutex_unlock (&_wapi_scratch_mutex);
990 gchar **_wapi_handle_scratch_lookup_string_array (guint32 idx)
993 guint32 *stored_strings;
996 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1000 stored_strings=_wapi_handle_scratch_lookup (idx);
1001 if(stored_strings==NULL) {
1005 /* stored_strings[0] is the number of strings, the index of
1006 * each string follows
1008 count=stored_strings[0];
1011 g_message (G_GNUC_PRETTY_FUNCTION
1012 ": looking up an array of %d strings", count);
1015 /* NULL-terminate the array */
1016 strings=g_new0 (gchar *, count+1);
1018 for(i=0; i<count; i++) {
1019 strings[i]=_wapi_handle_scratch_lookup (stored_strings[i+1]);
1022 g_message (G_GNUC_PRETTY_FUNCTION ": string %d is [%s]", i,
1027 g_free (stored_strings);
1033 * _wapi_handle_scratch_delete_internal:
1034 * @idx: Index to free block
1036 * Like free(3) except its for the shared memory segment's scratch
1039 void _wapi_handle_scratch_delete_internal (guint32 idx)
1041 struct _WapiScratchHeader *hdr;
1044 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1048 pthread_mutex_lock (&_wapi_scratch_mutex);
1050 storage=_wapi_shared_scratch->scratch_data;
1052 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
1053 memset (&storage[idx], '\0', hdr->length);
1054 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
1056 /* We could coalesce forwards here if the next block is also
1057 * free, but the _store() function will do that anyway.
1060 pthread_mutex_unlock (&_wapi_scratch_mutex);
1063 void _wapi_handle_scratch_delete (guint32 idx)
1066 WapiHandleRequest scratch_free={0};
1067 WapiHandleResponse scratch_free_resp={0};
1069 scratch_free.type=WapiHandleRequestType_ScratchFree;
1070 scratch_free.u.scratch_free.idx=idx;
1072 _wapi_daemon_request_response (daemon_sock, &scratch_free,
1073 &scratch_free_resp);
1075 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
1076 g_warning (G_GNUC_PRETTY_FUNCTION
1077 ": bogus daemon response, type %d",
1078 scratch_free_resp.type);
1079 g_assert_not_reached ();
1082 _wapi_handle_scratch_delete_internal (idx);
1086 void _wapi_handle_scratch_delete_string_array (guint32 idx)
1088 guint32 *stored_strings;
1091 stored_strings=_wapi_handle_scratch_lookup (idx);
1092 if(stored_strings==NULL) {
1096 /* stored_strings[0] is the number of strings, the index of
1097 * each string follows
1099 count=stored_strings[0];
1102 g_message (G_GNUC_PRETTY_FUNCTION ": deleting an array of %d strings",
1106 for(i=1; i<count; i++) {
1107 _wapi_handle_scratch_delete (stored_strings[i]);
1110 _wapi_handle_scratch_delete (idx);
1112 g_free (stored_strings);
1115 void _wapi_handle_register_capabilities (WapiHandleType type,
1116 WapiHandleCapability caps)
1118 handle_caps[type]=caps;
1121 gboolean _wapi_handle_test_capabilities (gpointer handle,
1122 WapiHandleCapability caps)
1124 guint32 idx, segment;
1125 WapiHandleType type;
1127 _wapi_handle_segment (handle, &segment, &idx);
1129 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1132 g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
1133 handle_caps[type], caps, handle_caps[type] & caps);
1136 return((handle_caps[type] & caps)!=0);
1139 void _wapi_handle_ops_close_shared (gpointer handle)
1141 guint32 idx, segment;
1142 WapiHandleType type;
1144 _wapi_handle_segment (handle, &segment, &idx);
1146 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1148 if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
1149 handle_ops[type]->close_shared (handle);
1153 void _wapi_handle_ops_close_private (gpointer handle)
1155 guint32 idx, segment;
1156 WapiHandleType type;
1158 _wapi_handle_segment (handle, &segment, &idx);
1160 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1162 /* When a handle in the process of being destroyed the shared
1163 * type has already been set to UNUSED
1165 if(type==WAPI_HANDLE_UNUSED && _wapi_private_data!=NULL) {
1166 type=_wapi_handle_get_private_segment (segment)->handles[idx].type;
1169 if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
1170 handle_ops[type]->close_private (handle);
1174 void _wapi_handle_ops_signal (gpointer handle)
1176 guint32 idx, segment;
1177 WapiHandleType type;
1179 _wapi_handle_segment (handle, &segment, &idx);
1181 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1183 if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
1184 handle_ops[type]->signal (handle);
1188 void _wapi_handle_ops_own (gpointer handle)
1190 guint32 idx, segment;
1191 WapiHandleType type;
1193 _wapi_handle_segment (handle, &segment, &idx);
1195 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1197 if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
1198 handle_ops[type]->own_handle (handle);
1202 gboolean _wapi_handle_ops_isowned (gpointer handle)
1204 guint32 idx, segment;
1205 WapiHandleType type;
1207 _wapi_handle_segment (handle, &segment, &idx);
1209 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1211 if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
1212 return(handle_ops[type]->is_owned (handle));
1220 * @handle: The handle to release
1222 * Closes and invalidates @handle, releasing any resources it
1223 * consumes. When the last handle to a temporary or non-persistent
1224 * object is closed, that object can be deleted. Closing the same
1225 * handle twice is an error.
1227 * Return value: %TRUE on success, %FALSE otherwise.
1229 gboolean CloseHandle(gpointer handle)
1231 _wapi_handle_unref (handle);
1236 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1242 guint32 count, i, iter=0;
1245 /* Lock all the handles, with backoff */
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 ": attempting to lock %p",
1257 ret=mono_mutex_trylock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1260 struct timespec sleepytime;
1263 g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p", handles[i]);
1267 _wapi_handle_segment (handles[i], &segment, &idx);
1268 mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1271 /* If iter ever reaches 100 the nanosleep will
1272 * return EINVAL immediately, but we have a
1273 * design flaw if that happens.
1277 g_warning (G_GNUC_PRETTY_FUNCTION
1278 ": iteration overflow!");
1282 sleepytime.tv_sec=0;
1283 sleepytime.tv_nsec=10000000 * iter; /* 10ms*iter */
1286 g_message (G_GNUC_PRETTY_FUNCTION
1287 ": Backing off for %d ms", iter*10);
1289 nanosleep (&sleepytime, NULL);
1296 g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
1302 for(i=0; i<numhandles; i++) {
1303 guint32 idx, segment;
1305 _wapi_handle_segment (handles[i], &segment, &idx);
1308 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
1312 if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
1313 (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
1314 (_wapi_handle_get_shared_segment (segment)->handles[idx].signalled==TRUE)) {
1318 g_message (G_GNUC_PRETTY_FUNCTION
1319 ": Handle %p signalled", handles[i]);
1328 g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
1332 if((waitall==TRUE && count==numhandles) ||
1333 (waitall==FALSE && count>0)) {
1340 g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
1348 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1352 for(i=0; i<numhandles; i++) {
1353 guint32 idx, segment;
1355 _wapi_handle_segment (handles[i], &segment, &idx);
1358 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p",
1362 mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1366 /* Process-shared handles (currently only process and thread handles
1367 * are allowed, and they only work because once signalled they can't
1368 * become unsignalled) are waited for by one process and signalled by
1369 * another. Without process-shared conditions, the waiting process
1370 * will block forever. To get around this, the four handle waiting
1371 * functions use a short timeout when _POSIX_THREAD_PROCESS_SHARED is
1372 * not available. They also return "success" if the fake timeout
1373 * expired, and let the caller check signal status.
1375 int _wapi_handle_wait_signal (void)
1377 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1378 return(mono_cond_wait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1379 &_wapi_handle_get_shared_segment (0)->signal_mutex));
1381 struct timespec fake_timeout;
1384 _wapi_calc_timeout (&fake_timeout, 100);
1386 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1387 &_wapi_handle_get_private_segment (0)->signal_mutex,
1389 if(ret==ETIMEDOUT) {
1394 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1397 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1399 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1400 return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1401 &_wapi_handle_get_shared_segment (0)->signal_mutex,
1404 struct timespec fake_timeout;
1407 _wapi_calc_timeout (&fake_timeout, 100);
1409 if((fake_timeout.tv_sec>timeout->tv_sec) ||
1410 (fake_timeout.tv_sec==timeout->tv_sec &&
1411 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1412 /* Real timeout is less than 100ms time */
1413 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1414 &_wapi_handle_get_private_segment (0)->signal_mutex,
1417 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1418 &_wapi_handle_get_private_segment (0)->signal_mutex,
1420 if(ret==ETIMEDOUT) {
1426 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1429 int _wapi_handle_wait_signal_handle (gpointer handle)
1431 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1432 guint32 idx, segment;
1434 _wapi_handle_segment (handle, &segment, &idx);
1436 return(mono_cond_wait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1437 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex));
1439 guint32 idx, segment;
1440 struct timespec fake_timeout;
1443 _wapi_handle_segment (handle, &segment, &idx);
1444 _wapi_calc_timeout (&fake_timeout, 100);
1446 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1447 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1449 if(ret==ETIMEDOUT) {
1454 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1457 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1458 struct timespec *timeout)
1460 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1461 guint32 idx, segment;
1463 _wapi_handle_segment (handle, &segment, &idx);
1465 return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1466 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1469 guint32 idx, segment;
1470 struct timespec fake_timeout;
1473 _wapi_handle_segment (handle, &segment, &idx);
1474 _wapi_calc_timeout (&fake_timeout, 100);
1476 if((fake_timeout.tv_sec>timeout->tv_sec) ||
1477 (fake_timeout.tv_sec==timeout->tv_sec &&
1478 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1479 /* Real timeout is less than 100ms time */
1480 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1481 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1484 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1485 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1487 if(ret==ETIMEDOUT) {
1493 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1496 gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, guint32 dir,
1497 gboolean inherit, guint32 flags,
1498 gpointer stdin_handle,
1499 gpointer stdout_handle,
1500 gpointer stderr_handle,
1501 gpointer *process_handle,
1502 gpointer *thread_handle, guint32 *pid,
1505 WapiHandleRequest fork_proc={0};
1506 WapiHandleResponse fork_proc_resp={0};
1507 int in_fd, out_fd, err_fd;
1513 fork_proc.type=WapiHandleRequestType_ProcessFork;
1514 fork_proc.u.process_fork.cmd=cmd;
1515 fork_proc.u.process_fork.env=env;
1516 fork_proc.u.process_fork.dir=dir;
1517 fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
1518 fork_proc.u.process_fork.stdout_handle=GPOINTER_TO_UINT (stdout_handle);
1519 fork_proc.u.process_fork.stderr_handle=GPOINTER_TO_UINT (stderr_handle);
1520 fork_proc.u.process_fork.inherit=inherit;
1521 fork_proc.u.process_fork.flags=flags;
1523 in_fd=_wapi_file_handle_to_fd (stdin_handle);
1524 out_fd=_wapi_file_handle_to_fd (stdout_handle);
1525 err_fd=_wapi_file_handle_to_fd (stderr_handle);
1527 if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
1528 /* We were given duff handles */
1529 /* FIXME: error code */
1533 _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
1534 &fork_proc_resp, in_fd,
1536 if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
1537 *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
1538 *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
1539 *pid=fork_proc_resp.u.process_fork.pid;
1540 *tid=fork_proc_resp.u.process_fork.tid;
1542 /* If there was an internal error, the handles will be
1543 * 0. If there was an error forking or execing, the
1544 * handles will have values, and process_handle's
1545 * exec_errno will be set, and the handle will be
1546 * signalled immediately.
1548 if(process_handle==0 || thread_handle==0) {
1554 g_warning (G_GNUC_PRETTY_FUNCTION
1555 ": bogus daemon response, type %d",
1556 fork_proc_resp.type);
1557 g_assert_not_reached ();