Blame gio/kqueue/kqueue-helper.c

Packit ae235b
/*******************************************************************************
Packit ae235b
  Copyright (c) 2011, 2012 Dmitry Matveev <me@dmitrymatveev.co.uk>
Packit ae235b
Packit ae235b
  Permission is hereby granted, free of charge, to any person obtaining a copy
Packit ae235b
  of this software and associated documentation files (the "Software"), to deal
Packit ae235b
  in the Software without restriction, including without limitation the rights
Packit ae235b
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Packit ae235b
  copies of the Software, and to permit persons to whom the Software is
Packit ae235b
  furnished to do so, subject to the following conditions:
Packit ae235b
Packit ae235b
  The above copyright notice and this permission notice shall be included in
Packit ae235b
  all copies or substantial portions of the Software.
Packit ae235b
Packit ae235b
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit ae235b
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit ae235b
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit ae235b
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit ae235b
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Packit ae235b
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Packit ae235b
  THE SOFTWARE.
Packit ae235b
*******************************************************************************/
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
#include <sys/types.h>
Packit ae235b
#include <sys/event.h>
Packit ae235b
#include <sys/time.h>
Packit ae235b
#include <sys/socket.h>
Packit ae235b
#include <gio/glocalfile.h>
Packit ae235b
#include <gio/glocalfilemonitor.h>
Packit ae235b
#include <gio/gfile.h>
Packit ae235b
#include <fcntl.h>
Packit ae235b
#include <unistd.h>
Packit ae235b
#include <string.h>
Packit ae235b
#include <errno.h>
Packit ae235b
#include <pthread.h>
Packit ae235b
#include "kqueue-helper.h"
Packit ae235b
#include "kqueue-utils.h"
Packit ae235b
#include "kqueue-thread.h"
Packit ae235b
#include "kqueue-missing.h"
Packit ae235b
#include "kqueue-exclusions.h"
Packit ae235b
Packit ae235b
static gboolean kh_debug_enabled = FALSE;
Packit ae235b
#define KH_W if (kh_debug_enabled) g_warning
Packit ae235b
Packit ae235b
static GHashTable *subs_hash_table = NULL;
Packit ae235b
G_LOCK_DEFINE_STATIC (hash_lock);
Packit ae235b
Packit ae235b
static int kqueue_descriptor = -1;
Packit ae235b
static int kqueue_socket_pair[] = {-1, -1};
Packit ae235b
static pthread_t kqueue_thread;
Packit ae235b
Packit ae235b
Packit ae235b
void _kh_file_appeared_cb (kqueue_sub *sub);
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * accessor function for kqueue_descriptor
Packit ae235b
 **/
Packit ae235b
int
Packit ae235b
get_kqueue_descriptor()
Packit ae235b
{
Packit ae235b
  return kqueue_descriptor;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * convert_kqueue_events_to_gio:
Packit ae235b
 * @flags: a set of kqueue filter flags
Packit ae235b
 * @done: a pointer to #gboolean indicating that the
Packit ae235b
 *      conversion has been done (out)
Packit ae235b
 *
Packit ae235b
 * Translates kqueue filter flags into GIO event flags.
Packit ae235b
 *
Packit ae235b
 * Returns: a #GFileMonitorEvent
Packit ae235b
 **/
Packit ae235b
static GFileMonitorEvent
Packit ae235b
convert_kqueue_events_to_gio (uint32_t flags, gboolean *done)
Packit ae235b
{
Packit ae235b
  g_assert (done != NULL);
Packit ae235b
  *done = FALSE;
Packit ae235b
Packit ae235b
  /* TODO: The following notifications should be emulated, if possible:
Packit ae235b
   * - G_FILE_MONITOR_EVENT_PRE_UNMOUNT
Packit ae235b
   */
Packit ae235b
  if (flags & NOTE_DELETE)
Packit ae235b
    {    
Packit ae235b
      *done = TRUE;
Packit ae235b
      return G_FILE_MONITOR_EVENT_DELETED;
Packit ae235b
    }
Packit ae235b
  if (flags & NOTE_ATTRIB)
Packit ae235b
    {
Packit ae235b
      *done = TRUE;
Packit ae235b
      return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
Packit ae235b
    }
Packit ae235b
  if (flags & (NOTE_WRITE | NOTE_EXTEND))
Packit ae235b
    {
Packit ae235b
      *done = TRUE;
Packit ae235b
      return G_FILE_MONITOR_EVENT_CHANGED;
Packit ae235b
    }
Packit ae235b
  if (flags & NOTE_RENAME)
Packit ae235b
    {
Packit ae235b
      /* Since there’s apparently no way to get the new name of the file out of
Packit ae235b
       * kqueue(), all we can do is say that this one has been deleted. */
Packit ae235b
      *done = TRUE;
Packit ae235b
      return G_FILE_MONITOR_EVENT_DELETED;
Packit ae235b
    }
Packit ae235b
  if (flags & NOTE_REVOKE)
Packit ae235b
    {
Packit ae235b
      *done = TRUE;
Packit ae235b
      return G_FILE_MONITOR_EVENT_UNMOUNTED;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  /* done is FALSE */
Packit ae235b
  return 0;
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct {
Packit ae235b
  kqueue_sub *sub;
Packit ae235b
  GFileMonitorSource *source;
Packit ae235b
} handle_ctx;
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * handle_created: 
Packit ae235b
 * @udata: a pointer to user data (#handle_context).
Packit ae235b
 * @path: file name of a new file.
Packit ae235b
 * @inode: inode number of a new file.
Packit ae235b
 *
Packit ae235b
 * A callback function for the directory diff calculation routine,
Packit ae235b
 * produces G_FILE_MONITOR_EVENT_CREATED event for a created file.
Packit ae235b
 **/
Packit ae235b
static void
Packit ae235b
handle_created (void *udata, const char *path, ino_t inode)
Packit ae235b
{
Packit ae235b
  handle_ctx *ctx = NULL;
Packit ae235b
Packit ae235b
  (void) inode;
Packit ae235b
  ctx = (handle_ctx *) udata;
Packit ae235b
  g_assert (udata != NULL);
Packit ae235b
  g_assert (ctx->sub != NULL);
Packit ae235b
  g_assert (ctx->source != NULL);
Packit ae235b
Packit ae235b
  g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_CREATED, path,
Packit ae235b
                                      NULL, NULL, g_get_monotonic_time ());
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * handle_deleted:
Packit ae235b
 * @udata: a pointer to user data (#handle_context).
Packit ae235b
 * @path: file name of the removed file.
Packit ae235b
 * @inode: inode number of the removed file.
Packit ae235b
 *
Packit ae235b
 * A callback function for the directory diff calculation routine,
Packit ae235b
 * produces G_FILE_MONITOR_EVENT_DELETED event for a deleted file.
Packit ae235b
 **/
Packit ae235b
static void
Packit ae235b
handle_deleted (void *udata, const char *path, ino_t inode)
Packit ae235b
{
Packit ae235b
  handle_ctx *ctx = NULL;
Packit ae235b
Packit ae235b
  (void) inode;
Packit ae235b
  ctx = (handle_ctx *) udata;
Packit ae235b
  g_assert (udata != NULL);
Packit ae235b
  g_assert (ctx->sub != NULL);
Packit ae235b
  g_assert (ctx->source != NULL);
Packit ae235b
Packit ae235b
  g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_DELETED, path,
Packit ae235b
                                      NULL, NULL, g_get_monotonic_time ());
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * handle_moved:
Packit ae235b
 * @udata: a pointer to user data (#handle_context).
Packit ae235b
 * @from_path: file name of the source file.
Packit ae235b
 * @from_inode: inode number of the source file.
Packit ae235b
 * @to_path: file name of the replaced file.
Packit ae235b
 * @to_inode: inode number of the replaced file.
Packit ae235b
 *
Packit ae235b
 * A callback function for the directory diff calculation routine,
Packit ae235b
 * produces G_FILE_MONITOR_EVENT_RENAMED event on a move.
Packit ae235b
 **/
Packit ae235b
static void
Packit ae235b
handle_moved (void       *udata,
Packit ae235b
              const char *from_path,
Packit ae235b
              ino_t       from_inode,
Packit ae235b
              const char *to_path,
Packit ae235b
              ino_t       to_inode)
Packit ae235b
{
Packit ae235b
  handle_ctx *ctx = NULL;
Packit ae235b
Packit ae235b
  (void) from_inode;
Packit ae235b
  (void) to_inode;
Packit ae235b
Packit ae235b
  ctx = (handle_ctx *) udata;
Packit ae235b
  g_assert (udata != NULL);
Packit ae235b
  g_assert (ctx->sub != NULL);
Packit ae235b
  g_assert (ctx->source != NULL);
Packit ae235b
Packit ae235b
  g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_RENAMED,
Packit ae235b
                                      from_path, to_path, NULL, g_get_monotonic_time ());
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * handle_overwritten:
Packit ae235b
 * @data: a pointer to user data (#handle_context).
Packit ae235b
 * @path: file name of the overwritten file.
Packit ae235b
 * @node: inode number of the overwritten file.
Packit ae235b
 *
Packit ae235b
 * A callback function for the directory diff calculation routine,
Packit ae235b
 * produces G_FILE_MONITOR_EVENT_DELETED/CREATED event pair when
Packit ae235b
 * an overwrite occurs in the directory (see dep-list for details).
Packit ae235b
 **/
Packit ae235b
static void
Packit ae235b
handle_overwritten (void *udata, const char *path, ino_t inode)
Packit ae235b
{
Packit ae235b
  handle_ctx *ctx = NULL;
Packit ae235b
Packit ae235b
  (void) inode;
Packit ae235b
  ctx = (handle_ctx *) udata;
Packit ae235b
  g_assert (udata != NULL);
Packit ae235b
  g_assert (ctx->sub != NULL);
Packit ae235b
  g_assert (ctx->source != NULL);
Packit ae235b
Packit ae235b
  g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_DELETED,
Packit ae235b
                                      path, NULL, NULL, g_get_monotonic_time ());
Packit ae235b
Packit ae235b
  g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_CREATED,
Packit ae235b
                                      path, NULL, NULL, g_get_monotonic_time ());
Packit ae235b
}
Packit ae235b
Packit ae235b
static const traverse_cbs cbs = {
Packit ae235b
  handle_created,
Packit ae235b
  handle_deleted,
Packit ae235b
  handle_moved,
Packit ae235b
  handle_overwritten,
Packit ae235b
  handle_moved,
Packit ae235b
  NULL, /* many added */
Packit ae235b
  NULL, /* many removed */
Packit ae235b
  NULL, /* names updated */
Packit ae235b
};
Packit ae235b
Packit ae235b
Packit ae235b
void
Packit ae235b
_kh_dir_diff (kqueue_sub *sub, GFileMonitorSource *source)
Packit ae235b
{
Packit ae235b
  dep_list *was;
Packit ae235b
  handle_ctx ctx;
Packit ae235b
Packit ae235b
  g_assert (sub != NULL);
Packit ae235b
  g_assert (source != NULL);
Packit ae235b
Packit ae235b
  memset (&ctx, 0, sizeof (handle_ctx));
Packit ae235b
  ctx.sub = sub;
Packit ae235b
  ctx.source = source;
Packit ae235b
Packit ae235b
  was = sub->deps;
Packit ae235b
  sub->deps = dl_listing (sub->filename);
Packit ae235b
 
Packit ae235b
  dl_calculate (was, sub->deps, &cbs, &ctx;;
Packit ae235b
Packit ae235b
  dl_free (was);
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * process_kqueue_notifications:
Packit ae235b
 * @gioc: unused.
Packit ae235b
 * @cond: unused.
Packit ae235b
 * @data: unused.
Packit ae235b
 *
Packit ae235b
 * Processes notifications, coming from the kqueue thread.
Packit ae235b
 *
Packit ae235b
 * Reads notifications from the command file descriptor, emits the
Packit ae235b
 * "changed" event on the appropriate monitor.
Packit ae235b
 *
Packit ae235b
 * A typical GIO Channel callback function.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE
Packit ae235b
 **/
Packit ae235b
static gboolean
Packit ae235b
process_kqueue_notifications (GIOChannel   *gioc,
Packit ae235b
                              GIOCondition  cond,
Packit ae235b
                              gpointer      data)
Packit ae235b
{
Packit ae235b
  struct kqueue_notification n;
Packit ae235b
  kqueue_sub *sub = NULL;
Packit ae235b
  GFileMonitorSource *source = NULL;
Packit ae235b
  GFileMonitorEvent mask = 0;
Packit ae235b
  
Packit ae235b
  g_assert (kqueue_socket_pair[0] != -1);
Packit ae235b
  if (!_ku_read (kqueue_socket_pair[0], &n, sizeof (struct kqueue_notification)))
Packit ae235b
    {
Packit ae235b
      KH_W ("Failed to read a kqueue notification, error %d", errno);
Packit ae235b
      return TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  G_LOCK (hash_lock);
Packit ae235b
  sub = (kqueue_sub *) g_hash_table_lookup (subs_hash_table, GINT_TO_POINTER (n.fd));
Packit ae235b
  G_UNLOCK (hash_lock);
Packit ae235b
Packit ae235b
  if (sub == NULL)
Packit ae235b
    {
Packit ae235b
      KH_W ("Got a notification for a deleted or non-existing subscription %d",
Packit ae235b
             n.fd);
Packit ae235b
      return TRUE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  source = sub->user_data;
Packit ae235b
  g_assert (source != NULL);
Packit ae235b
Packit ae235b
  if (n.flags & (NOTE_DELETE | NOTE_REVOKE))
Packit ae235b
    {
Packit ae235b
      if (sub->deps)
Packit ae235b
        {
Packit ae235b
          dl_free (sub->deps);
Packit ae235b
          sub->deps = NULL;  
Packit ae235b
        }  
Packit ae235b
      _km_add_missing (sub);
Packit ae235b
Packit ae235b
      if (!(n.flags & NOTE_REVOKE))
Packit ae235b
        {
Packit ae235b
          /* Note that NOTE_REVOKE is issued by the kqueue thread
Packit ae235b
           * on EV_ERROR kevent. In this case, a file descriptor is
Packit ae235b
           * already closed from the kqueue thread, no need to close
Packit ae235b
           * it manually */ 
Packit ae235b
          _kh_cancel_sub (sub);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (sub->is_dir && n.flags & (NOTE_WRITE | NOTE_EXTEND))
Packit ae235b
    {
Packit ae235b
      _kh_dir_diff (sub, source);
Packit ae235b
      n.flags &= ~(NOTE_WRITE | NOTE_EXTEND);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (n.flags)
Packit ae235b
    {
Packit ae235b
      gboolean done = FALSE;
Packit ae235b
      mask = convert_kqueue_events_to_gio (n.flags, &done);
Packit ae235b
      if (done == TRUE)
Packit ae235b
        g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, g_get_monotonic_time ());
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/*
Packit ae235b
 * _kh_startup_impl:
Packit ae235b
 * @unused: unused
Packit ae235b
 *
Packit ae235b
 * Kqueue backend startup code. Should be called only once.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE on success, %FALSE otherwise.
Packit ae235b
 **/
Packit ae235b
static gpointer
Packit ae235b
_kh_startup_impl (gpointer unused)
Packit ae235b
{
Packit ae235b
  GIOChannel *channel = NULL;
Packit ae235b
  gboolean result = FALSE;
Packit ae235b
Packit ae235b
  kqueue_descriptor = kqueue ();
Packit ae235b
  result = (kqueue_descriptor != -1);
Packit ae235b
  if (!result)
Packit ae235b
    {
Packit ae235b
      KH_W ("Failed to initialize kqueue\n!");
Packit ae235b
      return GINT_TO_POINTER (FALSE);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  result = socketpair (AF_UNIX, SOCK_STREAM, 0, kqueue_socket_pair);
Packit ae235b
  if (result != 0)
Packit ae235b
    {
Packit ae235b
      KH_W ("Failed to create socket pair\n!");
Packit ae235b
      return GINT_TO_POINTER (FALSE) ;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  result = pthread_create (&kqueue_thread,
Packit ae235b
                           NULL,
Packit ae235b
                           _kqueue_thread_func,
Packit ae235b
                           &kqueue_socket_pair[1]);
Packit ae235b
  if (result != 0)
Packit ae235b
    {
Packit ae235b
      KH_W ("Failed to run kqueue thread\n!");
Packit ae235b
      return GINT_TO_POINTER (FALSE);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  _km_init (_kh_file_appeared_cb);
Packit ae235b
Packit ae235b
  channel = g_io_channel_unix_new (kqueue_socket_pair[0]);
Packit ae235b
  g_io_add_watch (channel, G_IO_IN, process_kqueue_notifications, NULL);
Packit ae235b
Packit ae235b
  subs_hash_table = g_hash_table_new (g_direct_hash, g_direct_equal);
Packit ae235b
Packit ae235b
  KH_W ("started gio kqueue backend\n");
Packit ae235b
  return GINT_TO_POINTER (TRUE);
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/*
Packit ae235b
 * _kh_startup:
Packit ae235b
 * Kqueue backend initialization.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE on success, %FALSE otherwise.
Packit ae235b
 **/
Packit ae235b
gboolean
Packit ae235b
_kh_startup (void)
Packit ae235b
{
Packit ae235b
  static GOnce init_once = G_ONCE_INIT;
Packit ae235b
  g_once (&init_once, _kh_startup_impl, NULL);
Packit ae235b
  return GPOINTER_TO_INT (init_once.retval);
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * _kh_start_watching:
Packit ae235b
 * @sub: a #kqueue_sub
Packit ae235b
 *
Packit ae235b
 * Starts watching on a subscription.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE on success, %FALSE otherwise.
Packit ae235b
 **/
Packit ae235b
gboolean
Packit ae235b
_kh_start_watching (kqueue_sub *sub)
Packit ae235b
{
Packit ae235b
  g_assert (kqueue_socket_pair[0] != -1);
Packit ae235b
  g_assert (sub != NULL);
Packit ae235b
  g_assert (sub->filename != NULL);
Packit ae235b
Packit ae235b
  /* kqueue requires a file descriptor to monitor. Sad but true */
Packit ae235b
#if defined (O_EVTONLY)
Packit ae235b
  sub->fd = open (sub->filename, O_EVTONLY);
Packit ae235b
#else
Packit ae235b
  sub->fd = open (sub->filename, O_RDONLY);
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  if (sub->fd == -1)
Packit ae235b
    {
Packit ae235b
      KH_W ("failed to open file %s (error %d)", sub->filename, errno);
Packit ae235b
      return FALSE;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  _ku_file_information (sub->fd, &sub->is_dir, NULL);
Packit ae235b
  if (sub->is_dir)
Packit ae235b
    {
Packit ae235b
      /* I know, it is very bad to make such decisions in this way and here.
Packit ae235b
       * We already do have an user_data at the #kqueue_sub, and it may point to
Packit ae235b
       * GKqueueFileMonitor or GKqueueDirectoryMonitor. For a directory case,
Packit ae235b
       * we need to scan in contents for the further diffs. Ideally this process
Packit ae235b
       * should be delegated to the GKqueueDirectoryMonitor, but for now I will
Packit ae235b
       * do it in a dirty way right here. */
Packit ae235b
      if (sub->deps)
Packit ae235b
        dl_free (sub->deps);
Packit ae235b
Packit ae235b
      sub->deps = dl_listing (sub->filename);  
Packit ae235b
    }
Packit ae235b
Packit ae235b
  G_LOCK (hash_lock);
Packit ae235b
  g_hash_table_insert (subs_hash_table, GINT_TO_POINTER (sub->fd), sub);
Packit ae235b
  G_UNLOCK (hash_lock);
Packit ae235b
Packit ae235b
  _kqueue_thread_push_fd (sub->fd);
Packit ae235b
  
Packit ae235b
  /* Bump the kqueue thread. It will pick up a new sub entry to monitor */
Packit ae235b
  if (!_ku_write (kqueue_socket_pair[0], "A", 1))
Packit ae235b
    KH_W ("Failed to bump the kqueue thread (add fd, error %d)", errno);
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * _kh_add_sub:
Packit ae235b
 * @sub: a #kqueue_sub
Packit ae235b
 *
Packit ae235b
 * Adds a subscription for monitoring.
Packit ae235b
 *
Packit ae235b
 * This funciton tries to start watching a subscription with
Packit ae235b
 * _kh_start_watching(). On failure, i.e. when a file does not exist yet,
Packit ae235b
 * the subscription will be added to a list of missing files to continue
Packit ae235b
 * watching when the file will appear.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE
Packit ae235b
 **/
Packit ae235b
gboolean
Packit ae235b
_kh_add_sub (kqueue_sub *sub)
Packit ae235b
{
Packit ae235b
  g_assert (sub != NULL);
Packit ae235b
Packit ae235b
  if (!_kh_start_watching (sub))
Packit ae235b
    _km_add_missing (sub);
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * _kh_cancel_sub:
Packit ae235b
 * @sub a #kqueue_sub
Packit ae235b
 *
Packit ae235b
 * Stops monitoring on a subscription.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE
Packit ae235b
 **/
Packit ae235b
gboolean
Packit ae235b
_kh_cancel_sub (kqueue_sub *sub)
Packit ae235b
{
Packit ae235b
  gboolean removed = FALSE;
Packit ae235b
  g_assert (kqueue_socket_pair[0] != -1);
Packit ae235b
  g_assert (sub != NULL);
Packit ae235b
Packit ae235b
  _km_remove (sub);
Packit ae235b
Packit ae235b
  G_LOCK (hash_lock);
Packit ae235b
  removed = g_hash_table_remove (subs_hash_table, GINT_TO_POINTER (sub->fd));
Packit ae235b
  G_UNLOCK (hash_lock);
Packit ae235b
Packit ae235b
  if (removed)
Packit ae235b
    {
Packit ae235b
      /* fd will be closed in the kqueue thread */
Packit ae235b
      _kqueue_thread_remove_fd (sub->fd);
Packit ae235b
Packit ae235b
      /* Bump the kqueue thread. It will pick up a new sub entry to remove*/
Packit ae235b
      if (!_ku_write (kqueue_socket_pair[0], "R", 1))
Packit ae235b
        KH_W ("Failed to bump the kqueue thread (remove fd, error %d)", errno);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * _kh_file_appeared_cb:
Packit ae235b
 * @sub: a #kqueue_sub
Packit ae235b
 *
Packit ae235b
 * A callback function for kqueue-missing subsystem.
Packit ae235b
 *
Packit ae235b
 * Signals that a missing file has finally appeared in the filesystem.
Packit ae235b
 * Emits %G_FILE_MONITOR_EVENT_CREATED.
Packit ae235b
 **/
Packit ae235b
void
Packit ae235b
_kh_file_appeared_cb (kqueue_sub *sub)
Packit ae235b
{
Packit ae235b
  GFile* child;
Packit ae235b
Packit ae235b
  g_assert (sub != NULL);
Packit ae235b
  g_assert (sub->filename);
Packit ae235b
Packit ae235b
  if (!g_file_test (sub->filename, G_FILE_TEST_EXISTS))
Packit ae235b
    return;
Packit ae235b
Packit ae235b
  child = g_file_new_for_path (sub->filename);
Packit ae235b
Packit ae235b
  g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
Packit ae235b
                             child,
Packit ae235b
                             NULL,
Packit ae235b
                             G_FILE_MONITOR_EVENT_CREATED);
Packit ae235b
Packit ae235b
  g_object_unref (child);
Packit ae235b
}