Implement vararg support for s390. Minor fix to atomic operation for s390.
[mono.git] / mono / io-layer / handles.c
bloba1c3dde13efd889a11789b6069411d2e8b706dbb
1 /*
2 * handles.c: Generic and internal operations on handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 */
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>
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>
31 #undef DEBUG
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
42 * still FALSE.
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]={
51 NULL,
52 &_wapi_file_ops,
53 &_wapi_console_ops,
54 &_wapi_thread_ops,
55 &_wapi_sem_ops,
56 &_wapi_mutex_ops,
57 &_wapi_event_ops,
58 &_wapi_socket_ops,
59 &_wapi_find_ops,
60 &_wapi_process_ops,
61 &_wapi_pipe_ops,
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 guint32 _wapi_fd_offset_table_size;
81 gpointer *_wapi_fd_offset_table=NULL;
83 static void shared_init (void)
85 struct sockaddr_un shared_socket_address;
86 gboolean tried_once=FALSE;
87 int ret;
88 int thr_ret;
90 _wapi_shared_data=g_new0 (struct _WapiHandleShared_list *, 1);
91 _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list *, 1);
93 attach_again:
95 #ifndef DISABLE_SHARED_HANDLES
96 if(getenv ("MONO_DISABLE_SHM"))
97 #endif
99 shared=FALSE;
100 #ifndef DISABLE_SHARED_HANDLES
101 } else {
102 /* Ensure that shared==FALSE while _wapi_shm_attach()
103 * calls fork()
105 shared=FALSE;
107 shared=_wapi_shm_attach (&_wapi_shared_data[0],
108 &_wapi_shared_scratch);
109 if(shared==FALSE) {
110 g_warning (
111 "Failed to attach shared memory! "
112 "Falling back to non-shared handles\n"
113 "See: http://www.go-mono.com/issues.html#wapi for details");
115 #endif /* DISABLE_SHARED_HANDLES */
119 if(shared==TRUE) {
120 daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0);
121 shared_socket_address.sun_family=AF_UNIX;
122 memcpy (shared_socket_address.sun_path,
123 _wapi_shared_data[0]->daemon, MONO_SIZEOF_SUNPATH);
124 ret=connect (daemon_sock,
125 (struct sockaddr *)&shared_socket_address,
126 sizeof(struct sockaddr_un));
127 if(ret==-1) {
128 if(tried_once==TRUE) {
129 g_warning (G_GNUC_PRETTY_FUNCTION
130 ": connect to daemon failed: %s",
131 g_strerror (errno));
132 /* Fall back to private handles */
133 shared=FALSE;
134 } else {
135 /* It's possible that the daemon
136 * crashed without destroying the
137 * shared memory segment (thus fooling
138 * subsequent processes into thinking
139 * the daemon is still active).
141 * Destroy the shared memory segment
142 * and try once more. This won't
143 * break running apps, but no new apps
144 * will be able to see the current
145 * shared memory segment.
147 tried_once=TRUE;
148 _wapi_shm_destroy ();
150 goto attach_again;
152 } else {
153 _wapi_fd_offset_table_size = _wapi_shared_data[0]->fd_offset_table_size;
154 _wapi_fd_offset_table=g_new0 (gpointer, _wapi_fd_offset_table_size);
158 if(shared==FALSE) {
159 #ifdef DEBUG
160 g_message (G_GNUC_PRETTY_FUNCTION
161 ": Using process-private handles");
162 #endif
163 _wapi_shared_data[0]=g_new0 (struct _WapiHandleShared_list, 1);
164 _wapi_shared_data[0]->num_segments=1;
166 _wapi_shared_scratch=g_new0 (struct _WapiHandleScratch, 1);
168 _wapi_fd_offset_table_size=getdtablesize ();
169 _wapi_fd_offset_table=g_new0 (gpointer,
170 _wapi_fd_offset_table_size);
172 _wapi_private_data[0]=g_new0 (struct _WapiHandlePrivate_list, 1);
173 _wapi_shm_mapped_segments=1;
175 thr_ret = pthread_mutexattr_init (&mutex_shared_attr);
176 g_assert (thr_ret == 0);
178 thr_ret = pthread_condattr_init (&cond_shared_attr);
179 g_assert (thr_ret == 0);
181 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
182 thr_ret = pthread_mutexattr_setpshared (&mutex_shared_attr,
183 PTHREAD_PROCESS_SHARED);
184 g_assert (thr_ret == 0);
186 thr_ret = pthread_condattr_setpshared (&cond_shared_attr,
187 PTHREAD_PROCESS_SHARED);
188 g_assert (thr_ret == 0);
189 #else
190 thr_ret = pthread_cond_init(&_wapi_private_data[0]->signal_cond, NULL);
191 g_assert (thr_ret == 0);
193 thr_ret = mono_mutex_init(&_wapi_private_data[0]->signal_mutex, NULL);
194 g_assert (thr_ret == 0);
195 #endif
198 #ifdef HEAVY_DEBUG
199 static void
200 print_handle_count (gint mask)
202 gint *count, num_handles;
203 gint i;
204 static const gchar *names [] = {"WAPI_HANDLE_UNUSED",
205 "WAPI_HANDLE_FILE",
206 "WAPI_HANDLE_CONSOLE",
207 "WAPI_HANDLE_THREAD",
208 "WAPI_HANDLE_SEM",
209 "WAPI_HANDLE_MUTEX",
210 "WAPI_HANDLE_EVENT",
211 "WAPI_HANDLE_SOCKET",
212 "WAPI_HANDLE_FIND",
213 "WAPI_HANDLE_PROCESS",
214 "WAPI_HANDLE_PIPE"
218 num_handles=_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT;
219 count=g_new0 (gint, num_handles);
221 for (i = 1; i < num_handles; i++) {
222 struct _WapiHandleShared *shared;
223 guint32 segment, idx;
225 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
226 _wapi_handle_ensure_mapped (segment);
228 shared = &_wapi_handle_get_shared_segment (segment)->handles[idx];
229 count [shared->type]++;
232 for (i = 0; i < num_handles; i++)
233 if ((i & mask) == i) /* Always prints the UNUSED count */
234 g_print ("%s: %d\n", names [i], count [i]);
236 g_free (count);
238 #endif /* HEAVY_DEBUG */
241 * _wapi_handle_new_internal:
242 * @type: Init handle to this type
244 * Search for a free handle and initialize it. Return the handle on
245 * success and 0 on failure.
247 guint32 _wapi_handle_new_internal (WapiHandleType type)
249 guint32 segment, idx;
250 guint32 i, j;
251 static guint32 last=1;
252 int thr_ret;
253 guint32 num_segments = _wapi_handle_get_shared_segment (0)->num_segments;
255 /* A linear scan should be fast enough. Start from the last
256 * allocation, assuming that handles are allocated more often
257 * than they're freed. Leave 0 (NULL) as a guard
259 #ifdef HEAVY_DEBUG
260 print_handle_count (0xFFF);
261 #endif
262 again:
263 _wapi_handle_segment (GUINT_TO_POINTER (last), &segment, &idx);
264 for(i=segment; i < num_segments; i++) {
265 if(i!=segment) {
266 idx=0;
269 for(j=idx; j<_WAPI_HANDLES_PER_SEGMENT; j++) {
270 struct _WapiHandleShared *shared;
272 /* Make sure we dont try and assign the
273 * handles that would clash with fds
275 if ((i * _WAPI_HANDLES_PER_SEGMENT + j) < _wapi_fd_offset_table_size) {
276 i = _wapi_fd_offset_table_size / _WAPI_HANDLES_PER_SEGMENT;
277 j = _wapi_fd_offset_table_size - (i * _WAPI_HANDLES_PER_SEGMENT);
279 if (i >= num_segments) {
280 /* Need to get the caller to
281 * add more shared segments
283 return(0);
286 continue;
289 shared=&_wapi_handle_get_shared_segment (i)->handles[j];
291 if(shared->type==WAPI_HANDLE_UNUSED) {
292 last=(_wapi_handle_index (i, j)+1) % (_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT);
293 shared->type=type;
294 shared->signalled=FALSE;
295 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
296 thr_ret = mono_mutex_init (&shared->signal_mutex, &mutex_shared_attr);
297 g_assert (thr_ret == 0);
299 thr_ret = pthread_cond_init (&shared->signal_cond, &cond_shared_attr);
300 g_assert (thr_ret == 0);
301 #else
302 thr_ret = pthread_cond_init(&shared->signal_cond, NULL);
303 g_assert (thr_ret == 0);
305 thr_ret = mono_mutex_init(&shared->signal_mutex, NULL);
306 g_assert (thr_ret == 0);
307 #endif
309 return(_wapi_handle_index (i, j));
314 if(last>1) {
315 /* Try again from the beginning */
316 last=1;
317 goto again;
320 /* Will need a new segment. The caller will sort it out */
322 return(0);
325 gpointer _wapi_handle_new (WapiHandleType type)
327 static mono_once_t shared_init_once = MONO_ONCE_INIT;
328 static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER;
329 guint32 handle_idx = 0, idx, segment;
330 gpointer handle;
331 WapiHandleRequest new={0};
332 WapiHandleResponse new_resp={0};
333 #if HAVE_BOEHM_GC
334 gboolean tried_collect=FALSE;
335 #endif
336 int thr_ret;
338 mono_once (&shared_init_once, shared_init);
340 again:
341 if(shared==TRUE) {
342 new.type=WapiHandleRequestType_New;
343 new.u.new.type=type;
345 _wapi_daemon_request_response (daemon_sock, &new, &new_resp);
347 if (new_resp.type==WapiHandleResponseType_New) {
348 handle_idx=new_resp.u.new.handle;
349 } else {
350 g_warning (G_GNUC_PRETTY_FUNCTION
351 ": bogus daemon response, type %d",
352 new_resp.type);
353 g_assert_not_reached ();
355 } else {
356 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
357 (void *)&scan_mutex);
358 thr_ret = pthread_mutex_lock (&scan_mutex);
359 g_assert (thr_ret == 0);
361 while ((handle_idx = _wapi_handle_new_internal (type)) == 0) {
362 /* Try and get a new segment, and have another go */
363 segment=_wapi_handle_get_shared_segment (0)->num_segments;
364 _wapi_handle_ensure_mapped (segment);
366 if(_wapi_handle_get_shared_segment (segment)!=NULL) {
367 /* Got a new segment */
368 _wapi_handle_get_shared_segment (0)->num_segments++;
369 } else {
370 /* Map failed. Just return 0 meaning
371 * "out of handles"
376 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
377 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
379 thr_ret = pthread_mutex_unlock (&scan_mutex);
380 g_assert (thr_ret == 0);
381 pthread_cleanup_pop (0);
384 /* Make sure we left the space for fd mappings */
385 g_assert (handle_idx >= _wapi_fd_offset_table_size);
387 if(handle_idx==0) {
388 g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!");
390 #if HAVE_BOEHM_GC
391 /* See if we can reclaim some handles by forcing a GC
392 * collection
394 if(tried_collect==FALSE) {
395 g_warning (G_GNUC_PRETTY_FUNCTION
396 ": Seeing if GC collection helps...");
397 GC_gcollect (); /* FIXME: we should wait for finalizers to be called */
398 tried_collect=TRUE;
399 goto again;
400 } else {
401 g_warning (G_GNUC_PRETTY_FUNCTION
402 ": didn't help, returning error");
404 #endif
406 return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID));
409 _wapi_handle_segment (GUINT_TO_POINTER (handle_idx), &segment, &idx);
410 _wapi_handle_ensure_mapped (segment);
412 if(_wapi_private_data!=NULL) {
413 _wapi_handle_get_private_segment (segment)->handles[idx].type=type;
416 #if !defined(_POSIX_THREAD_PROCESS_SHARED) || _POSIX_THREAD_PROCESS_SHARED == -1
417 thr_ret = mono_mutex_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex, &mutex_shared_attr);
418 g_assert (thr_ret == 0);
420 thr_ret = pthread_cond_init (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond, &cond_shared_attr);
421 g_assert (thr_ret == 0);
422 #endif
423 handle=GUINT_TO_POINTER (handle_idx);
425 #ifdef DEBUG
426 g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle);
427 #endif
429 return(handle);
432 gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type,
433 gpointer *shared, gpointer *private)
435 struct _WapiHandleShared *shared_handle_data;
436 struct _WapiHandlePrivate *private_handle_data = NULL;
437 guint32 idx;
438 guint32 segment;
440 g_assert (GPOINTER_TO_UINT (handle) >= _wapi_fd_offset_table_size);
442 _wapi_handle_segment (handle, &segment, &idx);
443 _wapi_handle_ensure_mapped (segment);
445 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
447 if(shared!=NULL) {
448 *shared=&shared_handle_data->u;
451 if(private!=NULL) {
452 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
454 *private=&private_handle_data->u;
457 if(shared_handle_data->type!=type) {
458 /* If shared type is UNUSED, see if the private type
459 * matches what we are looking for - this can happen
460 * when the handle is being destroyed and the
461 * close_private function is looking up the private
462 * data
464 if(shared_handle_data->type==WAPI_HANDLE_UNUSED &&
465 (private!=NULL && private_handle_data->type==type)) {
466 return(TRUE);
467 } else {
468 return(FALSE);
472 return(TRUE);
475 gpointer _wapi_search_handle (WapiHandleType type,
476 gboolean (*check)(gpointer test, gpointer user),
477 gpointer user_data,
478 gpointer *shared, gpointer *private)
480 struct _WapiHandleShared *shared_handle_data;
481 struct _WapiHandlePrivate *private_handle_data;
482 guint32 i, segment, idx;
484 for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
485 struct _WapiHandleShared *shared;
487 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
488 _wapi_handle_ensure_mapped (segment);
490 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
492 if(shared->type==type) {
493 if(check (GUINT_TO_POINTER (i), user_data)==TRUE) {
494 break;
499 if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
500 return(GUINT_TO_POINTER (0));
503 if(shared!=NULL) {
504 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
506 *shared=&shared_handle_data->u;
509 if(private!=NULL) {
510 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
512 *private=&private_handle_data->u;
515 return(GUINT_TO_POINTER (i));
518 gpointer _wapi_search_handle_namespace (WapiHandleType type,
519 gchar *utf8_name, gpointer *shared,
520 gpointer *private)
522 struct _WapiHandleShared *shared_handle_data;
523 struct _WapiHandlePrivate *private_handle_data;
524 guint32 i, segment, idx;
526 #ifdef DEBUG
527 g_message (G_GNUC_PRETTY_FUNCTION
528 ": Lookup for handle named [%s] type %d", utf8_name, type);
529 #endif
531 for(i=1; i<_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT; i++) {
532 struct _WapiHandleShared *shared;
534 _wapi_handle_segment (GUINT_TO_POINTER (i), &segment, &idx);
535 _wapi_handle_ensure_mapped (segment);
537 shared=&_wapi_handle_get_shared_segment (segment)->handles[idx];
539 /* Check mutex, event, semaphore, timer, job and file-mapping
540 * object names. So far only mutex is implemented.
542 if(_WAPI_SHARED_NAMESPACE (shared->type)) {
543 gchar *lookup_name;
544 WapiSharedNamespace *sharedns;
546 #ifdef DEBUG
547 g_message (G_GNUC_PRETTY_FUNCTION ": found a shared namespace handle at 0x%x (type %d)", i, shared->type);
548 #endif
550 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
551 sharedns=(WapiSharedNamespace *)&shared_handle_data->u;
554 if(sharedns->name) {
555 lookup_name=_wapi_handle_scratch_lookup (
556 sharedns->name);
557 } else {
558 #ifdef DEBUG
559 g_message (G_GNUC_PRETTY_FUNCTION
560 ": handle 0x%x is unnamed", i);
561 #endif
562 continue;
565 if(lookup_name==NULL) {
566 #ifdef DEBUG
567 g_message (G_GNUC_PRETTY_FUNCTION
568 ": couldn't find handle 0x%x name",
570 #endif
571 continue;
574 #ifdef DEBUG
575 g_message (G_GNUC_PRETTY_FUNCTION ": name is [%s]",
576 lookup_name);
577 #endif
579 if(strcmp (lookup_name, utf8_name)==0) {
580 if(shared->type!=type) {
581 /* Its the wrong type, so fail now */
582 #ifdef DEBUG
583 g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name but is wrong type: %d", i, shared->type);
584 #endif
585 return(_WAPI_HANDLE_INVALID);
586 } else {
587 /* fall through so we can fill
588 * in the data
590 #ifdef DEBUG
591 g_message (G_GNUC_PRETTY_FUNCTION ": handle 0x%x matches name and type", i);
592 #endif
593 break;
599 if(i==_wapi_handle_get_shared_segment (0)->num_segments * _WAPI_HANDLES_PER_SEGMENT) {
600 return(GUINT_TO_POINTER (0));
603 if(shared!=NULL) {
604 shared_handle_data=&_wapi_handle_get_shared_segment (segment)->handles[idx];
606 *shared=&shared_handle_data->u;
609 if(private!=NULL) {
610 private_handle_data=&_wapi_handle_get_private_segment (segment)->handles[idx];
612 *private=&private_handle_data->u;
615 return(GUINT_TO_POINTER (i));
618 void _wapi_handle_ref (gpointer handle)
620 g_assert (GPOINTER_TO_UINT (handle) >= _wapi_fd_offset_table_size);
622 if(shared==TRUE) {
623 WapiHandleRequest req={0};
624 WapiHandleResponse resp={0};
626 req.type=WapiHandleRequestType_Open;
627 req.u.open.handle=GPOINTER_TO_UINT (handle);
629 _wapi_daemon_request_response (daemon_sock, &req, &resp);
630 if(resp.type!=WapiHandleResponseType_Open) {
631 g_warning (G_GNUC_PRETTY_FUNCTION
632 ": bogus daemon response, type %d",
633 resp.type);
634 g_assert_not_reached ();
636 } else {
637 guint32 idx, segment;
639 _wapi_handle_segment (handle, &segment, &idx);
641 _wapi_handle_get_shared_segment (segment)->handles[idx].ref++;
643 #ifdef DEBUG
644 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d",
645 handle,
646 _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
647 #endif
651 /* The handle must not be locked on entry to this function */
652 void _wapi_handle_unref (gpointer handle)
654 guint32 idx, segment;
655 gboolean destroy = FALSE;
656 int thr_ret;
658 g_assert (GPOINTER_TO_UINT (handle) >= _wapi_fd_offset_table_size);
660 _wapi_handle_segment (handle, &segment, &idx);
662 if(shared==TRUE) {
663 WapiHandleRequest req={0};
664 WapiHandleResponse resp={0};
666 req.type=WapiHandleRequestType_Close;
667 req.u.close.handle=GPOINTER_TO_UINT (handle);
669 _wapi_daemon_request_response (daemon_sock, &req, &resp);
670 if(resp.type!=WapiHandleResponseType_Close) {
671 g_warning (G_GNUC_PRETTY_FUNCTION
672 ": bogus daemon response, type %d",
673 resp.type);
674 g_assert_not_reached ();
675 } else {
676 destroy=resp.u.close.destroy;
678 } else {
679 _wapi_handle_get_shared_segment (segment)->handles[idx].ref--;
681 #ifdef DEBUG
682 g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", handle, _wapi_handle_get_shared_segment (segment)->handles[idx].ref);
683 #endif
685 /* Possible race condition here if another thread refs
686 * the handle between here and setting the type to
687 * UNUSED. I could lock a mutex, but I'm not sure
688 * that allowing a handle reference to reach 0 isn't
689 * an application bug anyway.
691 destroy=(_wapi_handle_get_shared_segment (segment)->handles[idx].ref==0);
694 if(destroy==TRUE) {
695 #ifdef DEBUG
696 g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p",
697 handle);
698 #endif
700 if(shared==FALSE) {
701 _wapi_handle_ops_close_shared (handle);
703 memset (&_wapi_handle_get_shared_segment (segment)->handles[idx].u, '\0', sizeof(_wapi_handle_get_shared_segment (segment)->handles[idx].u));
706 _wapi_handle_ops_close_private (handle);
707 _wapi_handle_get_shared_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
708 _wapi_handle_get_private_segment (segment)->handles[idx].type=WAPI_HANDLE_UNUSED;
710 /* Destroy the mutex and cond var. We hope nobody
711 * tried to grab them between the handle unlock and
712 * now, but pthreads doesn't have a
713 * "unlock_and_destroy" atomic function.
715 thr_ret = mono_mutex_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
716 g_assert (thr_ret == 0);
718 thr_ret = pthread_cond_destroy (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond);
719 g_assert (thr_ret == 0);
723 #define HDRSIZE sizeof(struct _WapiScratchHeader)
725 static pthread_mutex_t _wapi_scratch_mutex=PTHREAD_MUTEX_INITIALIZER;
727 /* _wapi_scratch_mutex must be held when this function is called in
728 * the non-shared case
730 static void _wapi_handle_scratch_expand (void)
732 guint32 old_len, new_len;
734 old_len=sizeof(struct _WapiHandleScratch) +
735 _wapi_shared_scratch->data_len;
736 new_len=old_len+_WAPI_SHM_SCRATCH_SIZE;
738 if(_wapi_shared_scratch->is_shared==TRUE) {
739 /* expand via mmap() */
740 _wapi_shared_scratch=_wapi_shm_file_expand (_wapi_shared_scratch, WAPI_SHM_SCRATCH, 0, old_len, new_len);
741 } else {
742 _wapi_shared_scratch=_wapi_g_renew0 (_wapi_shared_scratch, old_len, new_len);
744 _wapi_shared_scratch->data_len+=_WAPI_SHM_SCRATCH_SIZE;
747 /* _wapi_scratch_mutex must be held when this function is called in
748 * the non-shared case
750 static guint32 _wapi_handle_scratch_locate_space (guint32 bytes)
752 guint32 idx=0, last_idx=0;
753 struct _WapiScratchHeader *hdr, *last_hdr = NULL;
754 gboolean last_was_free=FALSE;
755 guchar *storage=_wapi_shared_scratch->scratch_data;
757 #ifdef DEBUG
758 g_message (G_GNUC_PRETTY_FUNCTION
759 ": looking for %d bytes of scratch space (%d bytes total)",
760 bytes, _wapi_shared_scratch->data_len);
761 #endif
763 while(idx< _wapi_shared_scratch->data_len) {
764 hdr=(struct _WapiScratchHeader *)&storage[idx];
766 /* Do a simple first-fit allocation, coalescing
767 * adjacent free blocks as we progress through the
768 * scratch space
770 if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
771 hdr->length >= bytes + HDRSIZE) {
772 /* found space */
773 guint32 old_length=hdr->length;
774 #ifdef DEBUG
775 g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length);
776 #endif
778 hdr->flags &= ~WAPI_SHM_SCRATCH_FREE;
779 hdr->length=bytes;
780 idx += HDRSIZE;
782 /* Put a new header in at the end of the used
783 * space
785 hdr=(struct _WapiScratchHeader *)&storage[idx+bytes];
786 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
787 hdr->length = old_length-bytes-HDRSIZE;
789 #ifdef DEBUG
790 g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length);
791 #endif
794 * It was memset(0..) when free/made so no need to do it here
797 return(idx);
798 } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE &&
799 last_was_free == FALSE) {
800 #ifdef DEBUG
801 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length);
802 #endif
804 /* save this point in case we can coalesce it with
805 * the next block, if that is free.
807 last_was_free=TRUE;
808 last_idx=idx;
809 last_hdr=hdr;
810 idx+=(hdr->length+HDRSIZE);
811 } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE &&
812 last_was_free == TRUE) {
813 #ifdef DEBUG
814 g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length);
815 #endif
817 /* This block and the previous are both free,
818 * so coalesce them
820 last_hdr->length += (hdr->length + HDRSIZE);
822 /* If the new block is now big enough, use it
823 * (next time round the loop)
825 if(last_hdr->length >= bytes + HDRSIZE) {
826 idx=last_idx;
827 } else {
828 /* leave the last free info as it is,
829 * in case the next block is also free
830 * and can be coalesced too
832 idx=last_idx+last_hdr->length+HDRSIZE;
834 } else {
835 #ifdef DEBUG
836 g_message (G_GNUC_PRETTY_FUNCTION
837 ": found used block at %d, length %d", idx,
838 hdr->length);
839 #endif
841 /* must be used, try next chunk */
842 idx+=(hdr->length+HDRSIZE);
844 /* Don't let the coalescing blow away this block */
845 last_was_free=FALSE;
847 /* But remember where the last block started */
848 last_idx=idx;
852 /* Not enough free space. last_idx points to the last block.
853 * If it's free, just tack on more space and update the
854 * length. If it's allocated, it must have fit right into the
855 * available space, so add more space and add a new header
856 * after this block.
858 _wapi_handle_scratch_expand ();
859 storage=_wapi_shared_scratch->scratch_data;
861 hdr=(struct _WapiScratchHeader *)&storage[last_idx];
862 if(hdr->flags & WAPI_SHM_SCRATCH_FREE) {
863 hdr->length+=_WAPI_SHM_SCRATCH_SIZE;
864 } else {
865 idx=(hdr->length+HDRSIZE);
866 hdr=(struct _WapiScratchHeader *)&storage[idx];
867 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
868 hdr->length = _WAPI_SHM_SCRATCH_SIZE-HDRSIZE;
871 /* The caller will try again */
872 return(0);
876 * _wapi_handle_scratch_store_internal:
877 * @bytes: Allocate no. bytes
879 * Like malloc(3) except its for the shared memory segment's scratch
880 * part. Memory block returned is zeroed out.
882 guint32 _wapi_handle_scratch_store_internal (guint32 bytes, gboolean *remap)
884 guchar *storage;
885 guint32 idx;
886 struct _WapiScratchHeader *hdr;
888 #ifdef DEBUG
889 g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
890 #endif
892 *remap=FALSE;
894 if(_wapi_shared_scratch->data_len==0) {
895 /* Need to expand the data array for the first use */
896 #ifdef DEBUG
897 g_message (G_GNUC_PRETTY_FUNCTION
898 ": setting up scratch space");
899 #endif
901 _wapi_handle_scratch_expand ();
902 *remap=TRUE;
905 storage=_wapi_shared_scratch->scratch_data;
906 hdr=(struct _WapiScratchHeader *)&storage[0];
907 if(hdr->flags==0 && hdr->length==0) {
908 /* Need to initialise scratch data */
909 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
910 hdr->length = _wapi_shared_scratch->data_len - HDRSIZE;
913 idx=_wapi_handle_scratch_locate_space (bytes);
914 if(idx==0) {
915 /* Some more space will have been allocated, so try again */
916 #ifdef DEBUG
917 g_message (G_GNUC_PRETTY_FUNCTION ": trying again");
918 #endif
920 idx=_wapi_handle_scratch_locate_space (bytes);
921 *remap=TRUE;
924 return(idx);
927 guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes)
929 guint32 idx = 0, store_bytes;
930 gboolean remap;
931 int thr_ret;
932 guint32 ret = 0;
934 #ifdef DEBUG
935 g_message (G_GNUC_PRETTY_FUNCTION ": storing %d bytes", bytes);
936 #endif
938 /* No point storing no data */
939 if(bytes==0) {
940 return(0);
943 /* Align bytes to 32 bits (needed for sparc at least) */
944 store_bytes = (((bytes) + 3) & (~3));
946 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
947 (void *)&_wapi_scratch_mutex);
948 thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
949 g_assert (thr_ret == 0);
951 if(shared==TRUE) {
952 WapiHandleRequest scratch={0};
953 WapiHandleResponse scratch_resp={0};
954 guint32 old_len=sizeof(struct _WapiHandleScratch) +
955 _wapi_shared_scratch->data_len;
957 scratch.type=WapiHandleRequestType_Scratch;
958 scratch.u.scratch.length=store_bytes;
960 _wapi_daemon_request_response (daemon_sock, &scratch,
961 &scratch_resp);
963 if(scratch_resp.type==WapiHandleResponseType_Scratch) {
964 idx=scratch_resp.u.scratch.idx;
965 remap=scratch_resp.u.scratch.remap;
966 } else {
967 g_warning (G_GNUC_PRETTY_FUNCTION
968 ": bogus daemon response, type %d",
969 scratch_resp.type);
970 g_assert_not_reached ();
973 if(remap==TRUE) {
974 munmap (_wapi_shared_scratch, old_len);
975 _wapi_shared_scratch=_wapi_shm_file_map (WAPI_SHM_SCRATCH, 0, NULL, NULL);
977 } else {
978 idx=_wapi_handle_scratch_store_internal (store_bytes, &remap);
979 if(idx==0) {
980 /* Failed to allocate space */
981 goto cleanup;
984 ret = idx;
986 #ifdef DEBUG
987 g_message (G_GNUC_PRETTY_FUNCTION
988 ": stored [%s] at %d (len %d, aligned len %d)",
989 (char *)data, idx, bytes, store_bytes);
990 #endif
992 memcpy (&_wapi_shared_scratch->scratch_data[idx], data, bytes);
994 cleanup:
995 thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
996 g_assert (thr_ret == 0);
997 pthread_cleanup_pop (0);
999 return(ret);
1002 guint32 _wapi_handle_scratch_store_string_array (gchar **data)
1004 guint32 *stored_strings, count=0, i, idx;
1005 gchar **strings;
1007 /* No point storing no data */
1008 if(data==NULL) {
1009 return(0);
1012 strings=data;
1013 while(*strings!=NULL) {
1014 count++;
1015 strings++;
1018 #ifdef DEBUG
1019 g_message (G_GNUC_PRETTY_FUNCTION ": %d strings to store", count);
1020 #endif
1022 if(count==0) {
1023 return(0);
1026 /* stored_strings[0] is the count */
1027 stored_strings=g_new0 (guint32, count+1);
1028 stored_strings[0]=count;
1030 strings=data;
1031 for(i=0; i<count; i++) {
1032 stored_strings[i+1]=_wapi_handle_scratch_store (strings[i], strlen (strings[i]));
1035 idx=_wapi_handle_scratch_store (stored_strings,
1036 sizeof(guint32)*(count+1));
1038 return(idx);
1041 gpointer _wapi_handle_scratch_lookup (guint32 idx)
1043 struct _WapiScratchHeader *hdr;
1044 gpointer ret;
1045 guchar *storage;
1046 int thr_ret;
1048 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1049 return(NULL);
1052 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
1053 (void *)&_wapi_scratch_mutex);
1054 thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
1055 g_assert (thr_ret == 0);
1057 storage=_wapi_shared_scratch->scratch_data;
1059 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
1060 ret=g_malloc0 (hdr->length+1);
1061 memcpy (ret, &storage[idx], hdr->length);
1063 thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
1064 g_assert (thr_ret == 0);
1065 pthread_cleanup_pop (0);
1067 return(ret);
1070 gchar **_wapi_handle_scratch_lookup_string_array (guint32 idx)
1072 gchar **strings;
1073 guint32 *stored_strings;
1074 guint32 count, i;
1076 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1077 return(NULL);
1080 stored_strings=_wapi_handle_scratch_lookup (idx);
1081 if(stored_strings==NULL) {
1082 return(NULL);
1085 /* stored_strings[0] is the number of strings, the index of
1086 * each string follows
1088 count=stored_strings[0];
1090 #ifdef DEBUG
1091 g_message (G_GNUC_PRETTY_FUNCTION
1092 ": looking up an array of %d strings", count);
1093 #endif
1095 /* NULL-terminate the array */
1096 strings=g_new0 (gchar *, count+1);
1098 for(i=0; i<count; i++) {
1099 strings[i]=_wapi_handle_scratch_lookup (stored_strings[i+1]);
1101 #ifdef DEBUG
1102 g_message (G_GNUC_PRETTY_FUNCTION ": string %d is [%s]", i,
1103 strings[i]);
1104 #endif
1107 g_free (stored_strings);
1109 return(strings);
1113 * _wapi_handle_scratch_delete_internal:
1114 * @idx: Index to free block
1116 * Like free(3) except its for the shared memory segment's scratch
1117 * part.
1119 void _wapi_handle_scratch_delete_internal (guint32 idx)
1121 struct _WapiScratchHeader *hdr;
1122 guchar *storage;
1123 int thr_ret;
1125 if(idx < HDRSIZE || idx > _wapi_shared_scratch->data_len) {
1126 return;
1129 pthread_cleanup_push ((void(*)(void *))pthread_mutex_unlock,
1130 (void *)&_wapi_scratch_mutex);
1131 thr_ret = pthread_mutex_lock (&_wapi_scratch_mutex);
1132 g_assert (thr_ret == 0);
1134 storage=_wapi_shared_scratch->scratch_data;
1136 hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE];
1137 memset (&storage[idx], '\0', hdr->length);
1138 hdr->flags |= WAPI_SHM_SCRATCH_FREE;
1140 /* We could coalesce forwards here if the next block is also
1141 * free, but the _store() function will do that anyway.
1144 thr_ret = pthread_mutex_unlock (&_wapi_scratch_mutex);
1145 g_assert (thr_ret == 0);
1146 pthread_cleanup_pop (0);
1149 void _wapi_handle_scratch_delete (guint32 idx)
1151 if(shared==TRUE) {
1152 WapiHandleRequest scratch_free={0};
1153 WapiHandleResponse scratch_free_resp={0};
1155 scratch_free.type=WapiHandleRequestType_ScratchFree;
1156 scratch_free.u.scratch_free.idx=idx;
1158 _wapi_daemon_request_response (daemon_sock, &scratch_free,
1159 &scratch_free_resp);
1161 if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) {
1162 g_warning (G_GNUC_PRETTY_FUNCTION
1163 ": bogus daemon response, type %d",
1164 scratch_free_resp.type);
1165 g_assert_not_reached ();
1167 } else {
1168 _wapi_handle_scratch_delete_internal (idx);
1172 void _wapi_handle_scratch_delete_string_array (guint32 idx)
1174 guint32 *stored_strings;
1175 guint32 count, i;
1177 stored_strings=_wapi_handle_scratch_lookup (idx);
1178 if(stored_strings==NULL) {
1179 return;
1182 /* stored_strings[0] is the number of strings, the index of
1183 * each string follows
1185 count=stored_strings[0];
1187 #ifdef DEBUG
1188 g_message (G_GNUC_PRETTY_FUNCTION ": deleting an array of %d strings",
1189 count);
1190 #endif
1192 for(i=1; i<count; i++) {
1193 _wapi_handle_scratch_delete (stored_strings[i]);
1196 _wapi_handle_scratch_delete (idx);
1198 g_free (stored_strings);
1201 void _wapi_handle_register_capabilities (WapiHandleType type,
1202 WapiHandleCapability caps)
1204 handle_caps[type]=caps;
1207 gboolean _wapi_handle_test_capabilities (gpointer handle,
1208 WapiHandleCapability caps)
1210 guint32 idx, segment;
1211 WapiHandleType type;
1213 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1214 handle = _wapi_handle_fd_offset_to_handle (handle);
1217 _wapi_handle_segment (handle, &segment, &idx);
1219 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1221 #ifdef DEBUG
1222 g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)",
1223 handle_caps[type], caps, handle_caps[type] & caps);
1224 #endif
1226 return((handle_caps[type] & caps)!=0);
1229 void _wapi_handle_ops_close_shared (gpointer handle)
1231 guint32 idx, segment;
1232 WapiHandleType type;
1234 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1235 handle = _wapi_handle_fd_offset_to_handle (handle);
1238 _wapi_handle_segment (handle, &segment, &idx);
1240 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1242 if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) {
1243 handle_ops[type]->close_shared (handle);
1247 void _wapi_handle_ops_close_private (gpointer handle)
1249 guint32 idx, segment;
1250 WapiHandleType type;
1252 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1253 handle = _wapi_handle_fd_offset_to_handle (handle);
1256 _wapi_handle_segment (handle, &segment, &idx);
1258 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1260 /* When a handle in the process of being destroyed the shared
1261 * type has already been set to UNUSED
1263 if(type==WAPI_HANDLE_UNUSED && _wapi_private_data!=NULL) {
1264 type=_wapi_handle_get_private_segment (segment)->handles[idx].type;
1267 if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) {
1268 handle_ops[type]->close_private (handle);
1272 void _wapi_handle_ops_signal (gpointer handle)
1274 guint32 idx, segment;
1275 WapiHandleType type;
1277 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1278 handle = _wapi_handle_fd_offset_to_handle (handle);
1281 _wapi_handle_segment (handle, &segment, &idx);
1283 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1285 if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) {
1286 handle_ops[type]->signal (handle);
1290 void _wapi_handle_ops_own (gpointer handle)
1292 guint32 idx, segment;
1293 WapiHandleType type;
1295 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1296 handle = _wapi_handle_fd_offset_to_handle (handle);
1299 _wapi_handle_segment (handle, &segment, &idx);
1301 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1303 if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) {
1304 handle_ops[type]->own_handle (handle);
1308 gboolean _wapi_handle_ops_isowned (gpointer handle)
1310 guint32 idx, segment;
1311 WapiHandleType type;
1313 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1314 handle = _wapi_handle_fd_offset_to_handle (handle);
1317 _wapi_handle_segment (handle, &segment, &idx);
1319 type=_wapi_handle_get_shared_segment (segment)->handles[idx].type;
1321 if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) {
1322 return(handle_ops[type]->is_owned (handle));
1323 } else {
1324 return(FALSE);
1329 * CloseHandle:
1330 * @handle: The handle to release
1332 * Closes and invalidates @handle, releasing any resources it
1333 * consumes. When the last handle to a temporary or non-persistent
1334 * object is closed, that object can be deleted. Closing the same
1335 * handle twice is an error.
1337 * Return value: %TRUE on success, %FALSE otherwise.
1339 gboolean CloseHandle(gpointer handle)
1341 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1342 handle = _wapi_handle_fd_offset_to_handle (handle);
1345 if (handle == NULL) {
1346 return(FALSE);
1349 _wapi_handle_unref (handle);
1351 return(TRUE);
1354 gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
1355 gpointer *handles,
1356 gboolean waitall,
1357 guint32 *retcount,
1358 guint32 *lowest)
1360 guint32 count, i, iter=0;
1361 gboolean ret;
1362 int thr_ret;
1364 /* Lock all the handles, with backoff */
1365 again:
1366 for(i=0; i<numhandles; i++) {
1367 guint32 idx, segment;
1368 gpointer handle = handles[i];
1370 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1371 handle = _wapi_handle_fd_offset_to_handle (handle);
1374 _wapi_handle_segment (handle, &segment, &idx);
1376 #ifdef DEBUG
1377 g_message (G_GNUC_PRETTY_FUNCTION ": attempting to lock %p",
1378 handle);
1379 #endif
1381 ret=mono_mutex_trylock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1382 if(ret!=0) {
1383 /* Bummer */
1384 struct timespec sleepytime;
1386 #ifdef DEBUG
1387 g_message (G_GNUC_PRETTY_FUNCTION ": attempt failed for %p: %s", handle, strerror (ret));
1388 #endif
1390 while(i--) {
1391 handle = handles[i];
1393 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1394 handle = _wapi_handle_fd_offset_to_handle (handle);
1397 _wapi_handle_segment (handle, &segment, &idx);
1398 thr_ret = mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1399 g_assert (thr_ret == 0);
1402 /* If iter ever reaches 100 the nanosleep will
1403 * return EINVAL immediately, but we have a
1404 * design flaw if that happens.
1406 iter++;
1407 if(iter==100) {
1408 g_warning (G_GNUC_PRETTY_FUNCTION
1409 ": iteration overflow!");
1410 iter=1;
1413 sleepytime.tv_sec=0;
1414 sleepytime.tv_nsec=10000000 * iter; /* 10ms*iter */
1416 #ifdef DEBUG
1417 g_message (G_GNUC_PRETTY_FUNCTION
1418 ": Backing off for %d ms", iter*10);
1419 #endif
1420 nanosleep (&sleepytime, NULL);
1422 goto again;
1426 #ifdef DEBUG
1427 g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles");
1428 #endif
1430 count=0;
1431 *lowest=numhandles;
1433 for(i=0; i<numhandles; i++) {
1434 guint32 idx, segment;
1435 gpointer handle = handles[i];
1437 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1438 handle = _wapi_handle_fd_offset_to_handle (handle);
1441 _wapi_handle_ref (handle);
1443 _wapi_handle_segment (handle, &segment, &idx);
1445 #ifdef DEBUG
1446 g_message (G_GNUC_PRETTY_FUNCTION ": Checking handle %p",
1447 handle);
1448 #endif
1450 if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) &&
1451 (_wapi_handle_ops_isowned (handle)==TRUE)) ||
1452 (_wapi_handle_get_shared_segment (segment)->handles[idx].signalled==TRUE)) {
1453 count++;
1455 #ifdef DEBUG
1456 g_message (G_GNUC_PRETTY_FUNCTION
1457 ": Handle %p signalled", handle);
1458 #endif
1459 if(*lowest>i) {
1460 *lowest=i;
1465 #ifdef DEBUG
1466 g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled",
1467 count);
1468 #endif
1470 if((waitall==TRUE && count==numhandles) ||
1471 (waitall==FALSE && count>0)) {
1472 ret=TRUE;
1473 } else {
1474 ret=FALSE;
1477 #ifdef DEBUG
1478 g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret);
1479 #endif
1481 *retcount=count;
1483 return(ret);
1486 void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles)
1488 guint32 i;
1489 int thr_ret;
1491 for(i=0; i<numhandles; i++) {
1492 guint32 idx, segment;
1493 gpointer handle = handles[i];
1495 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1496 handle = _wapi_handle_fd_offset_to_handle (handle);
1499 _wapi_handle_segment (handle, &segment, &idx);
1501 #ifdef DEBUG
1502 g_message (G_GNUC_PRETTY_FUNCTION ": unlocking handle %p",
1503 handle);
1504 #endif
1506 thr_ret = mono_mutex_unlock (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex);
1507 g_assert (thr_ret == 0);
1509 _wapi_handle_unref (handle);
1513 /* Process-shared handles (currently only process and thread handles
1514 * are allowed, and they only work because once signalled they can't
1515 * become unsignalled) are waited for by one process and signalled by
1516 * another. Without process-shared conditions, the waiting process
1517 * will block forever. To get around this, the four handle waiting
1518 * functions use a short timeout when _POSIX_THREAD_PROCESS_SHARED is
1519 * not available. They also return "success" if the fake timeout
1520 * expired, and let the caller check signal status.
1522 int _wapi_handle_wait_signal (void)
1524 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1525 return(mono_cond_wait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1526 &_wapi_handle_get_shared_segment (0)->signal_mutex));
1527 #else
1528 struct timespec fake_timeout;
1529 int ret;
1531 _wapi_calc_timeout (&fake_timeout, 100);
1533 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1534 &_wapi_handle_get_private_segment (0)->signal_mutex,
1535 &fake_timeout);
1536 if(ret==ETIMEDOUT) {
1537 ret=0;
1540 return(ret);
1541 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1544 int _wapi_handle_timedwait_signal (struct timespec *timeout)
1546 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1547 return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (0)->signal_cond,
1548 &_wapi_handle_get_shared_segment (0)->signal_mutex,
1549 timeout));
1550 #else
1551 struct timespec fake_timeout;
1552 int ret;
1554 _wapi_calc_timeout (&fake_timeout, 100);
1556 if((fake_timeout.tv_sec>timeout->tv_sec) ||
1557 (fake_timeout.tv_sec==timeout->tv_sec &&
1558 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1559 /* Real timeout is less than 100ms time */
1560 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1561 &_wapi_handle_get_private_segment (0)->signal_mutex,
1562 timeout);
1563 } else {
1564 ret=mono_cond_timedwait (&_wapi_handle_get_private_segment (0)->signal_cond,
1565 &_wapi_handle_get_private_segment (0)->signal_mutex,
1566 &fake_timeout);
1567 if(ret==ETIMEDOUT) {
1568 ret=0;
1572 return(ret);
1573 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1576 int _wapi_handle_wait_signal_handle (gpointer handle)
1578 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1579 guint32 idx, segment;
1581 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1582 handle = _wapi_handle_fd_offset_to_handle (handle);
1585 _wapi_handle_segment (handle, &segment, &idx);
1587 return(mono_cond_wait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1588 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex));
1589 #else
1590 guint32 idx, segment;
1591 struct timespec fake_timeout;
1592 int ret;
1594 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1595 handle = _wapi_handle_fd_offset_to_handle (handle);
1598 _wapi_handle_segment (handle, &segment, &idx);
1599 _wapi_calc_timeout (&fake_timeout, 100);
1601 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1602 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1603 &fake_timeout);
1604 if(ret==ETIMEDOUT) {
1605 ret=0;
1608 return(ret);
1609 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1612 int _wapi_handle_timedwait_signal_handle (gpointer handle,
1613 struct timespec *timeout)
1615 #if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED != -1
1616 guint32 idx, segment;
1618 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1619 handle = _wapi_handle_fd_offset_to_handle (handle);
1622 _wapi_handle_segment (handle, &segment, &idx);
1624 return(mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1625 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1626 timeout));
1627 #else
1628 guint32 idx, segment;
1629 struct timespec fake_timeout;
1630 int ret;
1632 if (GPOINTER_TO_UINT (handle) < _wapi_fd_offset_table_size) {
1633 handle = _wapi_handle_fd_offset_to_handle (handle);
1636 _wapi_handle_segment (handle, &segment, &idx);
1637 _wapi_calc_timeout (&fake_timeout, 100);
1639 if((fake_timeout.tv_sec>timeout->tv_sec) ||
1640 (fake_timeout.tv_sec==timeout->tv_sec &&
1641 fake_timeout.tv_nsec > timeout->tv_nsec)) {
1642 /* Real timeout is less than 100ms time */
1643 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1644 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1645 timeout);
1646 } else {
1647 ret=mono_cond_timedwait (&_wapi_handle_get_shared_segment (segment)->handles[idx].signal_cond,
1648 &_wapi_handle_get_shared_segment (segment)->handles[idx].signal_mutex,
1649 &fake_timeout);
1650 if(ret==ETIMEDOUT) {
1651 ret=0;
1655 return(ret);
1656 #endif /* _POSIX_THREAD_PROCESS_SHARED */
1659 gboolean _wapi_handle_process_fork (guint32 cmd, guint32 env, guint32 dir,
1660 gboolean inherit, guint32 flags,
1661 gpointer stdin_handle,
1662 gpointer stdout_handle,
1663 gpointer stderr_handle,
1664 gpointer *process_handle,
1665 gpointer *thread_handle, guint32 *pid,
1666 guint32 *tid)
1668 WapiHandleRequest fork_proc={0};
1669 WapiHandleResponse fork_proc_resp={0};
1670 int in_fd, out_fd, err_fd;
1672 if(shared!=TRUE) {
1673 return(FALSE);
1676 fork_proc.type=WapiHandleRequestType_ProcessFork;
1677 fork_proc.u.process_fork.cmd=cmd;
1678 fork_proc.u.process_fork.env=env;
1679 fork_proc.u.process_fork.dir=dir;
1680 fork_proc.u.process_fork.stdin_handle=GPOINTER_TO_UINT (stdin_handle);
1681 fork_proc.u.process_fork.stdout_handle=GPOINTER_TO_UINT (stdout_handle);
1682 fork_proc.u.process_fork.stderr_handle=GPOINTER_TO_UINT (stderr_handle);
1683 fork_proc.u.process_fork.inherit=inherit;
1684 fork_proc.u.process_fork.flags=flags;
1686 in_fd=_wapi_file_handle_to_fd (stdin_handle);
1687 out_fd=_wapi_file_handle_to_fd (stdout_handle);
1688 err_fd=_wapi_file_handle_to_fd (stderr_handle);
1690 if(in_fd==-1 || out_fd==-1 || err_fd==-1) {
1691 /* We were given duff handles */
1692 /* FIXME: error code */
1693 return(FALSE);
1696 _wapi_daemon_request_response_with_fds (daemon_sock, &fork_proc,
1697 &fork_proc_resp, in_fd,
1698 out_fd, err_fd);
1699 if(fork_proc_resp.type==WapiHandleResponseType_ProcessFork) {
1700 *process_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.process_handle);
1701 *thread_handle=GUINT_TO_POINTER (fork_proc_resp.u.process_fork.thread_handle);
1702 *pid=fork_proc_resp.u.process_fork.pid;
1703 *tid=fork_proc_resp.u.process_fork.tid;
1705 /* If there was an internal error, the handles will be
1706 * 0. If there was an error forking or execing, the
1707 * handles will have values, and process_handle's
1708 * exec_errno will be set, and the handle will be
1709 * signalled immediately.
1711 if(*process_handle==0 || *thread_handle==0) {
1712 return(FALSE);
1713 } else {
1714 /* This call returns new handles, so we need to do
1715 * a little bookkeeping
1717 if (_wapi_private_data != NULL) {
1718 guint32 segment, idx;
1720 _wapi_handle_segment (*process_handle,
1721 &segment, &idx);
1722 _wapi_handle_ensure_mapped (segment);
1723 _wapi_handle_get_private_segment (segment)->handles[idx].type = WAPI_HANDLE_PROCESS;
1725 _wapi_handle_segment (*thread_handle,
1726 &segment, &idx);
1727 _wapi_handle_ensure_mapped (segment);
1728 _wapi_handle_get_private_segment (segment)->handles[idx].type = WAPI_HANDLE_THREAD;
1731 return(TRUE);
1733 } else {
1734 g_warning (G_GNUC_PRETTY_FUNCTION
1735 ": bogus daemon response, type %d",
1736 fork_proc_resp.type);
1737 g_assert_not_reached ();
1740 return(FALSE);
1743 gboolean
1744 _wapi_handle_process_kill (pid_t process, guint32 signo, gint *errnum)
1746 WapiHandleRequest killproc = {0};
1747 WapiHandleResponse killprocresp = {0};
1748 gint result;
1750 if (shared != TRUE) {
1751 if (errnum) *errnum = EINVAL;
1752 return FALSE;
1755 killproc.type = WapiHandleRequestType_ProcessKill;
1756 killproc.u.process_kill.pid = process;
1757 killproc.u.process_kill.signo = signo;
1759 _wapi_daemon_request_response (daemon_sock, &killproc, &killprocresp);
1761 if (killprocresp.type != WapiHandleResponseType_ProcessKill) {
1762 g_warning (G_GNUC_PRETTY_FUNCTION
1763 ": bogus daemon response, type %d",
1764 killprocresp.type);
1765 g_assert_not_reached ();
1768 result = killprocresp.u.process_kill.err;
1769 if (result != 0 && errnum != NULL)
1770 *errnum = (result == FALSE) ? result : 0;
1772 return (result == 0);
1775 gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode,
1776 guint32 new_sharemode,
1777 guint32 new_access,
1778 guint32 *old_sharemode,
1779 guint32 *old_access)
1781 WapiHandleRequest req = {0};
1782 WapiHandleResponse resp = {0};
1784 if(shared != TRUE) {
1785 /* No daemon means we don't know if a file is sharable.
1786 * We're running in our own little world if this is
1787 * the case, so there's no point in pretending that
1788 * the file isn't sharable.
1790 return(FALSE);
1793 req.type = WapiHandleRequestType_GetOrSetShare;
1794 req.u.get_or_set_share.device = device;
1795 req.u.get_or_set_share.inode = inode;
1796 req.u.get_or_set_share.new_sharemode = new_sharemode;
1797 req.u.get_or_set_share.new_access = new_access;
1799 _wapi_daemon_request_response (daemon_sock, &req, &resp);
1800 if (resp.type != WapiHandleResponseType_GetOrSetShare) {
1801 g_warning (G_GNUC_PRETTY_FUNCTION
1802 ": bogus daemon response, type %d", resp.type);
1803 g_assert_not_reached ();
1806 *old_sharemode = resp.u.get_or_set_share.sharemode;
1807 *old_access = resp.u.get_or_set_share.access;
1809 return(resp.u.get_or_set_share.exists);
1812 void _wapi_handle_set_share (dev_t device, ino_t inode, guint32 sharemode,
1813 guint32 access)
1815 WapiHandleRequest req = {0};
1816 WapiHandleResponse resp = {0};
1818 if(shared != TRUE) {
1819 /* No daemon, so there's no one else to tell about
1820 * file sharing.
1822 return;
1825 req.type = WapiHandleRequestType_SetShare;
1826 req.u.set_share.device = device;
1827 req.u.set_share.inode = inode;
1828 req.u.set_share.sharemode = sharemode;
1829 req.u.set_share.access = access;
1831 _wapi_daemon_request_response (daemon_sock, &req, &resp);
1832 if (resp.type != WapiHandleResponseType_SetShare) {
1833 g_warning (G_GNUC_PRETTY_FUNCTION
1834 ": bogus daemon response, type %d", resp.type);
1835 g_assert_not_reached ();