7 #include <sys/socket.h>
14 #include <mono/io-layer/wapi.h>
15 #include <mono/io-layer/wapi-private.h>
16 #include <mono/io-layer/handles-private.h>
17 #include <mono/io-layer/mono-mutex.h>
18 #include <mono/io-layer/shared.h>
19 #include <mono/io-layer/misc-private.h>
20 #include <mono/io-layer/daemon-messages.h>
24 static pthread_once_t shared_data_once=PTHREAD_ONCE_INIT;
25 static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0};
26 static gboolean shared=FALSE;
27 static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={
40 static int daemon_sock;
42 static pthread_mutexattr_t mutex_shared_attr;
43 static pthread_condattr_t cond_shared_attr;
45 struct _WapiHandleShared_list *_wapi_shared_data=NULL;
46 struct _WapiHandlePrivate_list *_wapi_private_data=NULL;
49 static void shared_init (void)
51 struct sockaddr_un sun;
54 if (getenv ("MONO_ENABLE_SHM"))
57 #ifndef DISABLE_SHARED_HANDLES
58 if(getenv ("MONO_DISABLE_SHM") || disable_shm)
62 g_message (G_GNUC_PRETTY_FUNCTION
63 ": Using process-private handles");
68 g_malloc0 (sizeof(struct _WapiHandleShared_list)+
69 _WAPI_SHM_SCRATCH_SIZE);
70 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1);
71 #ifndef DISABLE_SHARED_HANDLES
75 _wapi_shared_data=_wapi_shm_attach (FALSE);
76 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1);
78 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
79 sun.sun_family=AF_UNIX;
80 memcpy (sun.sun_path, _wapi_shared_data->daemon, 108);
81 ret=connect (daemon_sock, (struct sockaddr *)&sun,
82 sizeof(struct sockaddr_un));
84 g_error (G_GNUC_PRETTY_FUNCTION
85 "connect to daemon failed: %s",
87 g_assert_not_reached ();
89 #endif /* DISABLE_SHARED_HANDLES */
92 pthread_mutexattr_init (&mutex_shared_attr);
93 pthread_condattr_init (&cond_shared_attr);
95 #ifdef _POSIX_THREAD_PROCESS_SHARED
96 pthread_mutexattr_setpshared (&mutex_shared_attr,
97 PTHREAD_PROCESS_SHARED);
98 pthread_condattr_setpshared (&cond_shared_attr,
99 PTHREAD_PROCESS_SHARED);
103 void _wapi_handle_init (void)
105 /* Create our own process and main thread handles (assume this
106 * function is being called from the main thread)
108 /* No need to make these variables visible outside this
109 * function, the handles will be cleaned up at process exit.
111 gpointer process_handle;
112 gpointer thread_handle;
114 process_handle=_wapi_handle_new (WAPI_HANDLE_PROCESS);
116 thread_handle=_wapi_handle_new (WAPI_HANDLE_THREAD);
119 guint32 _wapi_handle_new_internal (WapiHandleType type)
123 /* A linear scan should be fast enough. Start from 1, leaving
124 * 0 (NULL) as a guard
126 for(i=1; i<_WAPI_MAX_HANDLES; i++) {
127 struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i];
129 if(shared->type==WAPI_HANDLE_UNUSED) {
131 shared->signalled=FALSE;
132 mono_mutex_init (&_wapi_shared_data->handles[i].signal_mutex, &mutex_shared_attr);
133 pthread_cond_init (&_wapi_shared_data->handles[i].signal_cond, &cond_shared_attr);
142 gpointer _wapi_handle_new (WapiHandleType type)
144 static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
147 WapiHandleRequest new;
148 WapiHandleResponse new_resp;
150 gboolean tried_collect=FALSE;
153 pthread_once (&shared_data_once, shared_init);
157 new.type=WapiHandleRequestType_New;
160 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
162 if (new_resp.type==WapiHandleResponseType_New) {
163 idx=new_resp.u.new.handle;
165 g_warning (G_GNUC_PRETTY_FUNCTION
166 ": bogus daemon response, type %d",
168 g_assert_not_reached ();
171 pthread_mutex_lock (&scan_mutex);
172 idx=_wapi_handle_new_internal (type);
173 pthread_mutex_unlock (&scan_mutex);
177 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
180 /* See if we can reclaim some handles by forcing a GC
183 if(tried_collect==FALSE) {
184 g_warning (G_GNUC_PRETTY_FUNCTION
185 ": Seeing if GC collection helps...");
190 g_warning (G_GNUC_PRETTY_FUNCTION
191 ": didn't help, returning error");
195 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
198 handle=GUINT_TO_POINTER (idx);
201 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
207 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
208 gpointer *shared, gpointer *private)
210 struct _WapiHandleShared *shared_handle_data;
211 struct _WapiHandlePrivate *private_handle_data;
212 guint32 idx=GPOINTER_TO_UINT (handle);
215 shared_handle_data=&_wapi_shared_data->handles[idx];
216 /* Allow WAPI_HANDLE_UNUSED to mean "dont care which
219 if(shared_handle_data->type!=type &&
220 type != WAPI_HANDLE_UNUSED) {
224 *shared=&shared_handle_data->u;
228 private_handle_data=&_wapi_private_data->handles[idx];
230 *private=&private_handle_data->u;
236 void _wapi_handle_ref (gpointer handle)
238 guint32 idx=GPOINTER_TO_UINT (handle);
241 WapiHandleRequest req;
242 WapiHandleResponse resp;
244 req.type=WapiHandleRequestType_Open;
245 req.u.open.handle=idx;
247 _wapi_daemon_request_response (daemon_sock, &req, &resp);
248 if(resp.type!=WapiHandleResponseType_Open) {
249 g_warning (G_GNUC_PRETTY_FUNCTION
250 ": bogus daemon response, type %d",
252 g_assert_not_reached ();
255 _wapi_shared_data->handles[idx].ref++;
258 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
259 handle, _wapi_shared_data->handles[idx].ref);
264 void _wapi_handle_unref (gpointer handle)
266 guint32 idx=GPOINTER_TO_UINT (handle);
270 WapiHandleRequest req;
271 WapiHandleResponse resp;
273 req.type=WapiHandleRequestType_Close;
274 req.u.close.handle=GPOINTER_TO_UINT (handle);
276 _wapi_daemon_request_response (daemon_sock, &req, &resp);
277 if(resp.type!=WapiHandleResponseType_Close) {
278 g_warning (G_GNUC_PRETTY_FUNCTION
279 ": bogus daemon response, type %d",
281 g_assert_not_reached ();
283 destroy=resp.u.close.destroy;
286 _wapi_shared_data->handles[idx].ref--;
289 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
290 handle, _wapi_shared_data->handles[idx].ref);
293 /* Possible race condition here if another thread refs
294 * the handle between here and setting the type to
295 * UNUSED. I could lock a mutex, but I'm not sure
296 * that allowing a handle reference to reach 0 isn't
297 * an application bug anyway.
299 destroy=(_wapi_shared_data->handles[idx].ref==0);
304 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
309 _wapi_handle_ops_close_shared (handle);
310 _wapi_shared_data->handles[idx].type=WAPI_HANDLE_UNUSED;
311 mono_mutex_destroy (&_wapi_shared_data->handles[idx].signal_mutex);
312 pthread_cond_destroy (&_wapi_shared_data->handles[idx].signal_cond);
313 memset (&_wapi_shared_data->handles[idx].u, '\0', sizeof(_wapi_shared_data->handles[idx].u));
316 _wapi_handle_ops_close_private (handle);
320 #define HDRSIZE sizeof(struct _WapiScratchHeader)
322 guint32 _wapi_handle_scratch_store_internal (guint32 bytes)
324 guint32 idx=0, last_idx=0;
325 struct _WapiScratchHeader *hdr, *last_hdr;
326 gboolean last_was_free=FALSE;
327 guchar *storage=&_wapi_shared_data->scratch_base[0];
330 g_message (G_GNUC_PRETTY_FUNCTION
331 ": looking for %d bytes of scratch space (%d bytes total)",
332 bytes, _WAPI_SHM_SCRATCH_SIZE);
335 hdr=(struct _WapiScratchHeader *)&storage[0];
336 if(hdr->flags==0 && hdr->length==0) {
337 /* Need to initialise scratch data */
338 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
339 hdr->length = _WAPI_SHM_SCRATCH_SIZE - HDRSIZE;
342 while(idx< _WAPI_SHM_SCRATCH_SIZE) {
343 hdr=(struct _WapiScratchHeader *)&storage[idx];
345 /* Do a simple first-fit allocation, coalescing
346 * adjacent free blocks as we progress through the
349 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
350 hdr->length >= bytes + HDRSIZE) {
352 guint32 old_length=hdr->length;
354 g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
357 hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
361 /* Put a new header in at the end of the used
364 hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
365 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
366 hdr->length = old_length-bytes-HDRSIZE;
369 g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
373 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
374 last_was_free == FALSE) {
376 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
379 /* save this point in case we can coalesce it with
380 * the next block, if that is free.
385 idx+=(hdr->length+HDRSIZE);
386 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
387 last_was_free == TRUE) {
389 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
392 /* This block and the previous are both free,
395 last_hdr->length += (hdr->length + HDRSIZE);
397 /* If the new block is now big enough, use it
398 * (next time round the loop)
400 if(last_hdr->length >= bytes + HDRSIZE) {
403 /* leave the last free info as it is,
404 * in case the next block is also free
405 * and can be coalesced too
407 idx=last_idx+last_hdr->length+HDRSIZE;
411 g_message (G_GNUC_PRETTY_FUNCTION
412 ": found used block at %d, length %d", idx,
416 /* must be used, try next chunk */
417 idx+=(hdr->length+HDRSIZE);
424 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
426 static pthread_mutex_t scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
430 WapiHandleRequest scratch;
431 WapiHandleResponse scratch_resp;
433 scratch.type=WapiHandleRequestType_Scratch;
434 scratch.u.scratch.length=bytes;
436 _wapi_daemon_request_response (daemon_sock, &scratch,
439 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
440 idx=scratch_resp.u.scratch.idx;
442 g_warning (G_GNUC_PRETTY_FUNCTION
443 ": bogus daemon response, type %d",
445 g_assert_not_reached ();
448 pthread_mutex_lock (&scratch_mutex);
449 idx=_wapi_handle_scratch_store_internal (bytes);
450 pthread_mutex_unlock (&scratch_mutex);
453 /* Failed to allocate space */
458 memcpy (&_wapi_shared_data->scratch_base[idx], data, bytes);
463 guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx)
465 struct _WapiScratchHeader *hdr;
467 guchar *storage=&_wapi_shared_data->scratch_base[0];
469 if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
473 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
474 str=g_malloc0 (hdr->length+1);
475 memcpy (str, &storage[idx], hdr->length);
480 void _wapi_handle_scratch_delete_internal (guint32 idx)
482 struct _WapiScratchHeader *hdr;
483 guchar *storage=&_wapi_shared_data->scratch_base[0];
485 if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) {
489 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
490 memset (&storage[idx], '\0', hdr->length);
491 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
493 /* We could coalesce forwards here if the next block is also
494 * free, but the _store() function will do that anyway.
498 void _wapi_handle_scratch_delete (guint32 idx)
501 WapiHandleRequest scratch_free;
502 WapiHandleResponse scratch_free_resp;
504 scratch_free.type=WapiHandleRequestType_ScratchFree;
505 scratch_free.u.scratch_free.idx=idx;
507 _wapi_daemon_request_response (daemon_sock, &scratch_free,
510 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
511 g_warning (G_GNUC_PRETTY_FUNCTION
512 ": bogus daemon response, type %d",
513 scratch_free_resp.type);
514 g_assert_not_reached ();
517 _wapi_handle_scratch_delete_internal (idx);
521 void _wapi_handle_register_capabilities (WapiHandleType type,
522 WapiHandleCapability caps)
524 handle_caps[type]=caps;
527 gboolean _wapi_handle_test_capabilities (gpointer handle,
528 WapiHandleCapability caps)
530 guint32 idx=GPOINTER_TO_UINT (handle);
533 type=_wapi_shared_data->handles[idx].type;
536 g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
537 handle_caps[type], caps, handle_caps[type] & caps);
540 return((handle_caps[type] & caps)!=0);
543 void _wapi_handle_ops_close_shared (gpointer handle)
545 guint32 idx=GPOINTER_TO_UINT (handle);
548 type=_wapi_shared_data->handles[idx].type;
550 if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
551 handle_ops[type]->close_shared (handle);
555 void _wapi_handle_ops_close_private (gpointer handle)
557 guint32 idx=GPOINTER_TO_UINT (handle);
560 type=_wapi_shared_data->handles[idx].type;
562 if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
563 handle_ops[type]->close_private (handle);
567 void _wapi_handle_ops_signal (gpointer handle)
569 guint32 idx=GPOINTER_TO_UINT (handle);
572 type=_wapi_shared_data->handles[idx].type;
574 if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
575 handle_ops[type]->signal (handle);
579 void _wapi_handle_ops_own (gpointer handle)
581 guint32 idx=GPOINTER_TO_UINT (handle);
584 type=_wapi_shared_data->handles[idx].type;
586 if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
587 handle_ops[type]->own_handle (handle);
591 gboolean _wapi_handle_ops_isowned (gpointer handle)
593 guint32 idx=GPOINTER_TO_UINT (handle);
596 type=_wapi_shared_data->handles[idx].type;
598 if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
599 return(handle_ops[type]->is_owned (handle));
607 * @handle: The handle to release
609 * Closes and invalidates @handle, releasing any resources it
610 * consumes. When the last handle to a temporary or non-persistent
611 * object is closed, that object can be deleted. Closing the same
612 * handle twice is an error.
614 * Return value: %TRUE on success, %FALSE otherwise.
616 gboolean CloseHandle(gpointer handle)
618 _wapi_handle_unref (handle);
623 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
629 guint32 count, i, iter=0;
632 /* Lock all the handles, with backoff */
634 for(i=0; i<numhandles; i++) {
635 guint32 idx=GPOINTER_TO_UINT (handles[i]);
637 ret=mono_mutex_trylock (&_wapi_shared_data->handles[idx].signal_mutex);
640 struct timespec sleepytime;
643 idx=GPOINTER_TO_UINT (handles[i]);
644 mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
647 /* If iter ever reaches 100 the nanosleep will
648 * return EINVAL immediately, but we have a
649 * design flaw if that happens.
653 g_warning (G_GNUC_PRETTY_FUNCTION
654 ": iteration overflow!");
659 sleepytime.tv_nsec=10000000 * iter; /* 10ms*iter */
662 g_message (G_GNUC_PRETTY_FUNCTION
663 ": Backing off for %d ms", iter*10);
665 nanosleep (&sleepytime, NULL);
672 g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
678 for(i=0; i<numhandles; i++) {
679 guint32 idx=GPOINTER_TO_UINT (handles[i]);
682 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
686 if(((_wapi_handle_test_capabilities (handles[i], WAPI_HANDLE_CAP_OWN)==TRUE) &&
687 (_wapi_handle_ops_isowned (handles[i])==TRUE)) ||
688 (_wapi_shared_data->handles[idx].signalled==TRUE)) {
692 g_message (G_GNUC_PRETTY_FUNCTION
693 ": Handle %p signalled", handles[i]);
702 g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
706 if((waitall==TRUE && count==numhandles) ||
707 (waitall==FALSE && count>0)) {
714 g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
722 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
726 for(i=0; i<numhandles; i++) {
727 guint32 idx=GPOINTER_TO_UINT (handles[i]);
729 mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex);
733 int _wapi_handle_wait_signal (void)
735 #ifdef _POSIX_THREAD_PROCESS_SHARED
736 return(mono_cond_wait (&_wapi_shared_data->signal_cond,
737 &_wapi_shared_data->signal_mutex));
739 return(mono_cond_wait (&_wapi_private_data->signal_cond,
740 &_wapi_private_data->signal_mutex));
741 #endif /* _POSIX_THREAD_PROCESS_SHARED */
744 int _wapi_handle_timedwait_signal (struct timespec *timeout)
746 #ifdef _POSIX_THREAD_PROCESS_SHARED
747 return(mono_cond_timedwait (&_wapi_shared_data->signal_cond,
748 &_wapi_shared_data->signal_mutex,
751 return(mono_cond_timedwait (&_wapi_private_data->signal_cond,
752 &_wapi_private_data->signal_mutex,
754 #endif /* _POSIX_THREAD_PROCESS_SHARED */
757 int _wapi_handle_wait_signal_handle (gpointer handle)
759 guint32 idx=GPOINTER_TO_UINT (handle);
761 return(mono_cond_wait (&_wapi_shared_data->handles[idx].signal_cond,
762 &_wapi_shared_data->handles[idx].signal_mutex));
765 int _wapi_handle_timedwait_signal_handle (gpointer handle,
766 struct timespec *timeout)
768 guint32 idx=GPOINTER_TO_UINT (handle);
770 return(mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond,
771 &_wapi_shared_data->handles[idx].signal_mutex,