mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-01 23:13:40 +02:00
Merge branch 'wip/lantw/freebsd-kqueue-complex' into 'master'
FreeBSD kqueue file monitor fixes: the complex parts See merge request GNOME/glib!77
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib/gfileutils.h>
|
||||
#include <gio/gfilemonitor.h>
|
||||
#include <gio/glocalfilemonitor.h>
|
||||
#include <gio/giomodule.h>
|
||||
@@ -52,19 +53,44 @@ static int kq_queue = -1;
|
||||
#define G_KQUEUE_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor))
|
||||
|
||||
/* C11 allows type redefinition, but GLib is configured to use C89, which causes
|
||||
* clang to show warnings when we use a C11 feature. Since the C89 requirement
|
||||
* is mostly used to support MSVC, we simply ignore the warning here because
|
||||
* this file is never going to be useful on Windows. */
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wtypedef-redefinition"
|
||||
#endif
|
||||
|
||||
typedef GLocalFileMonitorClass GKqueueFileMonitorClass;
|
||||
|
||||
typedef struct
|
||||
/* When the file we are monitoring is a directory, sub_dir is subscribed to the
|
||||
* directory itself and sub_file is NULL.
|
||||
*
|
||||
* When the file we are monitoring is a regular file, sub_dir is subscribed to
|
||||
* the directory containing the file and sub_file is subscribed to the file
|
||||
* being monitored. We have to monitor both because it is possible that the
|
||||
* file chosen for monitoring doesn't exist when the file monitor is started.
|
||||
* We monitor on its parent in order to get notification when it is created.
|
||||
*
|
||||
* To distinguish between a directory monitor and a regular file monitor, check
|
||||
* whether sub_file is NULL. */
|
||||
typedef struct _GKqueueFileMonitor
|
||||
{
|
||||
GLocalFileMonitor parent_instance;
|
||||
|
||||
kqueue_sub *sub;
|
||||
kqueue_sub *sub_dir;
|
||||
kqueue_sub *sub_file;
|
||||
#ifndef O_EVTONLY
|
||||
GFileMonitor *fallback;
|
||||
GFile *fbfile;
|
||||
#endif
|
||||
} GKqueueFileMonitor;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
GType g_kqueue_file_monitor_get_type (void);
|
||||
G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
|
||||
g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||
@@ -78,12 +104,23 @@ G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL
|
||||
#define O_KQFLAG O_EVTONLY
|
||||
#endif
|
||||
|
||||
#define NOTE_ALL (NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_RENAME)
|
||||
static inline unsigned int
|
||||
note_all (void)
|
||||
{
|
||||
unsigned int notes = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME;
|
||||
#ifdef NOTE_TRUNCATE
|
||||
notes |= NOTE_TRUNCATE;
|
||||
#endif
|
||||
#ifdef NOTE_CLOSE_WRITE
|
||||
notes |= NOTE_CLOSE_WRITE;
|
||||
#endif
|
||||
return notes;
|
||||
}
|
||||
|
||||
static gboolean g_kqueue_file_monitor_cancel (GFileMonitor* monitor);
|
||||
static gboolean g_kqueue_file_monitor_is_supported (void);
|
||||
|
||||
static kqueue_sub *_kqsub_new (const gchar *, GLocalFileMonitor *, GFileMonitorSource *);
|
||||
static kqueue_sub *_kqsub_new (gchar *, gchar *, GKqueueFileMonitor *, GFileMonitorSource *);
|
||||
static void _kqsub_free (kqueue_sub *);
|
||||
static gboolean _kqsub_cancel (kqueue_sub *);
|
||||
|
||||
@@ -138,11 +175,18 @@ g_kqueue_file_monitor_finalize (GObject *object)
|
||||
{
|
||||
GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (object);
|
||||
|
||||
if (kqueue_monitor->sub)
|
||||
if (kqueue_monitor->sub_dir)
|
||||
{
|
||||
_kqsub_cancel (kqueue_monitor->sub);
|
||||
_kqsub_free (kqueue_monitor->sub);
|
||||
kqueue_monitor->sub = NULL;
|
||||
_kqsub_cancel (kqueue_monitor->sub_dir);
|
||||
_kqsub_free (kqueue_monitor->sub_dir);
|
||||
kqueue_monitor->sub_dir = NULL;
|
||||
}
|
||||
|
||||
if (kqueue_monitor->sub_file)
|
||||
{
|
||||
_kqsub_cancel (kqueue_monitor->sub_file);
|
||||
_kqsub_free (kqueue_monitor->sub_file);
|
||||
kqueue_monitor->sub_file = NULL;
|
||||
}
|
||||
|
||||
#ifndef O_EVTONLY
|
||||
@@ -165,17 +209,51 @@ g_kqueue_file_monitor_start (GLocalFileMonitor *local_monitor,
|
||||
GFileMonitorSource *source)
|
||||
{
|
||||
GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (local_monitor);
|
||||
kqueue_sub *sub;
|
||||
const gchar *path;
|
||||
kqueue_sub *sub_dir = NULL, *sub_file = NULL;
|
||||
gchar *path_dir, *path_file, *file_basename;
|
||||
|
||||
path = filename;
|
||||
if (path == NULL)
|
||||
path = dirname;
|
||||
/* There are three possible cases here:
|
||||
*
|
||||
* 1. Directory: dirname != NULL, basename == NULL, filename == NULL
|
||||
* 2. Regular file: dirname != NULL, basename != NULL, filename == NULL
|
||||
* 3. Hard links: dirname == NULL, basename == NULL, filename != NULL
|
||||
*
|
||||
* Note that we don't distinguish between case 2 and 3. Kqueue monitors
|
||||
* files based on file descriptors, so we always receive events come from
|
||||
* hard links.
|
||||
*/
|
||||
if (filename != NULL)
|
||||
{
|
||||
path_dir = g_path_get_dirname (filename);
|
||||
path_file = g_strdup (filename);
|
||||
file_basename = g_path_get_basename (filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
path_dir = g_strdup (dirname);
|
||||
if (basename != NULL)
|
||||
{
|
||||
path_file = g_build_filename (dirname, basename, NULL);
|
||||
file_basename = g_strdup (basename);
|
||||
}
|
||||
else
|
||||
{
|
||||
path_file = NULL;
|
||||
file_basename = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef O_EVTONLY
|
||||
if (_ke_is_excluded (path))
|
||||
if (_ke_is_excluded (path_dir))
|
||||
{
|
||||
GFile *file = g_file_new_for_path (path);
|
||||
GFile *file;
|
||||
if (path_file != NULL)
|
||||
file = g_file_new_for_path (path_file);
|
||||
else
|
||||
file = g_file_new_for_path (path_dir);
|
||||
g_free (path_dir);
|
||||
g_free (path_file);
|
||||
g_free (file_basename);
|
||||
kqueue_monitor->fbfile = file;
|
||||
kqueue_monitor->fallback = _g_poll_file_monitor_new (file);
|
||||
g_signal_connect (kqueue_monitor->fallback, "changed",
|
||||
@@ -191,13 +269,30 @@ g_kqueue_file_monitor_start (GLocalFileMonitor *local_monitor,
|
||||
* file, GIO uses a GKqueueFileMonitor object for that. If a directory
|
||||
* will be created under that path, GKqueueFileMonitor will have to
|
||||
* handle the directory notifications. */
|
||||
sub = _kqsub_new (path, local_monitor, source);
|
||||
if (sub == NULL)
|
||||
return;
|
||||
sub_dir = _kqsub_new (g_steal_pointer (&path_dir), NULL,
|
||||
kqueue_monitor, source);
|
||||
if (!_kqsub_start_watching (sub_dir))
|
||||
_km_add_missing (sub_dir);
|
||||
|
||||
kqueue_monitor->sub = sub;
|
||||
if (!_kqsub_start_watching (sub))
|
||||
_km_add_missing (sub);
|
||||
/* Unlike GInotifyFileMonitor, which always uses a directory monitor
|
||||
* regardless of the type of the file being monitored, kqueue doesn't
|
||||
* give us events generated by files under it when we are monitoring
|
||||
* a directory. We have to monitor the file itself to know changes which
|
||||
* was made to the file itself. */
|
||||
if (path_file != NULL)
|
||||
{
|
||||
sub_file = _kqsub_new (g_steal_pointer (&path_file),
|
||||
g_steal_pointer (&file_basename),
|
||||
kqueue_monitor, source);
|
||||
if (!_kqsub_start_watching (sub_file))
|
||||
_km_add_missing (sub_file);
|
||||
}
|
||||
|
||||
kqueue_monitor->sub_dir = sub_dir;
|
||||
kqueue_monitor->sub_file = sub_file;
|
||||
g_clear_pointer (&path_dir, g_free);
|
||||
g_clear_pointer (&path_file, g_free);
|
||||
g_clear_pointer (&file_basename, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -230,59 +325,127 @@ g_kqueue_file_monitor_callback (gint fd, GIOCondition condition, gpointer user_d
|
||||
struct timespec ts;
|
||||
|
||||
memset (&ts, 0, sizeof(ts));
|
||||
|
||||
/* We must hold the global lock before accessing any kqueue_sub because it is
|
||||
* possible for other threads to call g_kqueue_file_monitor_cancel, which may
|
||||
* free the kqueue_sub struct we are accessing. */
|
||||
G_LOCK (kq_lock);
|
||||
|
||||
while (kevent(fd, NULL, 0, &ev, 1, &ts) > 0)
|
||||
{
|
||||
GFileMonitorEvent mask = 0;
|
||||
|
||||
if (ev.filter != EVFILT_VNODE || ev.udata == NULL)
|
||||
continue;
|
||||
|
||||
sub = ev.udata;
|
||||
sub = ev.udata;
|
||||
source = sub->source;
|
||||
|
||||
/* When we are monitoring a regular file which already exists, ignore
|
||||
* events generated by its parent directory. This has to be the first
|
||||
* check to prevent the following code to emit useless events */
|
||||
if (sub->is_dir && sub->mon->sub_file != NULL && sub->mon->sub_file->fd != -1)
|
||||
continue;
|
||||
|
||||
if (ev.flags & EV_ERROR)
|
||||
ev.fflags = NOTE_REVOKE;
|
||||
|
||||
if (ev.fflags & (NOTE_DELETE | NOTE_REVOKE))
|
||||
{
|
||||
_kqsub_cancel (sub);
|
||||
_km_add_missing (sub);
|
||||
}
|
||||
|
||||
if (sub->is_dir && ev.fflags & (NOTE_WRITE | NOTE_EXTEND))
|
||||
{
|
||||
_kh_dir_diff (sub);
|
||||
/* If we are monitoring on a non-existent regular file, trigger the
|
||||
* rescan of missing files immediately so we don't have to wait for
|
||||
* 4 seconds for discovering missing files. We pass the sub_file
|
||||
* corresponding to the GKqueueFileMonitor to 'check_this_sub_only'
|
||||
* argument to prevent _km_scan_missing from emiting 'CREATED'
|
||||
* events because _kh_dir_diff will do it for us. */
|
||||
if (sub->mon->sub_file != NULL && sub->mon->sub_file->fd == -1)
|
||||
_km_scan_missing (sub->mon->sub_file);
|
||||
|
||||
/* If we are monitoring a regular file, don't emit 'DELETED' events
|
||||
* from the directory monitor because it will be emitted from the
|
||||
* file itself when a NOTE_DELETE is reported on sub_file. */
|
||||
_kh_dir_diff (sub, sub->mon->sub_file == NULL);
|
||||
|
||||
#ifdef NOTE_TRUNCATE
|
||||
ev.fflags &= ~(NOTE_WRITE | NOTE_EXTEND | NOTE_TRUNCATE);
|
||||
#else
|
||||
ev.fflags &= ~(NOTE_WRITE | NOTE_EXTEND);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Here starts the long section of mapping kqueue events to
|
||||
* GFileMonitorEvent. Since kqueue can return multiple events in a
|
||||
* single kevent struct, we must use 'if' instead of 'else if'. */
|
||||
if (ev.fflags & NOTE_DELETE)
|
||||
{
|
||||
mask = G_FILE_MONITOR_EVENT_DELETED;
|
||||
struct stat st;
|
||||
if (fstat (sub->fd, &st) < 0)
|
||||
st.st_nlink = 0;
|
||||
|
||||
g_file_monitor_source_handle_event (source,
|
||||
G_FILE_MONITOR_EVENT_DELETED,
|
||||
sub->basename, NULL, NULL, now);
|
||||
|
||||
/* If the last reference to the file was removed, delete the
|
||||
* subscription from kqueue and add it to the missing list.
|
||||
* If you are monitoring a file which has hard link count higher
|
||||
* than 1, it is possible for the same file to emit 'DELETED'
|
||||
* events multiple times. */
|
||||
if (st.st_nlink == 0)
|
||||
{
|
||||
_kqsub_cancel (sub);
|
||||
_km_add_missing (sub);
|
||||
}
|
||||
}
|
||||
else if (ev.fflags & NOTE_ATTRIB)
|
||||
if (ev.fflags & NOTE_REVOKE)
|
||||
{
|
||||
g_file_monitor_source_handle_event (source,
|
||||
G_FILE_MONITOR_EVENT_UNMOUNTED,
|
||||
sub->basename, NULL, NULL, now);
|
||||
_kqsub_cancel (sub);
|
||||
_km_add_missing (sub);
|
||||
}
|
||||
if (ev.fflags & NOTE_ATTRIB)
|
||||
{
|
||||
mask = G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
|
||||
g_file_monitor_source_handle_event (source,
|
||||
G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED,
|
||||
sub->basename, NULL, NULL, now);
|
||||
}
|
||||
else if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND))
|
||||
#ifdef NOTE_TRUNCATE
|
||||
if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND | NOTE_TRUNCATE))
|
||||
#else
|
||||
if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND))
|
||||
#endif
|
||||
{
|
||||
mask = G_FILE_MONITOR_EVENT_CHANGED;
|
||||
g_file_monitor_source_handle_event (source,
|
||||
G_FILE_MONITOR_EVENT_CHANGED,
|
||||
sub->basename, NULL, NULL, now);
|
||||
}
|
||||
else if (ev.fflags & NOTE_RENAME)
|
||||
if (ev.fflags & NOTE_RENAME)
|
||||
{
|
||||
/* Since there’s apparently no way to get the new name of the
|
||||
* file out of kqueue(), all we can do is say that this one has
|
||||
* been deleted. */
|
||||
mask = G_FILE_MONITOR_EVENT_DELETED;
|
||||
g_file_monitor_source_handle_event (source,
|
||||
G_FILE_MONITOR_EVENT_DELETED,
|
||||
sub->basename, NULL, NULL, now);
|
||||
}
|
||||
else if (ev.fflags & NOTE_REVOKE)
|
||||
#ifdef NOTE_CLOSE_WRITE
|
||||
if (ev.fflags & NOTE_CLOSE_WRITE)
|
||||
{
|
||||
mask = G_FILE_MONITOR_EVENT_UNMOUNTED;
|
||||
g_file_monitor_source_handle_event (source,
|
||||
G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT,
|
||||
sub->basename, NULL, NULL, now);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mask)
|
||||
g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, now);
|
||||
/* Handle the case when a file is created again shortly after it was
|
||||
* deleted. It has to be the last check because 'DELETED' must happen
|
||||
* before 'CREATED'. */
|
||||
if (ev.fflags & (NOTE_DELETE | NOTE_REVOKE))
|
||||
_km_scan_missing (NULL);
|
||||
}
|
||||
|
||||
G_UNLOCK (kq_lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -320,14 +483,28 @@ g_kqueue_file_monitor_cancel (GFileMonitor *monitor)
|
||||
{
|
||||
GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (monitor);
|
||||
|
||||
if (kqueue_monitor->sub)
|
||||
/* We must hold the global lock before calling _kqsub_cancel. However, we
|
||||
* cannot call G_LOCK in _kqsub_cancel because it is also used by
|
||||
* g_kqueue_file_monitor_callback, which already holds the lock itself. */
|
||||
G_LOCK (kq_lock);
|
||||
|
||||
if (kqueue_monitor->sub_dir)
|
||||
{
|
||||
_kqsub_cancel (kqueue_monitor->sub);
|
||||
_kqsub_free (kqueue_monitor->sub);
|
||||
kqueue_monitor->sub = NULL;
|
||||
_kqsub_cancel (kqueue_monitor->sub_dir);
|
||||
_kqsub_free (kqueue_monitor->sub_dir);
|
||||
kqueue_monitor->sub_dir = NULL;
|
||||
}
|
||||
if (kqueue_monitor->sub_file)
|
||||
{
|
||||
_kqsub_cancel (kqueue_monitor->sub_file);
|
||||
_kqsub_free (kqueue_monitor->sub_file);
|
||||
kqueue_monitor->sub_file = NULL;
|
||||
}
|
||||
|
||||
G_UNLOCK (kq_lock);
|
||||
|
||||
#ifndef O_EVTONLY
|
||||
else if (kqueue_monitor->fallback)
|
||||
if (kqueue_monitor->fallback)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (kqueue_monitor->fallback, _fallback_callback, kqueue_monitor);
|
||||
g_file_monitor_cancel (kqueue_monitor->fallback);
|
||||
@@ -341,12 +518,13 @@ g_kqueue_file_monitor_cancel (GFileMonitor *monitor)
|
||||
}
|
||||
|
||||
static kqueue_sub *
|
||||
_kqsub_new (const gchar *filename, GLocalFileMonitor *mon, GFileMonitorSource *source)
|
||||
_kqsub_new (gchar *filename, gchar *basename, GKqueueFileMonitor *mon, GFileMonitorSource *source)
|
||||
{
|
||||
kqueue_sub *sub;
|
||||
|
||||
sub = g_slice_new (kqueue_sub);
|
||||
sub->filename = g_strdup (filename);
|
||||
sub->filename = filename;
|
||||
sub->basename = basename;
|
||||
sub->mon = mon;
|
||||
g_source_ref ((GSource *) source);
|
||||
sub->source = source;
|
||||
@@ -365,19 +543,23 @@ _kqsub_free (kqueue_sub *sub)
|
||||
|
||||
g_source_unref ((GSource *) sub->source);
|
||||
g_free (sub->filename);
|
||||
g_free (sub->basename);
|
||||
g_slice_free (kqueue_sub, sub);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_kqsub_cancel (kqueue_sub *sub)
|
||||
{
|
||||
/* WARNING: Before calling this function, you must hold a lock on kq_lock
|
||||
* or you will cause use-after-free in g_kqueue_file_monitor_callback. */
|
||||
|
||||
struct kevent ev;
|
||||
|
||||
/* Remove the event and close the file descriptor to automatically
|
||||
* delete pending events. */
|
||||
if (sub->fd != -1)
|
||||
{
|
||||
EV_SET (&ev, sub->fd, EVFILT_VNODE, EV_DELETE, NOTE_ALL, 0, sub);
|
||||
EV_SET (&ev, sub->fd, EVFILT_VNODE, EV_DELETE, note_all (), 0, sub);
|
||||
if (kevent (kq_queue, &ev, 1, NULL, 0, NULL) == -1)
|
||||
{
|
||||
g_warning ("Unable to remove event for %s: %s", sub->filename, g_strerror (errno));
|
||||
@@ -425,7 +607,7 @@ _kqsub_start_watching (kqueue_sub *sub)
|
||||
sub->deps = dl_listing (sub->filename);
|
||||
}
|
||||
|
||||
EV_SET (&ev, sub->fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_ALL, 0, sub);
|
||||
EV_SET (&ev, sub->fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, note_all (), 0, sub);
|
||||
if (kevent (kq_queue, &ev, 1, NULL, 0, NULL) == -1)
|
||||
{
|
||||
g_warning ("Unable to add event for %s: %s", sub->filename, g_strerror (errno));
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <gio/glocalfile.h>
|
||||
#include <gio/glocalfilemonitor.h>
|
||||
#include <gio/gfile.h>
|
||||
@@ -38,6 +39,7 @@
|
||||
typedef struct {
|
||||
kqueue_sub *sub;
|
||||
GFileMonitorSource *source;
|
||||
gboolean handle_deleted;
|
||||
} handle_ctx;
|
||||
|
||||
/**
|
||||
@@ -53,6 +55,9 @@ static void
|
||||
handle_created (void *udata, const char *path, ino_t inode)
|
||||
{
|
||||
handle_ctx *ctx = NULL;
|
||||
gint64 now;
|
||||
gchar *fullname;
|
||||
struct stat st;
|
||||
|
||||
(void) inode;
|
||||
ctx = (handle_ctx *) udata;
|
||||
@@ -60,8 +65,16 @@ handle_created (void *udata, const char *path, ino_t inode)
|
||||
g_assert (ctx->sub != NULL);
|
||||
g_assert (ctx->source != NULL);
|
||||
|
||||
now = g_get_monotonic_time ();
|
||||
g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_CREATED, path,
|
||||
NULL, NULL, g_get_monotonic_time ());
|
||||
NULL, NULL, now);
|
||||
|
||||
/* Copied from ih_event_callback to report 'CHANGES_DONE_HINT' earlier. */
|
||||
fullname = g_build_filename (ctx->sub->filename, path, NULL);
|
||||
if (stat (fullname, &st) != 0 || !S_ISREG (st.st_mode) || st.st_nlink != 1)
|
||||
g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, path,
|
||||
NULL, NULL, now);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,6 +97,9 @@ handle_deleted (void *udata, const char *path, ino_t inode)
|
||||
g_assert (ctx->sub != NULL);
|
||||
g_assert (ctx->source != NULL);
|
||||
|
||||
if (!ctx->handle_deleted)
|
||||
return;
|
||||
|
||||
g_file_monitor_source_handle_event (ctx->source, G_FILE_MONITOR_EVENT_DELETED, path,
|
||||
NULL, NULL, g_get_monotonic_time ());
|
||||
}
|
||||
@@ -161,7 +177,7 @@ static const traverse_cbs cbs = {
|
||||
|
||||
|
||||
void
|
||||
_kh_dir_diff (kqueue_sub *sub)
|
||||
_kh_dir_diff (kqueue_sub *sub, gboolean handle_deleted)
|
||||
{
|
||||
dep_list *was;
|
||||
handle_ctx ctx;
|
||||
@@ -169,6 +185,7 @@ _kh_dir_diff (kqueue_sub *sub)
|
||||
memset (&ctx, 0, sizeof (handle_ctx));
|
||||
ctx.sub = sub;
|
||||
ctx.source = sub->source;
|
||||
ctx.handle_deleted = handle_deleted;
|
||||
|
||||
was = sub->deps;
|
||||
sub->deps = dl_listing (sub->filename);
|
||||
|
@@ -28,26 +28,33 @@
|
||||
|
||||
#include "dep-list.h"
|
||||
|
||||
typedef struct _GKqueueFileMonitor GKqueueFileMonitor;
|
||||
|
||||
/**
|
||||
* kqueue_sub:
|
||||
* @mon: a pointer to the GKqueueFileMonitor which holds this subscription
|
||||
* @filename: a name of the file to monitor
|
||||
* @fd: the associated file descriptor (used by kqueue)
|
||||
*
|
||||
* Represents a subscription on a file or directory.
|
||||
* Represents a subscription on a file or directory. To check whether a
|
||||
* subscription is active, check the fd field. If fd is not -1, it is an
|
||||
* active subscription which can emit events from kqueue.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
GLocalFileMonitor *mon;
|
||||
GKqueueFileMonitor *mon;
|
||||
GFileMonitorSource *source;
|
||||
gchar* filename;
|
||||
gchar* basename;
|
||||
int fd;
|
||||
dep_list* deps;
|
||||
int is_dir;
|
||||
} kqueue_sub;
|
||||
|
||||
gboolean _kqsub_start_watching (kqueue_sub *sub);
|
||||
void _kh_dir_diff (kqueue_sub *sub);
|
||||
void _kh_dir_diff (kqueue_sub *sub, gboolean handle_deleted);
|
||||
void _km_add_missing (kqueue_sub *sub);
|
||||
gboolean _km_scan_missing (kqueue_sub *check_this_sub_only);
|
||||
void _km_remove (kqueue_sub *sub);
|
||||
|
||||
#endif /* __KQUEUE_HELPER_H */
|
||||
|
@@ -21,16 +21,13 @@
|
||||
*******************************************************************************/
|
||||
|
||||
#include <glib.h>
|
||||
#include "glib-private.h"
|
||||
|
||||
#include "kqueue-helper.h"
|
||||
|
||||
|
||||
#define SCAN_MISSING_TIME 4 /* 1/4 Hz */
|
||||
|
||||
void _kh_file_appeared_cb (kqueue_sub *sub);
|
||||
|
||||
static gboolean km_scan_missing (gpointer user_data);
|
||||
|
||||
static gboolean km_debug_enabled = FALSE;
|
||||
#define KM_W if (km_debug_enabled) g_warning
|
||||
|
||||
@@ -40,6 +37,12 @@ G_LOCK_DEFINE_STATIC (missing_lock);
|
||||
static volatile gboolean scan_missing_running = FALSE;
|
||||
|
||||
|
||||
static gboolean
|
||||
_km_scan_missing_cb (gpointer user_data)
|
||||
{
|
||||
return _km_scan_missing (NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* _km_add_missing:
|
||||
* @sub: a #kqueue_sub
|
||||
@@ -63,8 +66,12 @@ _km_add_missing (kqueue_sub *sub)
|
||||
|
||||
if (!scan_missing_running)
|
||||
{
|
||||
GSource *source;
|
||||
scan_missing_running = TRUE;
|
||||
g_timeout_add_seconds (SCAN_MISSING_TIME, km_scan_missing, NULL);
|
||||
source = g_timeout_source_new_seconds (SCAN_MISSING_TIME);
|
||||
g_source_set_callback (source, _km_scan_missing_cb, NULL, NULL);
|
||||
g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
|
||||
g_source_unref (source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +84,10 @@ _km_add_missing (kqueue_sub *sub)
|
||||
* Signals that a missing file has finally appeared in the filesystem.
|
||||
* Emits %G_FILE_MONITOR_EVENT_CREATED.
|
||||
**/
|
||||
void
|
||||
static void
|
||||
_kh_file_appeared_cb (kqueue_sub *sub)
|
||||
{
|
||||
GFile *child;
|
||||
gint64 now = g_get_monotonic_time ();
|
||||
|
||||
g_assert (sub != NULL);
|
||||
g_assert (sub->filename);
|
||||
@@ -88,18 +95,14 @@ _kh_file_appeared_cb (kqueue_sub *sub)
|
||||
if (!g_file_test (sub->filename, G_FILE_TEST_EXISTS))
|
||||
return;
|
||||
|
||||
child = g_file_new_for_path (sub->filename);
|
||||
|
||||
g_file_monitor_emit_event (G_FILE_MONITOR (sub->mon),
|
||||
child,
|
||||
NULL,
|
||||
G_FILE_MONITOR_EVENT_CREATED);
|
||||
|
||||
g_object_unref (child);
|
||||
g_file_monitor_source_handle_event (sub->source, G_FILE_MONITOR_EVENT_CREATED,
|
||||
sub->basename, NULL, NULL, now);
|
||||
g_file_monitor_source_handle_event (sub->source, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT,
|
||||
sub->basename, NULL, NULL, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* km_scan_missing:
|
||||
* _km_scan_missing:
|
||||
* @user_data: unused
|
||||
*
|
||||
* The core missing files watching routine.
|
||||
@@ -110,8 +113,8 @@ _kh_file_appeared_cb (kqueue_sub *sub)
|
||||
*
|
||||
* Returns: %FALSE if no missing files left, %TRUE otherwise.
|
||||
**/
|
||||
static gboolean
|
||||
km_scan_missing (gpointer user_data)
|
||||
gboolean
|
||||
_km_scan_missing (kqueue_sub *check_this_sub_only)
|
||||
{
|
||||
GSList *head;
|
||||
GSList *not_missing = NULL;
|
||||
@@ -128,10 +131,14 @@ km_scan_missing (gpointer user_data)
|
||||
g_assert (sub != NULL);
|
||||
g_assert (sub->filename != NULL);
|
||||
|
||||
if (check_this_sub_only != NULL && sub != check_this_sub_only)
|
||||
continue;
|
||||
|
||||
if (_kqsub_start_watching (sub))
|
||||
{
|
||||
KM_W ("file %s now exists, starting watching", sub->filename);
|
||||
_kh_file_appeared_cb (sub);
|
||||
if (check_this_sub_only == NULL)
|
||||
_kh_file_appeared_cb (sub);
|
||||
not_missing = g_slist_prepend (not_missing, head);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user