Date: Mon, 7 May 2018 18:42:54 +0000 (UTC) From: Koop Mast <kwm@FreeBSD.org> To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r469308 - in head/devel/glib20: . files Message-ID: <201805071842.w47IgsrJ046283@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kwm Date: Mon May 7 18:42:54 2018 New Revision: 469308 URL: https://svnweb.freebsd.org/changeset/ports/469308 Log: Update the kqueue file monitering backend. This update is a backport of work done by Martin Pieuchot (mpi@openbsd). And will be available in the next major Glib released in September. This update simplifies the backend and fixes a number of races and other issues in the old backend. Many thanks to Martin Pieuchot mpi@openbsd for the rewrite! PR: 226920 Submitted by: Ting-Wei Lan <lantw44@gmail.com>, lightside@gmx.com Obtained from: glib upstream Added: head/devel/glib20/files/patch-gio_filemonitor (contents, props changed) Deleted: head/devel/glib20/files/patch-gio_kqueue_gkqueuefilemonitor.c head/devel/glib20/files/patch-gio_kqueue_kqueue-helper.c Modified: head/devel/glib20/Makefile Modified: head/devel/glib20/Makefile ============================================================================== --- head/devel/glib20/Makefile Mon May 7 18:18:17 2018 (r469307) +++ head/devel/glib20/Makefile Mon May 7 18:42:54 2018 (r469308) @@ -3,7 +3,7 @@ PORTNAME= glib PORTVERSION= 2.50.3 -PORTREVISION= 2 +PORTREVISION= 3 PORTEPOCH= 1 CATEGORIES= devel MASTER_SITES= GNOME Added: head/devel/glib20/files/patch-gio_filemonitor ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/devel/glib20/files/patch-gio_filemonitor Mon May 7 18:42:54 2018 (r469308) @@ -0,0 +1,1038 @@ +# gio: Always purge kqueue subs from missing list +# https://gitlab.gnome.org/GNOME/glib/commit/e305fe971e4647d971428a772b7290b9c308a96f +# kqueue: Fix invalid emission of G_FILE_MONITOR_EVENT_MOVED event +# https://gitlab.gnome.org/GNOME/glib/commit/76072a2dde4a4acc8be8d3c47efbc6811ebe0c1e +# kqueue: Multiple fixes and simplifications +# https://gitlab.gnome.org/GNOME/glib/commit/aa39a0557c679fc345b0ba72a87c33152eb8ebcd +# gpollfilemonitor: Fix use-after-free caused by leaking GSource +# https://gitlab.gnome.org/GNOME/glib/commit/ba4a9538e14e8ba0ea037cab5f4b23aa47272a4c +# Reorder operations in _kqsub_cancel() to prevent races. +# https://gitlab.gnome.org/GNOME/glib/commit/ab179184b883ad378a420223f378071821f0c8b9 +# Convert gio/kqueue/Makefile.am to gio/kqueue/Makefile.in patch + +--- gio/gpollfilemonitor.c.orig 2016-10-22 05:17:34 UTC ++++ gio/gpollfilemonitor.c +@@ -50,7 +50,9 @@ g_poll_file_monitor_finalize (GObject* o + + poll_monitor = G_POLL_FILE_MONITOR (object); + ++ g_poll_file_monitor_cancel (G_FILE_MONITOR (poll_monitor)); + g_object_unref (poll_monitor->file); ++ g_clear_object (&poll_monitor->last_info); + + G_OBJECT_CLASS (g_poll_file_monitor_parent_class)->finalize (object); + } +--- gio/kqueue/Makefile.in.orig 2017-02-13 15:22:04 UTC ++++ gio/kqueue/Makefile.in +@@ -183,9 +183,8 @@ LTLIBRARIES = $(installed_test_LTLIBRARI + libkqueue_la_LIBADD = + am__objects_1 = + am_libkqueue_la_OBJECTS = libkqueue_la-gkqueuefilemonitor.lo \ +- libkqueue_la-kqueue-helper.lo libkqueue_la-kqueue-thread.lo \ +- libkqueue_la-kqueue-sub.lo libkqueue_la-kqueue-missing.lo \ +- libkqueue_la-kqueue-utils.lo libkqueue_la-kqueue-exclusions.lo \ ++ libkqueue_la-kqueue-helper.lo \ ++ libkqueue_la-kqueue-missing.lo \ + libkqueue_la-dep-list.lo $(am__objects_1) + libkqueue_la_OBJECTS = $(am_libkqueue_la_OBJECTS) + AM_V_lt = $(am__v_lt_@AM_V@) +--- gio/kqueue/gkqueuefilemonitor.c.orig 2016-10-22 05:18:22 UTC ++++ gio/kqueue/gkqueuefilemonitor.c +@@ -22,33 +22,73 @@ + + #include "config.h" + +-#include "gkqueuefilemonitor.h" +-#include "kqueue-helper.h" +-#include "kqueue-exclusions.h" ++#include <sys/types.h> ++#include <sys/event.h> ++#include <sys/time.h> ++#include <sys/socket.h> ++#include <sys/stat.h> ++ ++#include <errno.h> ++#include <fcntl.h> ++#include <string.h> ++ ++#include <glib-object.h> ++#include <gio/gfilemonitor.h> ++#include <gio/glocalfilemonitor.h> ++#include <gio/giomodule.h> + #include <gio/gpollfilemonitor.h> + #include <gio/gfile.h> +-#include <gio/giomodule.h> ++#include <glib-unix.h> ++#include "glib-private.h" + ++#include "kqueue-helper.h" ++#include "dep-list.h" + +-struct _GKqueueFileMonitor ++G_LOCK_DEFINE_STATIC (kq_lock); ++static GSource *kq_source; ++static int kq_queue = -1; ++ ++#define G_TYPE_KQUEUE_FILE_MONITOR (g_kqueue_file_monitor_get_type ()) ++#define G_KQUEUE_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ ++ G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor)) ++ ++typedef GLocalFileMonitorClass GKqueueFileMonitorClass; ++ ++typedef struct + { + GLocalFileMonitor parent_instance; + + kqueue_sub *sub; +- ++#ifndef O_EVTONLY + GFileMonitor *fallback; + GFile *fbfile; +-}; ++#endif ++} GKqueueFileMonitor; ++ ++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, ++ g_define_type_id, ++ "kqueue", ++ 20)) ++ ++#ifndef O_EVTONLY ++#define O_KQFLAG O_RDONLY ++#else ++#define O_KQFLAG O_EVTONLY ++#endif ++ ++#define NOTE_ALL (NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_RENAME) + + static gboolean g_kqueue_file_monitor_cancel (GFileMonitor* monitor); ++static gboolean g_kqueue_file_monitor_is_supported (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, +- g_define_type_id, +- "kqueue", +- 20)) ++static kqueue_sub *_kqsub_new (const gchar *, GLocalFileMonitor *, GFileMonitorSource *); ++static void _kqsub_free (kqueue_sub *); ++static gboolean _kqsub_cancel (kqueue_sub *); + + ++#ifndef O_EVTONLY + static void + _fallback_callback (GFileMonitor *unused, + GFile *first, +@@ -57,21 +97,41 @@ _fallback_callback (GFileMonitor *u + gpointer udata) + { + GKqueueFileMonitor *kq_mon = G_KQUEUE_FILE_MONITOR (udata); +- GFileMonitor *mon = G_FILE_MONITOR (kq_mon); +- g_assert (kq_mon != NULL); +- g_assert (mon != NULL); +- (void) unused; +- +- if (event == G_FILE_MONITOR_EVENT_CHANGED) +- { +- GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (kq_mon); + +- _kh_dir_diff (kq_mon->sub, local_monitor->source); +- } +- else +- g_file_monitor_emit_event (mon, first, second, event); ++ g_file_monitor_emit_event (G_FILE_MONITOR (kq_mon), first, second, event); + } + ++/* ++ * _ke_is_excluded: ++ * @full_path - a path to file to check. ++ * ++ * Returns: TRUE if the file should be excluded from the kqueue-powered ++ * monitoring, FALSE otherwise. ++ **/ ++gboolean ++_ke_is_excluded (const char *full_path) ++{ ++ GFile *f = NULL; ++ GMount *mount = NULL; ++ ++ f = g_file_new_for_path (full_path); ++ ++ if (f != NULL) { ++ mount = g_file_find_enclosing_mount (f, NULL, NULL); ++ g_object_unref (f); ++ } ++ ++ if ((mount != NULL && (g_mount_can_unmount (mount))) || g_str_has_prefix (full_path, "/mnt/")) ++ { ++ g_warning ("Excluding %s from kernel notification, falling back to poll", full_path); ++ if (mount) ++ g_object_unref (mount); ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++#endif /* !O_EVTONLY */ + + static void + g_kqueue_file_monitor_finalize (GObject *object) +@@ -80,16 +140,18 @@ g_kqueue_file_monitor_finalize (GObject + + if (kqueue_monitor->sub) + { +- _kh_cancel_sub (kqueue_monitor->sub); +- _kh_sub_free (kqueue_monitor->sub); ++ _kqsub_cancel (kqueue_monitor->sub); ++ _kqsub_free (kqueue_monitor->sub); + kqueue_monitor->sub = NULL; + } + ++#ifndef O_EVTONLY + if (kqueue_monitor->fallback) + g_object_unref (kqueue_monitor->fallback); + + if (kqueue_monitor->fbfile) + g_object_unref (kqueue_monitor->fbfile); ++#endif + + if (G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) + (*G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) (object); +@@ -103,21 +165,25 @@ g_kqueue_file_monitor_start (GLocalFileM + GFileMonitorSource *source) + { + GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (local_monitor); +- GObject *obj; +- GKqueueFileMonitorClass *klass; +- GObjectClass *parent_class; +- kqueue_sub *sub = NULL; +- gboolean ret_kh_startup = FALSE; +- const gchar *path = NULL; +- +- +- ret_kh_startup = _kh_startup (); +- g_assert (ret_kh_startup); ++ kqueue_sub *sub; ++ const gchar *path; + + path = filename; +- if (!path) ++ if (path == NULL) + path = dirname; + ++#ifndef O_EVTONLY ++ if (_ke_is_excluded (path)) ++ { ++ GFile *file = g_file_new_for_path (path); ++ kqueue_monitor->fbfile = file; ++ kqueue_monitor->fallback = _g_poll_file_monitor_new (file); ++ g_signal_connect (kqueue_monitor->fallback, "changed", ++ G_CALLBACK (_fallback_callback), kqueue_monitor); ++ return; ++ } ++#endif ++ + /* For a directory monitor, create a subscription object anyway. + * It will be used for directory diff calculation routines. + * Wait, directory diff in a GKqueueFileMonitor? +@@ -125,33 +191,13 @@ g_kqueue_file_monitor_start (GLocalFileM + * 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 = _kh_sub_new (path, TRUE, source); +- +- /* FIXME: what to do about errors here? we can't return NULL or another +- * kind of error and an assertion is probably too hard (same issue as in +- * the inotify backend) */ +- g_assert (sub != NULL); + kqueue_monitor->sub = sub; +- +- if (!_ke_is_excluded (path)) +- _kh_add_sub (sub); +- else +- { +- GFile *file = g_file_new_for_path (path); +- kqueue_monitor->fbfile = file; +- kqueue_monitor->fallback = _g_poll_file_monitor_new (file); +- g_signal_connect (kqueue_monitor->fallback, +- "changed", +- G_CALLBACK (_fallback_callback), +- kqueue_monitor); +- } +-} +- +-static gboolean +-g_kqueue_file_monitor_is_supported (void) +-{ +- return _kh_startup (); ++ if (!_kqsub_start_watching (sub)) ++ _km_add_missing (sub); + } + + static void +@@ -175,24 +221,218 @@ g_kqueue_file_monitor_init (GKqueueFileM + } + + static gboolean ++g_kqueue_file_monitor_callback (gint fd, GIOCondition condition, gpointer user_data) ++{ ++ gint64 now = g_source_get_time (kq_source); ++ kqueue_sub *sub; ++ GFileMonitorSource *source; ++ struct kevent ev; ++ struct timespec ts; ++ ++ memset (&ts, 0, sizeof(ts)); ++ while (kevent(fd, NULL, 0, &ev, 1, &ts) > 0) ++ { ++ GFileMonitorEvent mask = 0; ++ ++ if (ev.filter != EVFILT_VNODE || ev.udata == NULL) ++ continue; ++ ++ sub = ev.udata; ++ source = sub->source; ++ ++ 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); ++ ev.fflags &= ~(NOTE_WRITE | NOTE_EXTEND); ++ } ++ ++ if (ev.fflags & NOTE_DELETE) ++ { ++ mask = G_FILE_MONITOR_EVENT_DELETED; ++ } ++ else if (ev.fflags & NOTE_ATTRIB) ++ { ++ mask = G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; ++ } ++ else if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND)) ++ { ++ mask = G_FILE_MONITOR_EVENT_CHANGED; ++ } ++ else 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; ++ } ++ else if (ev.fflags & NOTE_REVOKE) ++ { ++ mask = G_FILE_MONITOR_EVENT_UNMOUNTED; ++ } ++ ++ if (mask) ++ g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, now); ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++g_kqueue_file_monitor_is_supported (void) ++{ ++ int errsv; ++ ++ G_LOCK (kq_lock); ++ ++ if (kq_queue == -1) ++ { ++ kq_queue = kqueue (); ++ errsv = errno; ++ ++ if (kq_queue == -1) ++ { ++ g_warning ("Unable to create a kqueue: %s", g_strerror (errsv)); ++ G_UNLOCK (kq_lock); ++ return FALSE; ++ } ++ ++ kq_source = g_unix_fd_source_new (kq_queue, G_IO_IN); ++ g_source_set_callback (kq_source, (GSourceFunc) g_kqueue_file_monitor_callback, NULL, NULL); ++ g_source_attach (kq_source, GLIB_PRIVATE_CALL (g_get_worker_context) ()); ++ } ++ ++ G_UNLOCK (kq_lock); ++ ++ return TRUE; ++} ++ ++static gboolean + g_kqueue_file_monitor_cancel (GFileMonitor *monitor) + { + GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (monitor); + + if (kqueue_monitor->sub) + { +- _kh_cancel_sub (kqueue_monitor->sub); +- _kh_sub_free (kqueue_monitor->sub); ++ _kqsub_cancel (kqueue_monitor->sub); ++ _kqsub_free (kqueue_monitor->sub); + kqueue_monitor->sub = NULL; + } ++#ifndef O_EVTONLY + else if (kqueue_monitor->fallback) + { + g_signal_handlers_disconnect_by_func (kqueue_monitor->fallback, _fallback_callback, kqueue_monitor); + g_file_monitor_cancel (kqueue_monitor->fallback); + } ++#endif + + if (G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) + (*G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) (monitor); + + return TRUE; + } ++ ++static kqueue_sub * ++_kqsub_new (const gchar *filename, GLocalFileMonitor *mon, GFileMonitorSource *source) ++{ ++ kqueue_sub *sub; ++ ++ sub = g_slice_new (kqueue_sub); ++ sub->filename = g_strdup (filename); ++ sub->mon = mon; ++ g_source_ref ((GSource *) source); ++ sub->source = source; ++ sub->fd = -1; ++ sub->deps = NULL; ++ sub->is_dir = 0; ++ ++ return sub; ++} ++ ++static void ++_kqsub_free (kqueue_sub *sub) ++{ ++ g_assert (sub->deps == NULL); ++ g_assert (sub->fd == -1); ++ ++ g_source_unref ((GSource *) sub->source); ++ g_free (sub->filename); ++ g_slice_free (kqueue_sub, sub); ++} ++ ++static gboolean ++_kqsub_cancel (kqueue_sub *sub) ++{ ++ 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); ++ if (kevent (kq_queue, &ev, 1, NULL, 0, NULL) == -1) ++ { ++ g_warning ("Unable to remove event for %s: %s", sub->filename, g_strerror (errno)); ++ return FALSE; ++ } ++ close (sub->fd); ++ sub->fd = -1; ++ } ++ ++ _km_remove (sub); ++ ++ if (sub->deps) ++ { ++ dl_free (sub->deps); ++ sub->deps = NULL; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++_kqsub_start_watching (kqueue_sub *sub) ++{ ++ struct stat st; ++ struct kevent ev; ++ ++ sub->fd = open (sub->filename, O_KQFLAG); ++ if (sub->fd == -1) ++ return FALSE; ++ ++ if (fstat (sub->fd, &st) == -1) ++ { ++ g_warning ("fstat failed for %s: %s", sub->filename, g_strerror (errno)); ++ close (sub->fd); ++ sub->fd = -1; ++ return FALSE; ++ } ++ ++ sub->is_dir = (st.st_mode & S_IFDIR) ? 1 : 0; ++ if (sub->is_dir) ++ { ++ if (sub->deps) ++ dl_free (sub->deps); ++ ++ sub->deps = dl_listing (sub->filename); ++ } ++ ++ 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)); ++ close (sub->fd); ++ sub->fd = -1; ++ return FALSE; ++ } ++ ++ return TRUE; ++} +--- gio/kqueue/kqueue-helper.c.orig 2016-10-22 05:18:22 UTC ++++ gio/kqueue/kqueue-helper.c +@@ -34,81 +34,6 @@ + #include <errno.h> + #include <pthread.h> + #include "kqueue-helper.h" +-#include "kqueue-utils.h" +-#include "kqueue-thread.h" +-#include "kqueue-missing.h" +-#include "kqueue-exclusions.h" +- +-static gboolean kh_debug_enabled = FALSE; +-#define KH_W if (kh_debug_enabled) g_warning +- +-static GHashTable *subs_hash_table = NULL; +-G_LOCK_DEFINE_STATIC (hash_lock); +- +-static int kqueue_descriptor = -1; +-static int kqueue_socket_pair[] = {-1, -1}; +-static pthread_t kqueue_thread; +- +- +-void _kh_file_appeared_cb (kqueue_sub *sub); +- +-/** +- * accessor function for kqueue_descriptor +- **/ +-int +-get_kqueue_descriptor() +-{ +- return kqueue_descriptor; +-} +- +-/** +- * convert_kqueue_events_to_gio: +- * @flags: a set of kqueue filter flags +- * @done: a pointer to #gboolean indicating that the +- * conversion has been done (out) +- * +- * Translates kqueue filter flags into GIO event flags. +- * +- * Returns: a #GFileMonitorEvent +- **/ +-static GFileMonitorEvent +-convert_kqueue_events_to_gio (uint32_t flags, gboolean *done) +-{ +- g_assert (done != NULL); +- *done = FALSE; +- +- /* TODO: The following notifications should be emulated, if possible: +- * - G_FILE_MONITOR_EVENT_PRE_UNMOUNT +- */ +- if (flags & NOTE_DELETE) +- { +- *done = TRUE; +- return G_FILE_MONITOR_EVENT_DELETED; +- } +- if (flags & NOTE_ATTRIB) +- { +- *done = TRUE; +- return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; +- } +- if (flags & (NOTE_WRITE | NOTE_EXTEND)) +- { +- *done = TRUE; +- return G_FILE_MONITOR_EVENT_CHANGED; +- } +- if (flags & NOTE_RENAME) +- { +- *done = TRUE; +- return G_FILE_MONITOR_EVENT_MOVED; +- } +- if (flags & NOTE_REVOKE) +- { +- *done = TRUE; +- return G_FILE_MONITOR_EVENT_UNMOUNTED; +- } +- +- /* done is FALSE */ +- return 0; +-} + + typedef struct { + kqueue_sub *sub; +@@ -236,323 +161,21 @@ static const traverse_cbs cbs = { + + + void +-_kh_dir_diff (kqueue_sub *sub, GFileMonitorSource *source) ++_kh_dir_diff (kqueue_sub *sub) + { + dep_list *was; + handle_ctx ctx; + +- g_assert (sub != NULL); +- g_assert (source != NULL); +- + memset (&ctx, 0, sizeof (handle_ctx)); + ctx.sub = sub; +- ctx.source = source; ++ ctx.source = sub->source; + + was = sub->deps; + sub->deps = dl_listing (sub->filename); +- ++ + dl_calculate (was, sub->deps, &cbs, &ctx); + + dl_free (was); + } + + +-/** +- * process_kqueue_notifications: +- * @gioc: unused. +- * @cond: unused. +- * @data: unused. +- * +- * Processes notifications, coming from the kqueue thread. +- * +- * Reads notifications from the command file descriptor, emits the +- * "changed" event on the appropriate monitor. +- * +- * A typical GIO Channel callback function. +- * +- * Returns: %TRUE +- **/ +-static gboolean +-process_kqueue_notifications (GIOChannel *gioc, +- GIOCondition cond, +- gpointer data) +-{ +- struct kqueue_notification n; +- kqueue_sub *sub = NULL; +- GFileMonitorSource *source = NULL; +- GFileMonitorEvent mask = 0; +- +- g_assert (kqueue_socket_pair[0] != -1); +- if (!_ku_read (kqueue_socket_pair[0], &n, sizeof (struct kqueue_notification))) +- { +- KH_W ("Failed to read a kqueue notification, error %d", errno); +- return TRUE; +- } +- +- G_LOCK (hash_lock); +- sub = (kqueue_sub *) g_hash_table_lookup (subs_hash_table, GINT_TO_POINTER (n.fd)); +- G_UNLOCK (hash_lock); +- +- if (sub == NULL) +- { +- KH_W ("Got a notification for a deleted or non-existing subscription %d", +- n.fd); +- return TRUE; +- } +- +- source = sub->user_data; +- g_assert (source != NULL); +- +- if (n.flags & (NOTE_DELETE | NOTE_REVOKE)) +- { +- if (sub->deps) +- { +- dl_free (sub->deps); +- sub->deps = NULL; +- } +- _km_add_missing (sub); +- +- if (!(n.flags & NOTE_REVOKE)) +- { +- /* Note that NOTE_REVOKE is issued by the kqueue thread +- * on EV_ERROR kevent. In this case, a file descriptor is +- * already closed from the kqueue thread, no need to close +- * it manually */ +- _kh_cancel_sub (sub); +- } +- } +- +- if (sub->is_dir && n.flags & (NOTE_WRITE | NOTE_EXTEND)) +- { +- _kh_dir_diff (sub, source); +- n.flags &= ~(NOTE_WRITE | NOTE_EXTEND); +- } +- +- if (n.flags) +- { +- gboolean done = FALSE; +- mask = convert_kqueue_events_to_gio (n.flags, &done); +- if (done == TRUE) +- g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, g_get_monotonic_time ()); +- } +- +- return TRUE; +-} +- +- +-/* +- * _kh_startup_impl: +- * @unused: unused +- * +- * Kqueue backend startup code. Should be called only once. +- * +- * Returns: %TRUE on success, %FALSE otherwise. +- **/ +-static gpointer +-_kh_startup_impl (gpointer unused) +-{ +- GIOChannel *channel = NULL; +- gboolean result = FALSE; +- +- kqueue_descriptor = kqueue (); +- result = (kqueue_descriptor != -1); +- if (!result) +- { +- KH_W ("Failed to initialize kqueue\n!"); +- return GINT_TO_POINTER (FALSE); +- } +- +- result = socketpair (AF_UNIX, SOCK_STREAM, 0, kqueue_socket_pair); +- if (result != 0) +- { +- KH_W ("Failed to create socket pair\n!"); +- return GINT_TO_POINTER (FALSE) ; +- } +- +- result = pthread_create (&kqueue_thread, +- NULL, +- _kqueue_thread_func, +- &kqueue_socket_pair[1]); +- if (result != 0) +- { +- KH_W ("Failed to run kqueue thread\n!"); +- return GINT_TO_POINTER (FALSE); +- } +- +- _km_init (_kh_file_appeared_cb); +- +- channel = g_io_channel_unix_new (kqueue_socket_pair[0]); +- g_io_add_watch (channel, G_IO_IN, process_kqueue_notifications, NULL); +- +- subs_hash_table = g_hash_table_new (g_direct_hash, g_direct_equal); +- +- KH_W ("started gio kqueue backend\n"); +- return GINT_TO_POINTER (TRUE); +-} +- +- +-/* +- * _kh_startup: +- * Kqueue backend initialization. +- * +- * Returns: %TRUE on success, %FALSE otherwise. +- **/ +-gboolean +-_kh_startup (void) +-{ +- static GOnce init_once = G_ONCE_INIT; +- g_once (&init_once, _kh_startup_impl, NULL); +- return GPOINTER_TO_INT (init_once.retval); +-} +- +- +-/** +- * _kh_start_watching: +- * @sub: a #kqueue_sub +- * +- * Starts watching on a subscription. +- * +- * Returns: %TRUE on success, %FALSE otherwise. +- **/ +-gboolean +-_kh_start_watching (kqueue_sub *sub) +-{ +- g_assert (kqueue_socket_pair[0] != -1); +- g_assert (sub != NULL); +- g_assert (sub->filename != NULL); +- +- /* kqueue requires a file descriptor to monitor. Sad but true */ +-#if defined (O_EVTONLY) +- sub->fd = open (sub->filename, O_EVTONLY); +-#else +- sub->fd = open (sub->filename, O_RDONLY); +-#endif +- +- if (sub->fd == -1) +- { +- KH_W ("failed to open file %s (error %d)", sub->filename, errno); +- return FALSE; +- } +- +- _ku_file_information (sub->fd, &sub->is_dir, NULL); +- if (sub->is_dir) +- { +- /* I know, it is very bad to make such decisions in this way and here. +- * We already do have an user_data at the #kqueue_sub, and it may point to +- * GKqueueFileMonitor or GKqueueDirectoryMonitor. For a directory case, +- * we need to scan in contents for the further diffs. Ideally this process +- * should be delegated to the GKqueueDirectoryMonitor, but for now I will +- * do it in a dirty way right here. */ +- if (sub->deps) +- dl_free (sub->deps); +- +- sub->deps = dl_listing (sub->filename); +- } +- +- G_LOCK (hash_lock); +- g_hash_table_insert (subs_hash_table, GINT_TO_POINTER (sub->fd), sub); +- G_UNLOCK (hash_lock); +- +- _kqueue_thread_push_fd (sub->fd); +- +- /* Bump the kqueue thread. It will pick up a new sub entry to monitor */ +- if (!_ku_write (kqueue_socket_pair[0], "A", 1)) +- KH_W ("Failed to bump the kqueue thread (add fd, error %d)", errno); +- return TRUE; +-} +- +- +-/** +- * _kh_add_sub: +- * @sub: a #kqueue_sub +- * +- * Adds a subscription for monitoring. +- * +- * This funciton tries to start watching a subscription with +- * _kh_start_watching(). On failure, i.e. when a file does not exist yet, +- * the subscription will be added to a list of missing files to continue +- * watching when the file will appear. +- * +- * Returns: %TRUE +- **/ +-gboolean +-_kh_add_sub (kqueue_sub *sub) +-{ +- g_assert (sub != NULL); +- +- if (!_kh_start_watching (sub)) +- _km_add_missing (sub); +- +- return TRUE; +-} +- +- +-/** +- * _kh_cancel_sub: +- * @sub a #kqueue_sub +- * +- * Stops monitoring on a subscription. +- * +- * Returns: %TRUE +- **/ +-gboolean +-_kh_cancel_sub (kqueue_sub *sub) +-{ +- gboolean missing = FALSE; +- g_assert (kqueue_socket_pair[0] != -1); +- g_assert (sub != NULL); +- +- G_LOCK (hash_lock); +- missing = !g_hash_table_remove (subs_hash_table, GINT_TO_POINTER (sub->fd)); +- G_UNLOCK (hash_lock); +- +- if (missing) +- { +- /* If there were no fd for this subscription, file is still +- * missing. */ +- KH_W ("Removing subscription from missing"); +- _km_remove (sub); +- } +- else +- { +- /* fd will be closed in the kqueue thread */ +- _kqueue_thread_remove_fd (sub->fd); +- +- /* Bump the kqueue thread. It will pick up a new sub entry to remove*/ +- if (!_ku_write (kqueue_socket_pair[0], "R", 1)) +- KH_W ("Failed to bump the kqueue thread (remove fd, error %d)", errno); +- } +- +- return TRUE; +-} +- +- +-/** +- * _kh_file_appeared_cb: +- * @sub: a #kqueue_sub +- * +- * A callback function for kqueue-missing subsystem. +- * +- * Signals that a missing file has finally appeared in the filesystem. +- * Emits %G_FILE_MONITOR_EVENT_CREATED. +- **/ +-void +-_kh_file_appeared_cb (kqueue_sub *sub) +-{ +- GFile* child; +- +- g_assert (sub != NULL); +- g_assert (sub->filename); +- +- 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->user_data), +- child, +- NULL, +- G_FILE_MONITOR_EVENT_CREATED); +- +- g_object_unref (child); +-} +--- gio/kqueue/kqueue-helper.h.orig 2016-10-22 05:18:22 UTC ++++ gio/kqueue/kqueue-helper.h +@@ -23,16 +23,31 @@ + #ifndef __KQUEUE_HELPER_H + #define __KQUEUE_HELPER_H + +-#include "kqueue-sub.h" + #include <gio/glocalfilemonitor.h> + #include <gio/gfilemonitor.h> + +-gboolean _kh_startup (void); +-gboolean _kh_add_sub (kqueue_sub *sub); +-gboolean _kh_cancel_sub (kqueue_sub *sub); ++#include "dep-list.h" + +-gboolean _kh_start_watching (kqueue_sub *sub); ++/** ++ * kqueue_sub: ++ * @filename: a name of the file to monitor ++ * @fd: the associated file descriptor (used by kqueue) ++ * ++ * Represents a subscription on a file or directory. ++ */ ++typedef struct ++{ ++ GLocalFileMonitor *mon; ++ GFileMonitorSource *source; ++ gchar* filename; ++ int fd; ++ dep_list* deps; ++ int is_dir; ++} kqueue_sub; + +-void _kh_dir_diff (kqueue_sub *sub, GFileMonitorSource *source); ++gboolean _kqsub_start_watching (kqueue_sub *sub); ++void _kh_dir_diff (kqueue_sub *sub); ++void _km_add_missing (kqueue_sub *sub); ++void _km_remove (kqueue_sub *sub); + + #endif /* __KQUEUE_HELPER_H */ +--- gio/kqueue/kqueue-missing.c.orig 2016-10-22 05:18:22 UTC ++++ gio/kqueue/kqueue-missing.c +@@ -23,12 +23,12 @@ + #include <glib.h> + + #include "kqueue-helper.h" +-#include "kqueue-sub.h" +-#include "kqueue-missing.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; +@@ -38,21 +38,6 @@ static GSList *missing_subs_list = NULL; + G_LOCK_DEFINE_STATIC (missing_lock); + + static volatile gboolean scan_missing_running = FALSE; +-static on_create_cb file_appeared_callback; +- +- +-/** +- * _km_init: +- * @cb: a callback function. It will be called when a watched file +- * will appear. +- * +- * Initialize the kqueue-missing module (optional). +- **/ +-void +-_km_init (on_create_cb cb) +-{ +- file_appeared_callback = cb; +-} + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201805071842.w47IgsrJ046283>