+#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/<pids>/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/<pid>/fd/ but ignore
+ * /proc/<our pid>/fd/<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);
+ }
+ }
+ }