[glib/wip/mount-watcher: 21/24] inotify: implement the "boredom" algorithm
- From: Ryan Lortie <desrt src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/mount-watcher: 21/24] inotify: implement the "boredom" algorithm
- Date: Fri, 16 Jan 2015 23:59:57 +0000 (UTC)
commit 54ca2ad94e184cff3c5f6df4882237c5fc164901
Author: Ryan Lortie <desrt desrt ca>
Date: Thu Jan 15 23:30:14 2015 -0500
inotify: implement the "boredom" algorithm
Also: add a hashtable for move pairing
gio/glocalfilemonitor.c | 2 +-
gio/inotify/inotify-kernel.c | 184 ++++++++++++++++++++++++++++++-----------
2 files changed, 135 insertions(+), 51 deletions(-)
---
diff --git a/gio/glocalfilemonitor.c b/gio/glocalfilemonitor.c
index cd5aa7a..8dbbdf8 100644
--- a/gio/glocalfilemonitor.c
+++ b/gio/glocalfilemonitor.c
@@ -193,7 +193,7 @@ g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms,
g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
- return TRUE;
+ return FALSE;
}
static gboolean
diff --git a/gio/inotify/inotify-kernel.c b/gio/inotify/inotify-kernel.c
index 2ea1609..3ca8414 100644
--- a/gio/inotify/inotify-kernel.c
+++ b/gio/inotify/inotify-kernel.c
@@ -35,6 +35,12 @@
#include "glib-private.h"
+/* Thresholds for the boredom algorithm */
+#define BOREDOM_MIN (1 * G_TIME_SPAN_MILLISECOND)
+#define BOREDOM_MAX (1 * G_TIME_SPAN_SECOND)
+#define BOREDOM_THRESHOLD (16 * G_TIME_SPAN_MILLISECOND)
+#define BOREDOM_FACTOR (2)
+
/* Define limits on the maximum amount of time and maximum amount of
* interceding events between FROM/TO that can be merged.
*/
@@ -83,17 +89,21 @@ _ik_event_free (ik_event_t *event)
typedef struct
{
- GSource source;
+ GSource source;
+
+ GQueue queue;
+ gpointer fd_tag;
+ gint fd;
- gpointer fd_tag;
- GQueue queue;
- gint fd;
+ GHashTable *unmatched_moves;
+ GTimeSpan boredom;
+ gboolean ignored;
} InotifyKernelSource;
static InotifyKernelSource *inotify_source;
static gint64
-ik_source_get_ready_time (InotifyKernelSource *iks)
+ik_source_get_dispatch_time (InotifyKernelSource *iks)
{
ik_event_t *head;
@@ -119,42 +129,17 @@ static gboolean
ik_source_can_dispatch_now (InotifyKernelSource *iks,
gint64 now)
{
- gint64 ready_time;
+ gint64 dispatch_time;
- ready_time = ik_source_get_ready_time (iks);
+ dispatch_time = ik_source_get_dispatch_time (iks);
- return 0 <= ready_time && ready_time <= now;
+ return 0 <= dispatch_time && dispatch_time <= now;
}
-static void
-ik_source_try_to_pair_head (InotifyKernelSource *iks)
+static gboolean
+ik_source_is_bored (InotifyKernelSource *iks)
{
- ik_event_t *head;
- GList *node;
-
- node = g_queue_peek_head_link (&iks->queue);
-
- if (!node)
- return;
-
- head = node->data;
-
- /* we should only be here if... */
- g_assert (head->mask & IN_MOVED_FROM && !head->pair);
-
- while ((node = node->next))
- {
- ik_event_t *candidate = node->data;
-
- if (candidate->cookie == head->cookie && candidate->mask & IN_MOVED_TO)
- {
- g_queue_remove (&iks->queue, candidate);
- candidate->is_second_in_pair = TRUE;
- head->pair = candidate;
- candidate->pair = head;
- return;
- }
- }
+ return iks->boredom > BOREDOM_THRESHOLD;
}
static gboolean
@@ -164,15 +149,53 @@ ik_source_dispatch (GSource *source,
{
InotifyKernelSource *iks = (InotifyKernelSource *) source;
gboolean (*user_callback) (ik_event_t *event) = (void *) func;
- gint64 now = g_source_get_time (source);
+ gboolean interesting = FALSE;
+ gboolean is_ready;
+ gint64 now;
now = g_source_get_time (source);
- /* Only try to fill the queue if we don't already have work to do. */
- if (!ik_source_can_dispatch_now (iks, now) &&
- g_source_query_unix_fd (source, iks->fd_tag))
+ /* If we woke up after a timeout caused by boredom, check to see if we
+ * actually have anything to read. If not, go back to waiting for the
+ * file descriptor to become ready.
+ */
+ if (ik_source_is_bored (iks) && g_source_get_ready_time (source))
+ {
+ GPollFD pollfd;
+ gint n_ready;
+
+ pollfd.fd = iks->fd;
+ pollfd.events = G_IO_IN;
+
+ do
+ n_ready = g_poll (&pollfd, 1, 0);
+ while (n_ready == -1 && errno == EINTR);
+
+ if (n_ready == -1)
+ g_error ("Unexpected error on poll() of inotify: %s\n", g_strerror (errno));
+
+ if (!n_ready)
+ {
+ /* Timeout fired but there is nothing to read. Switch back to
+ * waiting for the fd to become ready, but do not reset
+ * boredom. We don't want to cancel our back-off and start
+ * all over again just because the delay got longer than the
+ * frequency of the change notifications.
+ */
+ g_source_modify_unix_fd (source, iks->fd_tag, G_IO_IN);
+ g_source_set_ready_time (source, 0);
+
+ return TRUE;
+ }
+
+ is_ready = TRUE;
+ }
+ else
+ is_ready = g_source_query_unix_fd (source, iks->fd_tag);
+
+ if (is_ready && !ik_source_can_dispatch_now (iks, now))
{
- gchar buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
+ gchar buffer[256 * 1024];
gssize result;
gssize offset;
@@ -187,30 +210,83 @@ ik_source_dispatch (GSource *source,
while (offset < result)
{
- struct inotify_event *event = (struct inotify_event *) (buffer + offset);
+ struct inotify_event *kevent = (struct inotify_event *) (buffer + offset);
+ ik_event_t *event;
- g_queue_push_tail (&iks->queue, ik_event_new (event, now));
+ event = ik_event_new (kevent, now);
offset += sizeof (struct inotify_event) + event->len;
+
+ if (event->mask & IN_MOVED_TO)
+ {
+ ik_event_t *pair;
+
+ pair = g_hash_table_lookup (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
+ if (pair != NULL)
+ {
+ g_assert (!pair->pair);
+
+ g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
+ event->is_second_in_pair = TRUE;
+ event->pair = pair;
+ pair->pair = event;
+ continue;
+ }
+ }
+
+ else if (event->mask & IN_MOVED_FROM)
+ {
+ gboolean new;
+
+ new = g_hash_table_insert (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), event);
+ if G_UNLIKELY (!new)
+ g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x",
event->cookie);
+ }
+
+ g_queue_push_tail (&iks->queue, event);
}
}
- if (!ik_source_can_dispatch_now (iks, now))
- ik_source_try_to_pair_head (iks);
-
- if (ik_source_can_dispatch_now (iks, now))
+ while (ik_source_can_dispatch_now (iks, now))
{
ik_event_t *event;
/* callback will free the event */
event = g_queue_pop_head (&iks->queue);
+ if (event->mask & IN_MOVED_TO && !event->pair)
+ g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
+
G_LOCK (inotify_lock);
- (* user_callback) (event);
+ interesting |= (* user_callback) (event);
G_UNLOCK (inotify_lock);
}
- g_source_set_ready_time (source, ik_source_get_ready_time (iks));
+ /* The queue gets blocked iff we have unmatched moves */
+ g_assert ((iks->queue.length > 0) == (g_hash_table_size (iks->unmatched_moves) > 0));
+
+ /* Unpaired moves are interesting since they will be reported to the
+ * user, one way or another. We also want to resolve them as soon as
+ * possible.
+ */
+ interesting |= iks->queue.length > 0;
+
+ if (!interesting)
+ {
+ iks->boredom = MAX(BOREDOM_MIN, MIN(iks->boredom * BOREDOM_FACTOR, BOREDOM_MAX));
+
+ if (ik_source_is_bored (iks))
+ {
+ g_source_set_ready_time (source, now + iks->boredom);
+ g_source_modify_unix_fd (source, iks->fd_tag, 0);
+ }
+ }
+ else
+ {
+ g_source_set_ready_time (source, ik_source_get_dispatch_time (iks));
+ g_source_modify_unix_fd (source, iks->fd_tag, G_IO_IN);
+ iks->boredom = 0;
+ }
return TRUE;
}
@@ -231,13 +307,21 @@ ik_source_new (gboolean (* callback) (ik_event_t *event))
g_source_set_name (source, "inotify kernel source");
+ iks->unmatched_moves = g_hash_table_new (NULL, NULL);
iks->fd = inotify_init1 (IN_CLOEXEC);
if (iks->fd < 0)
iks->fd = inotify_init ();
if (iks->fd >= 0)
- iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
+ {
+ GError *error = NULL;
+
+ g_unix_set_fd_nonblocking (iks->fd, TRUE, &error);
+ g_assert_no_error (error);
+
+ iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
+ }
g_source_set_callback (source, (GSourceFunc) callback, NULL, NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]