X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Fhandles.c;h=25635718c1fc1f18bacd9d94666d10de635641ff;hb=3cf809f80ed339af9e08bd3fbae6538988ae5f3b;hp=6e2896ae4a8812b9688bea3fd689da7ad865c0f6;hpb=12e84038d657bc96d596a4f91f00d404f1b34e10;p=mono.git diff --git a/mono/io-layer/handles.c b/mono/io-layer/handles.c index 6e2896ae4a8..25635718c1f 100644 --- a/mono/io-layer/handles.c +++ b/mono/io-layer/handles.c @@ -12,26 +12,30 @@ #include #include #include +#include #include #include #include +#include +#include +#include -#if HAVE_BOEHM_GC -#include -#endif +#include #include #include #include #include -#include #include -#include +#include +#include #undef DEBUG +#undef DEBUG_REFS + +static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer); static WapiHandleCapability handle_caps[WAPI_HANDLE_COUNT]={0}; -static gboolean shared=FALSE; static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={ NULL, &_wapi_file_ops, @@ -43,604 +47,1031 @@ static struct _WapiHandleOps *handle_ops[WAPI_HANDLE_COUNT]={ &_wapi_socket_ops, &_wapi_find_ops, &_wapi_process_ops, + &_wapi_pipe_ops, + &_wapi_namedmutex_ops, }; -static int daemon_sock; +static void _wapi_shared_details (gpointer handle_info); -static pthread_mutexattr_t mutex_shared_attr; -static pthread_condattr_t cond_shared_attr; +static void (*handle_details[WAPI_HANDLE_COUNT])(gpointer) = { + NULL, + _wapi_file_details, + _wapi_console_details, + _wapi_shared_details, /* thread */ + _wapi_sem_details, + _wapi_mutex_details, + _wapi_event_details, + NULL, /* Nothing useful to see in a socket handle */ + NULL, /* Nothing useful to see in a find handle */ + _wapi_shared_details, /* process */ + _wapi_pipe_details, + _wapi_shared_details, /* namedmutex */ +}; -struct _WapiHandleShared_list *_wapi_shared_data=NULL; -struct _WapiHandlePrivate_list *_wapi_private_data=NULL; +const char *_wapi_handle_typename[] = { + "Unused", + "File", + "Console", + "Thread", + "Sem", + "Mutex", + "Event", + "Socket", + "Find", + "Process", + "Pipe", + "N.Mutex", + "Error!!" +}; -int disable_shm = 0; +/* + * We can hold _WAPI_PRIVATE_MAX_SLOTS * _WAPI_HANDLE_INITIAL_COUNT handles. + * If 4M handles are not enough... Oh, well... we will crash. + */ +#define SLOT_INDEX(x) (x / _WAPI_HANDLE_INITIAL_COUNT) +#define SLOT_OFFSET(x) (x % _WAPI_HANDLE_INITIAL_COUNT) + +struct _WapiHandleUnshared *_wapi_private_handles [_WAPI_PRIVATE_MAX_SLOTS]; +static guint32 _wapi_private_handle_count = 0; + +struct _WapiHandleSharedLayout *_wapi_shared_layout = NULL; +struct _WapiFileShareLayout *_wapi_fileshare_layout = NULL; + +guint32 _wapi_fd_reserve; + +mono_mutex_t _wapi_global_signal_mutex; +pthread_cond_t _wapi_global_signal_cond; + +int _wapi_sem_id; + +static mono_mutex_t scan_mutex = MONO_MUTEX_INITIALIZER; + +static mono_once_t shared_init_once = MONO_ONCE_INIT; static void shared_init (void) { - struct sockaddr_un shared_socket_address; - int ret; + int thr_ret; + int idx = 0; + + _wapi_fd_reserve = getdtablesize(); + + do { + _wapi_private_handles [idx++] = g_new0 (struct _WapiHandleUnshared, + _WAPI_HANDLE_INITIAL_COUNT); - if (getenv ("MONO_ENABLE_SHM")) - disable_shm = 0; + _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT; + } while(_wapi_fd_reserve > _wapi_private_handle_count); -#ifndef DISABLE_SHARED_HANDLES - if(getenv ("MONO_DISABLE_SHM") || disable_shm) -#endif - { - shared=FALSE; -#ifndef DISABLE_SHARED_HANDLES - } else { - int shm_id; - - _wapi_shared_data=_wapi_shm_attach (FALSE, &shared, &shm_id); - if(shared==FALSE) { - g_warning ("Failed to attach shared memory! " - "(tried shared memory ID 0x%x). " - "Falling back to non-shared handles", - shm_id); + _wapi_shared_layout = _wapi_shm_attach (WAPI_SHM_DATA); + g_assert (_wapi_shared_layout != NULL); + + _wapi_shm_semaphores_init (); + + _wapi_fileshare_layout = _wapi_shm_attach (WAPI_SHM_FILESHARE); + g_assert (_wapi_fileshare_layout != NULL); + + _wapi_collection_init (); + + thr_ret = pthread_cond_init(&_wapi_global_signal_cond, NULL); + g_assert (thr_ret == 0); + + thr_ret = mono_mutex_init(&_wapi_global_signal_mutex, NULL); + g_assert (thr_ret == 0); +} + +static void _wapi_handle_init_shared_metadata (struct _WapiHandleSharedMetadata *meta) +{ + meta->timestamp = (guint32)(time (NULL) & 0xFFFFFFFF); + meta->signalled = FALSE; +} + +static void _wapi_handle_init_shared (struct _WapiHandleShared *handle, + WapiHandleType type, + gpointer handle_specific) +{ + handle->type = type; + handle->stale = FALSE; + + if (handle_specific != NULL) { + memcpy (&handle->u, handle_specific, sizeof (handle->u)); + } +} + +static void _wapi_handle_init (struct _WapiHandleUnshared *handle, + WapiHandleType type, gpointer handle_specific) +{ + int thr_ret; + + handle->type = type; + handle->signalled = FALSE; + handle->ref = 1; + + if (!_WAPI_SHARED_HANDLE(type)) { + thr_ret = pthread_cond_init (&handle->signal_cond, NULL); + g_assert (thr_ret == 0); + + thr_ret = mono_mutex_init (&handle->signal_mutex, NULL); + g_assert (thr_ret == 0); + + if (handle_specific != NULL) { + memcpy (&handle->u, handle_specific, + sizeof (handle->u)); } -#endif /* DISABLE_SHARED_HANDLES */ - } - - - if(shared==TRUE) { - daemon_sock=socket (PF_UNIX, SOCK_STREAM, 0); - shared_socket_address.sun_family=AF_UNIX; - memcpy (shared_socket_address.sun_path, _wapi_shared_data->daemon, 108); - ret=connect (daemon_sock, (struct sockaddr *)&shared_socket_address, - sizeof(struct sockaddr_un)); - if(ret==-1) { - g_warning (G_GNUC_PRETTY_FUNCTION - "connect to daemon failed: %s", - strerror (errno)); - /* Fall back to private handles */ - shared=FALSE; + } +} + +static guint32 _wapi_handle_new_shared_offset (guint32 offset) +{ + guint32 i; + static guint32 last = 1; + +again: + /* FIXME: expandable array */ + /* leave a few slots at the end so that there's always space + * to move a handle. (We leave the space in the offset table + * too, so we don't have to keep track of inter-segment + * offsets.) + */ + for(i = last; i <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM; i++) { + struct _WapiHandleSharedMetadata *meta = &_wapi_shared_layout->metadata[i]; + + if(meta->offset == 0) { + if (InterlockedCompareExchange (&meta->offset, offset, + 0) == 0) { + last = i + 1; + + _wapi_handle_init_shared_metadata (meta); + return(i); + } else { + /* Someone else beat us to it, just + * continue looking + */ + } } } - if(shared==FALSE) { -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Using process-private handles"); -#endif - _wapi_shared_data= - g_malloc0 (sizeof(struct _WapiHandleShared_list)+ - _WAPI_SHM_SCRATCH_SIZE); + if(last > 1) { + /* Try again from the beginning */ + last = 1; + goto again; } - _wapi_private_data=g_new0 (struct _WapiHandlePrivate_list, 1); - pthread_mutexattr_init (&mutex_shared_attr); - pthread_condattr_init (&cond_shared_attr); + /* Will need to expand the array. The caller will sort it out */ -#ifdef _POSIX_THREAD_PROCESS_SHARED - pthread_mutexattr_setpshared (&mutex_shared_attr, - PTHREAD_PROCESS_SHARED); - pthread_condattr_setpshared (&cond_shared_attr, - PTHREAD_PROCESS_SHARED); -#endif + return(0); } -void _wapi_handle_init (void) +static guint32 _wapi_handle_new_shared (WapiHandleType type, + gpointer handle_specific) { - /* Create our own process and main thread handles (assume this - * function is being called from the main thread) + guint32 offset; + static guint32 last = 1; + + /* The shared memory holds an offset to the real data, so we + * can update the handle RCU-style without taking a lock. + * This function just allocates the next available data slot, + * use _wapi_handle_new_shared_offset to get the offset entry. */ - /* No need to make these variables visible outside this - * function, the handles will be cleaned up at process exit. + + /* Leave the first slot empty as a guard */ +again: + /* FIXME: expandable array */ + /* Leave a few slots at the end so that there's always space + * to move a handle */ - gpointer process_handle; - gpointer thread_handle; + for(offset = last; offset <_WAPI_HANDLE_INITIAL_COUNT - _WAPI_HEADROOM; + offset++) { + struct _WapiHandleShared *handle = &_wapi_shared_layout->handles[offset]; + + if(handle->type == WAPI_HANDLE_UNUSED) { + if (InterlockedCompareExchange ((gint32 *)&handle->type, type, WAPI_HANDLE_UNUSED) == WAPI_HANDLE_UNUSED) { + last = offset + 1; + + _wapi_handle_init_shared (handle, type, + handle_specific); + return(offset); + } else { + /* Someone else beat us to it, just + * continue looking + */ + } + } + } - process_handle=_wapi_handle_new (WAPI_HANDLE_PROCESS); - - thread_handle=_wapi_handle_new (WAPI_HANDLE_THREAD); + if(last > 1) { + /* Try again from the beginning */ + last = 1; + goto again; + } + + /* Will need to expand the array. The caller will sort it out */ + + return(0); } -guint32 _wapi_handle_new_internal (WapiHandleType type) +/* + * _wapi_handle_new_internal: + * @type: Init handle to this type + * + * Search for a free handle and initialize it. Return the handle on + * success and 0 on failure. This is only called from + * _wapi_handle_new, and scan_mutex must be held. + */ +static guint32 _wapi_handle_new_internal (WapiHandleType type, + gpointer handle_specific) { - guint32 i; - static guint32 last=1; + guint32 i, k, count; + static guint32 last = 0; + gboolean retry = FALSE; /* A linear scan should be fast enough. Start from the last * allocation, assuming that handles are allocated more often - * than they're freed. Leave 0 (NULL) as a guard + * than they're freed. Leave the space reserved for file + * descriptors */ + + if (last < _wapi_fd_reserve) { + last = _wapi_fd_reserve; + } else { + retry = TRUE; + } + again: - for(i=last; i<_WAPI_MAX_HANDLES; i++) { - struct _WapiHandleShared *shared=&_wapi_shared_data->handles[i]; - - if(shared->type==WAPI_HANDLE_UNUSED) { - last=i; - shared->type=type; - shared->signalled=FALSE; - mono_mutex_init (&_wapi_shared_data->handles[i].signal_mutex, &mutex_shared_attr); - pthread_cond_init (&_wapi_shared_data->handles[i].signal_cond, &cond_shared_attr); + count = last; + for(i = SLOT_INDEX (count); _wapi_private_handles [i] != NULL; i++) { + for (k = SLOT_OFFSET (count); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { + struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k]; - return(i); + if(handle->type == WAPI_HANDLE_UNUSED) { + last = count + 1; + + _wapi_handle_init (handle, type, handle_specific); + return (count); + } + count++; } } - if(last>1) { + if(retry && last > _wapi_fd_reserve) { /* Try again from the beginning */ - last=1; + last = _wapi_fd_reserve; goto again; } - + + /* Will need to expand the array. The caller will sort it out */ return(0); } -gpointer _wapi_handle_new (WapiHandleType type) +gpointer _wapi_handle_new (WapiHandleType type, gpointer handle_specific) { - static mono_once_t shared_init_once = MONO_ONCE_INIT; - static pthread_mutex_t scan_mutex=PTHREAD_MUTEX_INITIALIZER; - guint32 idx; + guint32 handle_idx = 0; gpointer handle; - WapiHandleRequest new; - WapiHandleResponse new_resp; -#if HAVE_BOEHM_GC - gboolean tried_collect=FALSE; -#endif + int thr_ret; mono_once (&shared_init_once, shared_init); -again: - if(shared==TRUE) { - new.type=WapiHandleRequestType_New; - new.u.new.type=type; +#ifdef DEBUG + g_message ("%s: Creating new handle of type %s", __func__, + _wapi_handle_typename[type]); +#endif + + g_assert(!_WAPI_FD_HANDLE(type)); - _wapi_daemon_request_response (daemon_sock, &new, &new_resp); + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + g_assert (thr_ret == 0); + + while ((handle_idx = _wapi_handle_new_internal (type, handle_specific)) == 0) { + /* Try and expand the array, and have another go */ + int idx = SLOT_INDEX (_wapi_private_handle_count); + _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared, + _WAPI_HANDLE_INITIAL_COUNT); + + _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT; + } + + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + /* Make sure we left the space for fd mappings */ + g_assert (handle_idx >= _wapi_fd_reserve); - if (new_resp.type==WapiHandleResponseType_New) { - idx=new_resp.u.new.handle; - } else { - g_warning (G_GNUC_PRETTY_FUNCTION - ": bogus daemon response, type %d", - new_resp.type); - g_assert_not_reached (); + handle = GUINT_TO_POINTER (handle_idx); + +#ifdef DEBUG + g_message ("%s: Allocated new handle %p", __func__, handle); +#endif + + if (_WAPI_SHARED_HANDLE(type)) { + /* Add the shared section too */ + guint32 offset, ref; + + offset = _wapi_handle_new_shared (type, handle_specific); + if (offset == 0) { + _wapi_handle_collect (); + offset = _wapi_handle_new_shared (type, + handle_specific); + if (offset == 0) { + /* FIXME: grow the arrays */ + return (_WAPI_HANDLE_INVALID); + } } - } else { - pthread_mutex_lock (&scan_mutex); - idx=_wapi_handle_new_internal (type); - pthread_mutex_unlock (&scan_mutex); + + ref = _wapi_handle_new_shared_offset (offset); + if (ref == 0) { + _wapi_handle_collect (); + ref = _wapi_handle_new_shared_offset (offset); + + if (ref == 0) { + /* FIXME: grow the arrays */ + return (_WAPI_HANDLE_INVALID); + } + } + + _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = ref; +#ifdef DEBUG + g_message ("%s: New shared handle at offset 0x%x", __func__, + ref); +#endif } + + return(handle); +} + +gpointer _wapi_handle_new_from_offset (WapiHandleType type, guint32 offset) +{ + guint32 handle_idx = 0; + gpointer handle; + int thr_ret, i, k; + + mono_once (&shared_init_once, shared_init); + +#ifdef DEBUG + g_message ("%s: Creating new handle of type %s to offset %d", __func__, + _wapi_handle_typename[type], offset); +#endif + + g_assert(!_WAPI_FD_HANDLE(type)); + g_assert(_WAPI_SHARED_HANDLE(type)); + g_assert(offset != 0); + + for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { + for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { + struct _WapiHandleUnshared *handle_data = &_wapi_private_handles [i][k]; - if(idx==0) { - g_warning (G_GNUC_PRETTY_FUNCTION ": Ran out of handles!"); + if (handle_data->type == type && + handle_data->u.shared.offset == offset) { + handle = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k); + _wapi_handle_ref (handle); -#if HAVE_BOEHM_GC - /* See if we can reclaim some handles by forcing a GC - * collection - */ - if(tried_collect==FALSE) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": Seeing if GC collection helps..."); - GC_gcollect (); - tried_collect=TRUE; - goto again; - } else { - g_warning (G_GNUC_PRETTY_FUNCTION - ": didn't help, returning error"); +#ifdef DEBUG + g_message ("%s: Returning old handle %p referencing 0x%x", __func__, handle, offset); +#endif + return (handle); + } } + } + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + g_assert (thr_ret == 0); + + while ((handle_idx = _wapi_handle_new_internal (type, NULL)) == 0) { + /* Try and expand the array, and have another go */ + int idx = SLOT_INDEX (_wapi_private_handle_count); + _wapi_private_handles [idx] = g_new0 (struct _WapiHandleUnshared, + _WAPI_HANDLE_INITIAL_COUNT); + + _wapi_private_handle_count += _WAPI_HANDLE_INITIAL_COUNT; + } + + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); + + /* Make sure we left the space for fd mappings */ + g_assert (handle_idx >= _wapi_fd_reserve); + + handle = GUINT_TO_POINTER (handle_idx); + + _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset = offset; + +#ifdef DEBUG + g_message ("%s: Allocated new handle %p referencing 0x%x", __func__, + handle, offset); +#endif + + return(handle); +} + +gpointer _wapi_handle_new_fd (WapiHandleType type, int fd, + gpointer handle_specific) +{ + struct _WapiHandleUnshared *handle; + + mono_once (&shared_init_once, shared_init); + +#ifdef DEBUG + g_message ("%s: Creating new handle of type %s", __func__, + _wapi_handle_typename[type]); #endif + g_assert(_WAPI_FD_HANDLE(type)); + g_assert(!_WAPI_SHARED_HANDLE(type)); + + if (fd >= _wapi_fd_reserve) { +#ifdef DEBUG + g_message ("%s: fd %d is too big", __func__, fd); +#endif + return(GUINT_TO_POINTER (_WAPI_HANDLE_INVALID)); } - handle=GUINT_TO_POINTER (idx); + handle = &_WAPI_PRIVATE_HANDLES(fd); + + if (handle->type != WAPI_HANDLE_UNUSED) { +#ifdef DEBUG + g_message ("%s: fd %d is already in use!", __func__, fd); +#endif + /* FIXME: clean up this handle? We can't do anything + * with the fd, cos thats the new one + */ + } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Allocated new handle %p", handle); + g_message ("%s: Assigning new fd handle %d", __func__, fd); #endif - return(handle); + _wapi_handle_init (handle, type, handle_specific); + + return(GUINT_TO_POINTER(fd)); } gboolean _wapi_lookup_handle (gpointer handle, WapiHandleType type, - gpointer *shared, gpointer *private) + gpointer *handle_specific) { - struct _WapiHandleShared *shared_handle_data; - struct _WapiHandlePrivate *private_handle_data; - guint32 idx=GPOINTER_TO_UINT (handle); + struct _WapiHandleUnshared *handle_data; + guint32 handle_idx = GPOINTER_TO_UINT(handle); - if(shared!=NULL) { - shared_handle_data=&_wapi_shared_data->handles[idx]; - /* Allow WAPI_HANDLE_UNUSED to mean "dont care which - * type" - */ - if(shared_handle_data->type!=type && - type != WAPI_HANDLE_UNUSED) { - return(FALSE); - } + handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx); + + if (handle_data->type != type) { + return(FALSE); + } - *shared=&shared_handle_data->u; + if (handle_specific == NULL) { + return(FALSE); } - if(private!=NULL) { - private_handle_data=&_wapi_private_data->handles[idx]; + if (_WAPI_SHARED_HANDLE(type)) { + struct _WapiHandle_shared_ref *ref; + struct _WapiHandleShared *shared_handle_data; + struct _WapiHandleSharedMetadata *shared_meta; + guint32 offset; + + /* Unsafe, because we don't want the handle to vanish + * while we're checking it + */ + _WAPI_HANDLE_COLLECTION_UNSAFE; + + do { + ref = &handle_data->u.shared; + shared_meta = &_wapi_shared_layout->metadata[ref->offset]; + offset = shared_meta->offset; + shared_handle_data = &_wapi_shared_layout->handles[offset]; + + g_assert(shared_handle_data->type == type); - *private=&private_handle_data->u; + *handle_specific = &shared_handle_data->u; + } while (offset != shared_meta->offset); + + _WAPI_HANDLE_COLLECTION_SAFE; + } else { + *handle_specific = &handle_data->u; } return(TRUE); } -void _wapi_handle_ref (gpointer handle) +gboolean _wapi_copy_handle (gpointer handle, WapiHandleType type, + struct _WapiHandleShared *handle_specific) { - guint32 idx=GPOINTER_TO_UINT (handle); + struct _WapiHandleUnshared *handle_data; + guint32 handle_idx = GPOINTER_TO_UINT(handle); + struct _WapiHandle_shared_ref *ref; + struct _WapiHandleShared *shared_handle_data; + struct _WapiHandleSharedMetadata *shared_meta; + guint32 offset; + + g_assert(_WAPI_SHARED_HANDLE(type)); + +#ifdef DEBUG + g_message ("%s: copying handle %p type %s", __func__, handle, + _wapi_handle_typename[type]); +#endif - if(shared==TRUE) { - WapiHandleRequest req; - WapiHandleResponse resp; + handle_data = &_WAPI_PRIVATE_HANDLES(handle_idx); - req.type=WapiHandleRequestType_Open; - req.u.open.handle=idx; + if(handle_data->type != type) { +#ifdef DEBUG + g_message ("%s: incorrect type, %p has type %s", __func__, + handle, _wapi_handle_typename[handle_data->type]); +#endif + + return(FALSE); + } + + if(handle_specific == NULL) { +#ifdef DEBUG + g_message ("%s: Nowhere to store data", __func__); +#endif + + return(FALSE); + } - _wapi_daemon_request_response (daemon_sock, &req, &resp); - if(resp.type!=WapiHandleResponseType_Open) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": bogus daemon response, type %d", - resp.type); - g_assert_not_reached (); - } - } else { - _wapi_shared_data->handles[idx].ref++; + do { + ref = &handle_data->u.shared; + shared_meta = &_wapi_shared_layout->metadata[ref->offset]; + offset = shared_meta->offset; + shared_handle_data = &_wapi_shared_layout->handles[offset]; + + g_assert(shared_handle_data->type == type); + + memcpy(handle_specific, shared_handle_data, + sizeof(struct _WapiHandleShared)); + } while (offset != shared_meta->offset); #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", - handle, _wapi_shared_data->handles[idx].ref); + g_message ("%s: OK", __func__); #endif - } + + return(TRUE); } -void _wapi_handle_unref (gpointer handle) +gboolean _wapi_replace_handle (gpointer handle, WapiHandleType type, + struct _WapiHandleShared *handle_specific) { - guint32 idx=GPOINTER_TO_UINT (handle); - gboolean destroy; + struct _WapiHandleShared *shared_handle_data; + struct _WapiHandleSharedMetadata *shared_meta; + guint32 handle_idx = GPOINTER_TO_UINT(handle); + guint32 old_off, new_off, ref; - if(shared==TRUE) { - WapiHandleRequest req; - WapiHandleResponse resp; +#ifdef DEBUG + g_message ("%s: Replacing handle %p of type %s", __func__, handle, + _wapi_handle_typename[type]); +#endif + + g_assert(_WAPI_SHARED_HANDLE(type)); + g_assert(_WAPI_PRIVATE_HANDLES(handle_idx).type == type); - req.type=WapiHandleRequestType_Close; - req.u.close.handle=GPOINTER_TO_UINT (handle); + ref = _WAPI_PRIVATE_HANDLES(handle_idx).u.shared.offset; + shared_meta = &_wapi_shared_layout->metadata[ref]; - _wapi_daemon_request_response (daemon_sock, &req, &resp); - if(resp.type!=WapiHandleResponseType_Close) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": bogus daemon response, type %d", - resp.type); - g_assert_not_reached (); - } else { - destroy=resp.u.close.destroy; + do { + old_off = shared_meta->offset; + new_off = _wapi_handle_new_shared (type, handle_specific); + if (new_off == 0) { + _wapi_handle_collect (); + new_off = _wapi_handle_new_shared (type, + handle_specific); + + if (new_off == 0) { + /* FIXME: grow the arrays */ + return (FALSE); + } } - } else { - _wapi_shared_data->handles[idx].ref--; - -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": handle %p ref now %d", - handle, _wapi_shared_data->handles[idx].ref); -#endif + + shared_handle_data = &_wapi_shared_layout->handles[new_off]; + + memcpy (shared_handle_data, handle_specific, + sizeof(struct _WapiHandleShared)); - /* Possible race condition here if another thread refs - * the handle between here and setting the type to - * UNUSED. I could lock a mutex, but I'm not sure - * that allowing a handle reference to reach 0 isn't - * an application bug anyway. + /* An entry can't become fresh again (its going to be + * collected eventually), so no need for atomic ops + * here. */ - destroy=(_wapi_shared_data->handles[idx].ref==0); - } + _wapi_shared_layout->handles[old_off].stale = TRUE; + } while(InterlockedCompareExchange (&shared_meta->offset, new_off, + old_off) != old_off); - if(destroy==TRUE) { #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Destroying handle %p", - handle); + g_message ("%s: handle at 0x%x is now found at 0x%x", __func__, ref, + new_off); #endif + + return (TRUE); +} + +void +_wapi_handle_foreach (WapiHandleType type, + gboolean (*on_each)(gpointer test, gpointer user), + gpointer user_data) +{ + struct _WapiHandleUnshared *handle_data = NULL; + gpointer ret = NULL; + guint32 i, k; + int thr_ret; + + pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup, + (void *)&scan_mutex); + thr_ret = mono_mutex_lock (&scan_mutex); + g_assert (thr_ret == 0); + + for (i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { + for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { + handle_data = &_wapi_private_handles [i][k]; - if(shared==FALSE) { - _wapi_handle_ops_close_shared (handle); - _wapi_shared_data->handles[idx].type=WAPI_HANDLE_UNUSED; - mono_mutex_destroy (&_wapi_shared_data->handles[idx].signal_mutex); - pthread_cond_destroy (&_wapi_shared_data->handles[idx].signal_cond); - memset (&_wapi_shared_data->handles[idx].u, '\0', sizeof(_wapi_shared_data->handles[idx].u)); + if (handle_data->type == type) { + ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k); + if (on_each (ret, user_data) == TRUE) + break; + } } - - _wapi_handle_ops_close_private (handle); } + + thr_ret = mono_mutex_unlock (&scan_mutex); + g_assert (thr_ret == 0); + pthread_cleanup_pop (0); } -#define HDRSIZE sizeof(struct _WapiScratchHeader) +/* This might list some shared handles twice if they are already + * opened by this process, and the check function returns FALSE the + * first time. Shared handles that are created during the search are + * unreffed if the check function returns FALSE, so callers must not + * rely on the handle persisting (unless the check function returns + * TRUE) + */ +gpointer _wapi_search_handle (WapiHandleType type, + gboolean (*check)(gpointer test, gpointer user), + gpointer user_data, + gpointer *handle_specific) +{ + struct _WapiHandleUnshared *handle_data = NULL; + gpointer ret = NULL; + guint32 i, k; + gboolean found = FALSE; + + + for (i = SLOT_INDEX (0); !found && _wapi_private_handles [i] != NULL; i++) { + for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { + handle_data = &_wapi_private_handles [i][k]; + + if (handle_data->type == type) { + ret = GUINT_TO_POINTER (i * _WAPI_HANDLE_INITIAL_COUNT + k); + if (check (ret, user_data) == TRUE) { + found = TRUE; + break; + } + } + } + } + + if (!found && _WAPI_SHARED_HANDLE (type)) { + /* Not found yet, so search the shared memory too */ +#ifdef DEBUG + g_message ("%s: Looking at other shared handles...", __func__); +#endif + + for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { + struct _WapiHandleShared *shared; + struct _WapiHandleSharedMetadata *meta; + WapiHandleType shared_type; -guint32 _wapi_handle_scratch_store_internal (guint32 bytes) + _WAPI_HANDLE_COLLECTION_UNSAFE; + + meta = &_wapi_shared_layout->metadata[i]; + shared = &_wapi_shared_layout->handles[meta->offset]; + shared_type = shared->type; + + _WAPI_HANDLE_COLLECTION_SAFE; + + if (shared_type == type) { + ret = _wapi_handle_new_from_offset (type, i); + +#ifdef DEBUG + g_message ("%s: Opened tmp handle %p (type %s) from offset %d", __func__, ret, _wapi_handle_typename[type], meta->offset); +#endif + + if (check (ret, user_data) == TRUE) { + found = TRUE; + handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(ret)); + + break; + } + + /* This isn't the handle we're looking + * for, so drop the reference we took + * in _wapi_handle_new_from_offset () + */ + _wapi_handle_unref (ret); + } + } + } + + if (!found) { + goto done; + } + + if(handle_specific != NULL) { + if (_WAPI_SHARED_HANDLE(type)) { + struct _WapiHandle_shared_ref *ref ; + struct _WapiHandleShared *shared_handle_data; + struct _WapiHandleSharedMetadata *shared_meta; + guint32 offset, now; + + /* Unsafe, because we don't want the handle to + * vanish while we're checking it + */ + _WAPI_HANDLE_COLLECTION_UNSAFE; + + do { + ref = &handle_data->u.shared; + shared_meta = &_wapi_shared_layout->metadata[ref->offset]; + offset = shared_meta->offset; + shared_handle_data = &_wapi_shared_layout->handles[offset]; + + g_assert(shared_handle_data->type == type); + + *handle_specific = &shared_handle_data->u; + } while (offset != shared_meta->offset); + + /* Make sure this handle doesn't vanish in the + * next collection + */ + now = (guint32)(time (NULL) & 0xFFFFFFFF); + InterlockedExchange (&shared_meta->timestamp, now); + + _WAPI_HANDLE_COLLECTION_SAFE; + } else { + *handle_specific = &handle_data->u; + } + } + +done: + return(ret); +} + +/* Returns the offset of the metadata array, or -1 on error, or 0 for + * not found (0 is not a valid offset) + */ +gint32 _wapi_search_handle_namespace (WapiHandleType type, + gchar *utf8_name) { - guint32 idx=0, last_idx=0; - struct _WapiScratchHeader *hdr, *last_hdr; - gboolean last_was_free=FALSE; - guchar *storage=&_wapi_shared_data->scratch_base[0]; + struct _WapiHandleShared *shared_handle_data; + struct _WapiHandleSharedMetadata *shared_meta; + guint32 i; + gint32 ret = 0; + + g_assert(_WAPI_SHARED_HANDLE(type)); #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": looking for %d bytes of scratch space (%d bytes total)", - bytes, _WAPI_SHM_SCRATCH_SIZE); + g_message ("%s: Lookup for handle named [%s] type %s", __func__, + utf8_name, _wapi_handle_typename[type]); #endif - hdr=(struct _WapiScratchHeader *)&storage[0]; - if(hdr->flags==0 && hdr->length==0) { - /* Need to initialise scratch data */ - hdr->flags |= WAPI_SHM_SCRATCH_FREE; - hdr->length = _WAPI_SHM_SCRATCH_SIZE - HDRSIZE; - } + _WAPI_HANDLE_COLLECTION_UNSAFE; - while(idx< _WAPI_SHM_SCRATCH_SIZE) { - hdr=(struct _WapiScratchHeader *)&storage[idx]; + for(i = 1; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { + WapiSharedNamespace *sharedns; - /* Do a simple first-fit allocation, coalescing - * adjacent free blocks as we progress through the - * scratch space - */ - if(hdr->flags & WAPI_SHM_SCRATCH_FREE && - hdr->length >= bytes + HDRSIZE) { - /* found space */ - guint32 old_length=hdr->length; -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": found suitable free size at %d, length %d", idx, hdr->length); -#endif + shared_meta = &_wapi_shared_layout->metadata[i]; + shared_handle_data = &_wapi_shared_layout->handles[shared_meta->offset]; - hdr->flags &= ~WAPI_SHM_SCRATCH_FREE; - hdr->length=bytes; - idx += HDRSIZE; - - /* Put a new header in at the end of the used - * space - */ - hdr=(struct _WapiScratchHeader *)&storage[idx+bytes]; - hdr->flags |= WAPI_SHM_SCRATCH_FREE; - hdr->length = old_length-bytes-HDRSIZE; + /* Check mutex, event, semaphore, timer, job and file-mapping + * object names. So far only mutex is implemented. + */ + if (!_WAPI_SHARED_NAMESPACE (shared_handle_data->type)) { + continue; + } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": new header at %d, length %d", idx+bytes, hdr->length); + g_message ("%s: found a shared namespace handle at 0x%x (type %s)", __func__, i, _wapi_handle_typename[shared_handle_data->type]); #endif + + sharedns=(WapiSharedNamespace *)&shared_handle_data->u; - return(idx); - } else if(hdr->flags & WAPI_SHM_SCRATCH_FREE && - last_was_free == FALSE) { #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous used)", idx, hdr->length); + g_message ("%s: name is [%s]", __func__, sharedns->name); #endif - /* save this point in case we can coalesce it with - * the next block, if that is free. - */ - last_was_free=TRUE; - last_idx=idx; - last_hdr=hdr; - idx+=(hdr->length+HDRSIZE); - } else if (hdr->flags & WAPI_SHM_SCRATCH_FREE && - last_was_free == TRUE) { + if (strcmp (sharedns->name, utf8_name) == 0) { + if (shared_handle_data->type != type) { + /* Its the wrong type, so fail now */ #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": found too-small free block at %d, length %d (previous free)", idx, hdr->length); + g_message ("%s: handle 0x%x matches name but is wrong type: %s", __func__, i, _wapi_handle_typename[shared_handle_data->type]); #endif - - /* This block and the previous are both free, - * so coalesce them - */ - last_hdr->length += (hdr->length + HDRSIZE); - - /* If the new block is now big enough, use it - * (next time round the loop) - */ - if(last_hdr->length >= bytes + HDRSIZE) { - idx=last_idx; + ret = -1; + goto done; } else { - /* leave the last free info as it is, - * in case the next block is also free - * and can be coalesced too - */ - idx=last_idx+last_hdr->length+HDRSIZE; - } - } else { #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": found used block at %d, length %d", idx, - hdr->length); + g_message ("%s: handle 0x%x matches name and type", __func__, i); #endif - - /* must be used, try next chunk */ - idx+=(hdr->length+HDRSIZE); - - /* Don't let the coalescing blow away this block */ - last_was_free=FALSE; + ret = i; + goto done; + } } } + +done: + _WAPI_HANDLE_COLLECTION_SAFE; - return(0); + return(ret); } -guint32 _wapi_handle_scratch_store (gconstpointer data, guint32 bytes) +void _wapi_handle_ref (gpointer handle) { - static pthread_mutex_t scratch_mutex=PTHREAD_MUTEX_INITIALIZER; - guint32 idx; + guint32 idx = GPOINTER_TO_UINT(handle); + guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); + struct _WapiHandleUnshared *handle_data = &_WAPI_PRIVATE_HANDLES(idx); - /* No point storing no data */ - if(bytes==0) { - return(0); - } - - if(shared==TRUE) { - WapiHandleRequest scratch; - WapiHandleResponse scratch_resp; - - scratch.type=WapiHandleRequestType_Scratch; - scratch.u.scratch.length=bytes; - - _wapi_daemon_request_response (daemon_sock, &scratch, - &scratch_resp); - - if(scratch_resp.type==WapiHandleResponseType_Scratch) { - idx=scratch_resp.u.scratch.idx; - } else { - g_warning (G_GNUC_PRETTY_FUNCTION - ": bogus daemon response, type %d", - scratch_resp.type); - g_assert_not_reached (); - } - } else { - pthread_mutex_lock (&scratch_mutex); - idx=_wapi_handle_scratch_store_internal (bytes); - pthread_mutex_unlock (&scratch_mutex); + InterlockedIncrement (&handle_data->ref); + + /* It's possible for processes to exit before getting around + * to updating timestamps in the collection thread, so if a + * shared handle is reffed do the timestamp here as well just + * to make sure. + */ + if (_WAPI_SHARED_HANDLE(handle_data->type)) { + struct _WapiHandleSharedMetadata *shared_meta = &_wapi_shared_layout->metadata[handle_data->u.shared.offset]; - if(idx==0) { - /* Failed to allocate space */ - return(0); - } + InterlockedExchange (&shared_meta->timestamp, now); } - - memcpy (&_wapi_shared_data->scratch_base[idx], data, bytes); - return(idx); +#ifdef DEBUG_REFS + g_message ("%s: handle %p ref now %d", __func__, handle, + _WAPI_PRIVATE_HANDLES(idx).ref); +#endif } -guchar *_wapi_handle_scratch_lookup_as_string (guint32 idx) +/* The handle must not be locked on entry to this function */ +void _wapi_handle_unref (gpointer handle) { - struct _WapiScratchHeader *hdr; - guchar *str; - guchar *storage=&_wapi_shared_data->scratch_base[0]; + guint32 idx = GPOINTER_TO_UINT(handle); + gboolean destroy = FALSE; + int thr_ret; + + /* Possible race condition here if another thread refs the + * handle between here and setting the type to UNUSED. I + * could lock a mutex, but I'm not sure that allowing a handle + * reference to reach 0 isn't an application bug anyway. + */ + destroy = (InterlockedDecrement (&_WAPI_PRIVATE_HANDLES(idx).ref) ==0); - if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) { - return(NULL); - } +#ifdef DEBUG_REFS + g_message ("%s: handle %p ref now %d (destroy %s)", __func__, handle, + _WAPI_PRIVATE_HANDLES(idx).ref, destroy?"TRUE":"FALSE"); +#endif - hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE]; - str=g_malloc0 (hdr->length+1); - memcpy (str, &storage[idx], hdr->length); + if(destroy==TRUE) { + /* Need to copy the handle info, reset the slot in the + * array, and _only then_ call the close function to + * avoid race conditions (eg file descriptors being + * closed, and another file being opened getting the + * same fd racing the memset()) + */ + struct _WapiHandleUnshared handle_data; + WapiHandleType type = _WAPI_PRIVATE_HANDLES(idx).type; + void (*close_func)(gpointer, gpointer) = _wapi_handle_ops_get_close_func (type); - return(str); -} +#ifdef DEBUG + g_message ("%s: Destroying handle %p", __func__, handle); +#endif + + memcpy (&handle_data, &_WAPI_PRIVATE_HANDLES(idx), + sizeof (struct _WapiHandleUnshared)); -void _wapi_handle_scratch_delete_internal (guint32 idx) -{ - struct _WapiScratchHeader *hdr; - guchar *storage=&_wapi_shared_data->scratch_base[0]; - - if(idx < HDRSIZE || idx > _WAPI_SHM_SCRATCH_SIZE) { - return; - } - - hdr=(struct _WapiScratchHeader *)&storage[idx - HDRSIZE]; - memset (&storage[idx], '\0', hdr->length); - hdr->flags |= WAPI_SHM_SCRATCH_FREE; - - /* We could coalesce forwards here if the next block is also - * free, but the _store() function will do that anyway. - */ -} + memset (&_WAPI_PRIVATE_HANDLES(idx).u, '\0', + sizeof(_WAPI_PRIVATE_HANDLES(idx).u)); -void _wapi_handle_scratch_delete (guint32 idx) -{ - if(shared==TRUE) { - WapiHandleRequest scratch_free; - WapiHandleResponse scratch_free_resp; - - scratch_free.type=WapiHandleRequestType_ScratchFree; - scratch_free.u.scratch_free.idx=idx; - - _wapi_daemon_request_response (daemon_sock, &scratch_free, - &scratch_free_resp); - - if(scratch_free_resp.type!=WapiHandleResponseType_ScratchFree) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": bogus daemon response, type %d", - scratch_free_resp.type); - g_assert_not_reached (); + _WAPI_PRIVATE_HANDLES(idx).type = WAPI_HANDLE_UNUSED; + + if (!_WAPI_SHARED_HANDLE(type)) { + /* Destroy the mutex and cond var. We hope nobody + * tried to grab them between the handle unlock and + * now, but pthreads doesn't have a + * "unlock_and_destroy" atomic function. + */ + thr_ret = mono_mutex_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_mutex); + g_assert (thr_ret == 0); + + thr_ret = pthread_cond_destroy (&_WAPI_PRIVATE_HANDLES(idx).signal_cond); + g_assert (thr_ret == 0); + } + + /* The garbage collector will take care of shared data + * if this is a shared handle + */ + + if (close_func != NULL) { + close_func (handle, &handle_data.u); } - } else { - _wapi_handle_scratch_delete_internal (idx); } } void _wapi_handle_register_capabilities (WapiHandleType type, WapiHandleCapability caps) { - handle_caps[type]=caps; + handle_caps[type] = caps; } gboolean _wapi_handle_test_capabilities (gpointer handle, WapiHandleCapability caps) { - guint32 idx=GPOINTER_TO_UINT (handle); + guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; - type=_wapi_shared_data->handles[idx].type; + type = _WAPI_PRIVATE_HANDLES(idx).type; #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": testing 0x%x against 0x%x (%d)", + g_message ("%s: testing 0x%x against 0x%x (%d)", __func__, handle_caps[type], caps, handle_caps[type] & caps); #endif - return((handle_caps[type] & caps)!=0); + return((handle_caps[type] & caps) != 0); } -void _wapi_handle_ops_close_shared (gpointer handle) +static void (*_wapi_handle_ops_get_close_func (WapiHandleType type))(gpointer, gpointer) { - guint32 idx=GPOINTER_TO_UINT (handle); - WapiHandleType type; - - type=_wapi_shared_data->handles[idx].type; - - if(handle_ops[type]!=NULL && handle_ops[type]->close_shared!=NULL) { - handle_ops[type]->close_shared (handle); + if (handle_ops[type] != NULL && + handle_ops[type]->close != NULL) { + return (handle_ops[type]->close); } + + return (NULL); } -void _wapi_handle_ops_close_private (gpointer handle) +void _wapi_handle_ops_close (gpointer handle, gpointer data) { - guint32 idx=GPOINTER_TO_UINT (handle); + guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; - type=_wapi_shared_data->handles[idx].type; + type = _WAPI_PRIVATE_HANDLES(idx).type; - if(handle_ops[type]!=NULL && handle_ops[type]->close_private!=NULL) { - handle_ops[type]->close_private (handle); + if (handle_ops[type] != NULL && + handle_ops[type]->close != NULL) { + handle_ops[type]->close (handle, data); } } void _wapi_handle_ops_signal (gpointer handle) { - guint32 idx=GPOINTER_TO_UINT (handle); + guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; - type=_wapi_shared_data->handles[idx].type; + type = _WAPI_PRIVATE_HANDLES(idx).type; - if(handle_ops[type]!=NULL && handle_ops[type]->signal!=NULL) { + if (handle_ops[type] != NULL && handle_ops[type]->signal != NULL) { handle_ops[type]->signal (handle); } } -void _wapi_handle_ops_own (gpointer handle) +gboolean _wapi_handle_ops_own (gpointer handle) { - guint32 idx=GPOINTER_TO_UINT (handle); + guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; + + type = _WAPI_PRIVATE_HANDLES(idx).type; - type=_wapi_shared_data->handles[idx].type; - - if(handle_ops[type]!=NULL && handle_ops[type]->own_handle!=NULL) { - handle_ops[type]->own_handle (handle); + if (handle_ops[type] != NULL && handle_ops[type]->own_handle != NULL) { + return(handle_ops[type]->own_handle (handle)); + } else { + return(FALSE); } } gboolean _wapi_handle_ops_isowned (gpointer handle) { - guint32 idx=GPOINTER_TO_UINT (handle); + guint32 idx = GPOINTER_TO_UINT(handle); WapiHandleType type; - type=_wapi_shared_data->handles[idx].type; + type = _WAPI_PRIVATE_HANDLES(idx).type; - if(handle_ops[type]!=NULL && handle_ops[type]->is_owned!=NULL) { + if (handle_ops[type] != NULL && handle_ops[type]->is_owned != NULL) { return(handle_ops[type]->is_owned (handle)); } else { return(FALSE); } } +guint32 _wapi_handle_ops_special_wait (gpointer handle, guint32 timeout) +{ + guint32 idx = GPOINTER_TO_UINT(handle); + WapiHandleType type; + + type = _WAPI_PRIVATE_HANDLES(idx).type; + + if (handle_ops[type] != NULL && + handle_ops[type]->special_wait != NULL) { + return(handle_ops[type]->special_wait (handle, timeout)); + } else { + return(WAIT_FAILED); + } +} + + /** * CloseHandle: * @handle: The handle to release @@ -667,20 +1098,43 @@ gboolean _wapi_handle_count_signalled_handles (guint32 numhandles, { guint32 count, i, iter=0; gboolean ret; + int thr_ret; + WapiHandleType type; /* Lock all the handles, with backoff */ again: + thr_ret = _wapi_handle_lock_shared_handles (); + g_assert (thr_ret == 0); + for(i=0; ihandles[idx].signal_mutex); - if(ret!=0) { + if (thr_ret != 0) { /* Bummer */ - struct timespec sleepytime; - while(i--) { - idx=GPOINTER_TO_UINT (handles[i]); - mono_mutex_unlock (&_wapi_shared_data->handles[idx].signal_mutex); +#ifdef DEBUG + g_message ("%s: attempt failed for %p: %s", __func__, + handle, strerror (thr_ret)); +#endif + + thr_ret = _wapi_handle_unlock_shared_handles (); + g_assert (thr_ret == 0); + + while (i--) { + handle = handles[i]; + idx = GPOINTER_TO_UINT(handle); + + thr_ret = _wapi_handle_unlock_handle (handle); + g_assert (thr_ret == 0); } /* If iter ever reaches 100 the nanosleep will @@ -689,47 +1143,51 @@ again: */ iter++; if(iter==100) { - g_warning (G_GNUC_PRETTY_FUNCTION - ": iteration overflow!"); + g_warning ("%s: iteration overflow!", + __func__); iter=1; } - sleepytime.tv_sec=0; - sleepytime.tv_nsec=10000000 * iter; /* 10ms*iter */ - #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Backing off for %d ms", iter*10); + g_message ("%s: Backing off for %d ms", __func__, + iter*10); #endif - nanosleep (&sleepytime, NULL); + _wapi_handle_spin (10 * iter); goto again; } } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Locked all handles"); + g_message ("%s: Locked all handles", __func__); #endif count=0; *lowest=numhandles; for(i=0; ihandles[idx].signalled==TRUE)) { + if(((_wapi_handle_test_capabilities (handle, WAPI_HANDLE_CAP_OWN)==TRUE) && + (_wapi_handle_ops_isowned (handle) == TRUE)) || + (_WAPI_SHARED_HANDLE(type) && + WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) || + (!_WAPI_SHARED_HANDLE(type) && + _WAPI_PRIVATE_HANDLES(idx).signalled == TRUE)) { count++; #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Handle %p signalled", handles[i]); + g_message ("%s: Handle %p signalled", __func__, + handle); #endif if(*lowest>i) { *lowest=i; @@ -738,19 +1196,18 @@ again: } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": %d event handles signalled", - count); + g_message ("%s: %d event handles signalled", __func__, count); #endif - if((waitall==TRUE && count==numhandles) || - (waitall==FALSE && count>0)) { + if ((waitall == TRUE && count == numhandles) || + (waitall == FALSE && count > 0)) { ret=TRUE; } else { ret=FALSE; } #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Returning %d", ret); + g_message ("%s: Returning %d", __func__, ret); #endif *retcount=count; @@ -761,52 +1218,434 @@ again: void _wapi_handle_unlock_handles (guint32 numhandles, gpointer *handles) { guint32 i; + int thr_ret; + + thr_ret = _wapi_handle_unlock_shared_handles (); + g_assert (thr_ret == 0); for(i=0; ihandles[idx].signal_mutex); +#ifdef DEBUG + g_message ("%s: unlocking handle %p", __func__, handle); +#endif + + thr_ret = _wapi_handle_unlock_handle (handle); + g_assert (thr_ret == 0); + } +} + +static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex, struct timespec *timeout) +{ + struct timespec fake_timeout; + int ret; + + _wapi_calc_timeout (&fake_timeout, 100); + + if (timeout != NULL && ((fake_timeout.tv_sec > timeout->tv_sec) || + (fake_timeout.tv_sec == timeout->tv_sec && + fake_timeout.tv_nsec > timeout->tv_nsec))) { + /* Real timeout is less than 100ms time */ + ret=mono_cond_timedwait (cond, mutex, timeout); + } else { + ret=mono_cond_timedwait (cond, mutex, &fake_timeout); + + /* Mask the fake timeout, this will cause + * another poll if the cond was not really signaled + */ + if (ret==ETIMEDOUT) { + ret=0; + } } + + return(ret); } int _wapi_handle_wait_signal (void) { -#ifdef _POSIX_THREAD_PROCESS_SHARED - return(mono_cond_wait (&_wapi_shared_data->signal_cond, - &_wapi_shared_data->signal_mutex)); -#else - return(mono_cond_wait (&_wapi_private_data->signal_cond, - &_wapi_private_data->signal_mutex)); -#endif /* _POSIX_THREAD_PROCESS_SHARED */ + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL); } int _wapi_handle_timedwait_signal (struct timespec *timeout) { -#ifdef _POSIX_THREAD_PROCESS_SHARED - return(mono_cond_timedwait (&_wapi_shared_data->signal_cond, - &_wapi_shared_data->signal_mutex, - timeout)); -#else - return(mono_cond_timedwait (&_wapi_private_data->signal_cond, - &_wapi_private_data->signal_mutex, - timeout)); -#endif /* _POSIX_THREAD_PROCESS_SHARED */ + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout); +} + +int _wapi_handle_wait_signal_poll_share (void) +{ +#ifdef DEBUG + g_message ("%s: poll private and shared handles", __func__); +#endif + + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, NULL); +} + +int _wapi_handle_timedwait_signal_poll_share (struct timespec *timeout) +{ +#ifdef DEBUG + g_message ("%s: poll private and shared handles", __func__); +#endif + + return timedwait_signal_poll_cond (&_wapi_global_signal_cond, &_wapi_global_signal_mutex, timeout); } int _wapi_handle_wait_signal_handle (gpointer handle) { - guint32 idx=GPOINTER_TO_UINT (handle); +#ifdef DEBUG + g_message ("%s: waiting for %p", __func__, handle); +#endif - return(mono_cond_wait (&_wapi_shared_data->handles[idx].signal_cond, - &_wapi_shared_data->handles[idx].signal_mutex)); + return _wapi_handle_timedwait_signal_handle (handle, NULL); } int _wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout) { - guint32 idx=GPOINTER_TO_UINT (handle); +#ifdef DEBUG + g_message ("%s: waiting for %p (type %s)", __func__, handle, + _wapi_handle_typename[_wapi_handle_type (handle)]); +#endif + + if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) { + if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { + return (0); + } + if (timeout != NULL) { + struct timespec fake_timeout; + _wapi_calc_timeout (&fake_timeout, 100); + + if ((fake_timeout.tv_sec > timeout->tv_sec) || + (fake_timeout.tv_sec == timeout->tv_sec && + fake_timeout.tv_nsec > timeout->tv_nsec)) { + /* FIXME: Real timeout is less than + * 100ms time, but is it really worth + * calculating to the exact ms? + */ + _wapi_handle_spin (100); + + if (WAPI_SHARED_HANDLE_METADATA(handle).signalled == TRUE) { + return (0); + } else { + return (ETIMEDOUT); + } + } + } + _wapi_handle_spin (100); + return (0); + + } else { + guint32 idx = GPOINTER_TO_UINT(handle); + return timedwait_signal_poll_cond (&_WAPI_PRIVATE_HANDLES(idx).signal_cond, &_WAPI_PRIVATE_HANDLES(idx).signal_mutex, timeout); + } +} + +gboolean _wapi_handle_get_or_set_share (dev_t device, ino_t inode, + guint32 new_sharemode, + guint32 new_access, + guint32 *old_sharemode, + guint32 *old_access, + struct _WapiFileShare **share_info) +{ + struct _WapiFileShare *file_share; + guint32 now = (guint32)(time(NULL) & 0xFFFFFFFF); + int thr_ret, i, first_unused = -1; + gboolean exists = FALSE; + + /* Marking this as COLLECTION_UNSAFE prevents entries from + * expiring under us as we search + */ + _WAPI_HANDLE_COLLECTION_UNSAFE; + + /* Prevent new entries racing with us */ + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); + g_assert (thr_ret == 0); + + /* If a linear scan gets too slow we'll have to fit a hash + * table onto the shared mem backing store + */ + *share_info = NULL; + for (i = 0; i <= _wapi_fileshare_layout->hwm; i++) { + file_share = &_wapi_fileshare_layout->share_info[i]; + + /* Make a note of an unused slot, in case we need to + * store share info + */ + if (first_unused == -1 && file_share->handle_refs == 0) { + first_unused = i; + continue; + } + + if (file_share->handle_refs == 0) { + continue; + } + + if (file_share->device == device && + file_share->inode == inode) { + *old_sharemode = file_share->sharemode; + *old_access = file_share->access; + *share_info = file_share; + + /* Increment the reference count while we + * still have sole access to the shared area. + * This makes the increment atomic wrt + * collections + */ + InterlockedIncrement (&file_share->handle_refs); + + exists = TRUE; + break; + } + } + + if (!exists) { + if (i == _WAPI_FILESHARE_SIZE && first_unused == -1) { + /* No more space */ + } else { + if (first_unused == -1) { + file_share = &_wapi_fileshare_layout->share_info[++i]; + _wapi_fileshare_layout->hwm = i; + } else { + file_share = &_wapi_fileshare_layout->share_info[first_unused]; + } + + file_share->device = device; + file_share->inode = inode; + file_share->opened_by_pid = getpid (); + file_share->sharemode = new_sharemode; + file_share->access = new_access; + file_share->handle_refs = 1; + *share_info = file_share; + } + } + + if (*share_info != NULL) { + InterlockedExchange (&(*share_info)->timestamp, now); + } + + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + + _WAPI_HANDLE_COLLECTION_SAFE; + + return(exists); +} + +/* If we don't have the info in /proc, check if the process that + * opened this share info is still there (it's not a perfect method, + * due to pid reuse) + */ +static void _wapi_handle_check_share_by_pid (struct _WapiFileShare *share_info) +{ + if (kill (share_info->opened_by_pid, 0) == -1 && + (errno == ESRCH || + errno == EPERM)) { + /* It's gone completely (or there's a new process + * owned by someone else) so mark this share info as + * dead + */ +#ifdef DEBUG + g_message ("%s: Didn't find it, destroying entry", __func__); +#endif + + memset (share_info, '\0', sizeof(struct _WapiFileShare)); + } +} + +/* Scan /proc//fd/ for open file descriptors to the file in + * question. If there are none, reset the share info. + * + * This implementation is Linux-specific; legacy systems will have to + * implement their own ways of finding out if a particular file is + * open by a process. + */ +void _wapi_handle_check_share (struct _WapiFileShare *share_info, int fd) +{ + gboolean found = FALSE, proc_fds = FALSE; + pid_t self = getpid(); + int pid; + int thr_ret, i; + + /* If there is no /proc, there's nothing more we can do here */ + if (access ("/proc", F_OK) == -1) { + _wapi_handle_check_share_by_pid (share_info); + return; + } + + /* Marking this as COLLECTION_UNSAFE prevents entries from + * expiring under us if we remove this one + */ + _WAPI_HANDLE_COLLECTION_UNSAFE; + + /* Prevent new entries racing with us */ + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); + g_assert (thr_ret == 0); + + for (i = 0; i < _WAPI_HANDLE_INITIAL_COUNT; i++) { + struct _WapiHandleShared *shared; + struct _WapiHandleSharedMetadata *meta; + struct _WapiHandle_process *process_handle; + + meta = &_wapi_shared_layout->metadata[i]; + shared = &_wapi_shared_layout->handles[meta->offset]; + + if (shared->type == WAPI_HANDLE_PROCESS) { + DIR *fd_dir; + struct dirent *fd_entry; + char subdir[_POSIX_PATH_MAX]; + + process_handle = &shared->u.process; + pid = process_handle->id; + + /* Look in /proc//fd/ but ignore + * /proc//fd/, as we have the + * file open too + */ + g_snprintf (subdir, _POSIX_PATH_MAX, "/proc/%d/fd", + pid); + + fd_dir = opendir (subdir); + if (fd_dir == NULL) { + continue; + } + +#ifdef DEBUG + g_message ("%s: Looking in %s", __func__, subdir); +#endif + + proc_fds = TRUE; + + while ((fd_entry = readdir (fd_dir)) != NULL) { + char path[_POSIX_PATH_MAX]; + struct stat link_stat; + + if (!strcmp (fd_entry->d_name, ".") || + !strcmp (fd_entry->d_name, "..") || + (pid == self && + fd == atoi (fd_entry->d_name))) { + continue; + } + + g_snprintf (path, _POSIX_PATH_MAX, + "/proc/%d/fd/%s", pid, + fd_entry->d_name); + + stat (path, &link_stat); + if (link_stat.st_dev == share_info->device && + link_stat.st_ino == share_info->inode) { +#ifdef DEBUG + g_message ("%s: Found it at %s", + __func__, path); +#endif + + found = TRUE; + } + } + + closedir (fd_dir); + } + } + + if (proc_fds == FALSE) { + _wapi_handle_check_share_by_pid (share_info); + } else if (found == FALSE) { + /* Blank out this entry, as it is stale */ +#ifdef DEBUG + g_message ("%s: Didn't find it, destroying entry", __func__); +#endif + + memset (share_info, '\0', sizeof(struct _WapiFileShare)); + } + + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + + _WAPI_HANDLE_COLLECTION_SAFE; +} + +void _wapi_handle_dump (void) +{ + struct _WapiHandleUnshared *handle_data; + guint32 i, k; + + for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { + for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { + handle_data = &_wapi_private_handles [i][k]; + + if (handle_data->type == WAPI_HANDLE_UNUSED) { + continue; + } + + g_print ("%3x [%7s] %s %d ", + i * _WAPI_HANDLE_INITIAL_COUNT + k, + _wapi_handle_typename[handle_data->type], + handle_data->signalled?"Sg":"Un", + handle_data->ref); + handle_details[handle_data->type](&handle_data->u); + g_print ("\n"); + } + } +} + +static void _wapi_shared_details (gpointer handle_info) +{ + struct _WapiHandle_shared_ref *shared = (struct _WapiHandle_shared_ref *)handle_info; + + g_print ("offset: 0x%x", shared->offset); +} + +void _wapi_handle_update_refs (void) +{ + guint32 i, k; + int thr_ret; + + _WAPI_HANDLE_COLLECTION_UNSAFE; + + /* Prevent file share entries racing with us */ + thr_ret = _wapi_shm_sem_lock (_WAPI_SHARED_SEM_SHARE); + g_assert(thr_ret == 0); + + for(i = SLOT_INDEX (0); _wapi_private_handles [i] != NULL; i++) { + for (k = SLOT_OFFSET (0); k < _WAPI_HANDLE_INITIAL_COUNT; k++) { + struct _WapiHandleUnshared *handle = &_wapi_private_handles [i][k]; + guint32 now = (guint32)(time (NULL) & 0xFFFFFFFF); + + if (_WAPI_SHARED_HANDLE(handle->type)) { + struct _WapiHandleSharedMetadata *shared_meta; + +#ifdef DEBUG + g_message ("%s: (%d) handle 0x%x is SHARED (%s)", __func__, + getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k, _wapi_handle_typename[handle->type]); +#endif + + shared_meta = &_wapi_shared_layout->metadata[handle->u.shared.offset]; + +#ifdef DEBUG + g_message ("%s: (%d) Updating timestamp of handle 0x%x", + __func__, getpid(), + handle->u.shared.offset); +#endif + + InterlockedExchange (&shared_meta->timestamp, now); + } else if (handle->type == WAPI_HANDLE_FILE) { + struct _WapiHandle_file *file_handle = &handle->u.file; + +#ifdef DEBUG + g_message ("%s: (%d) handle 0x%x is FILE", __func__, + getpid (), i * _WAPI_HANDLE_INITIAL_COUNT + k); +#endif + + g_assert (file_handle->share_info != NULL); + +#ifdef DEBUG + g_message ("%s: (%d) Inc refs on fileshare 0x%x", + __func__, getpid(), + (file_handle->share_info - &_wapi_fileshare_layout->share_info[0]) / sizeof(struct _WapiFileShare)); +#endif + + InterlockedExchange (&file_handle->share_info->timestamp, now); + } + } + } - return(mono_cond_timedwait (&_wapi_shared_data->handles[idx].signal_cond, - &_wapi_shared_data->handles[idx].signal_mutex, - timeout)); + thr_ret = _wapi_shm_sem_unlock (_WAPI_SHARED_SEM_SHARE); + + _WAPI_HANDLE_COLLECTION_SAFE; }