Home | History | Annotate | Download | only in gio
      1 /* GIO - GLib Input, Output and Streaming Library
      2  *
      3  * Copyright (C) 2006-2007 Red Hat, Inc.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Lesser General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Lesser General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General
     16  * Public License along with this library; if not, write to the
     17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     18  * Boston, MA 02111-1307, USA.
     19  *
     20  * Author: Alexander Larsson <alexl (at) redhat.com>
     21  */
     22 
     23 #include "config.h"
     24 #include <string.h>
     25 
     26 #include "gfilemonitor.h"
     27 #include "gio-marshal.h"
     28 #include "gioenumtypes.h"
     29 #include "gfile.h"
     30 #include "gvfs.h"
     31 #include "glibintl.h"
     32 
     33 #include "gioalias.h"
     34 
     35 struct _FileChange;
     36 typedef struct _FileChange FileChange;
     37 static void file_change_free (FileChange *change);
     38 
     39 /**
     40  * SECTION:gfilemonitor
     41  * @short_description: File Monitor
     42  * @include: gio/gio.h
     43  *
     44  * Monitors a file or directory for changes.
     45  *
     46  * To obtain a #GFileMonitor for a file or directory, use
     47  * g_file_monitor(), g_file_monitor_file(), or
     48  * g_file_monitor_directory().
     49  *
     50  * To get informed about changes to the file or directory you
     51  * are monitoring, connect to the #GFileMonitor::changed signal.
     52  **/
     53 
     54 G_LOCK_DEFINE_STATIC(cancelled);
     55 
     56 enum {
     57   CHANGED,
     58   LAST_SIGNAL
     59 };
     60 
     61 /* work around a limitation of the aliasing foo */
     62 #undef g_file_monitor
     63 
     64 G_DEFINE_ABSTRACT_TYPE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT);
     65 
     66 typedef struct {
     67   GFile *file;
     68   guint32 last_sent_change_time; /* 0 == not sent */
     69   guint32 send_delayed_change_at; /* 0 == never */
     70   guint32 send_virtual_changes_done_at; /* 0 == never */
     71 } RateLimiter;
     72 
     73 struct _GFileMonitorPrivate {
     74   gboolean cancelled;
     75   int rate_limit_msec;
     76 
     77   /* Rate limiting change events */
     78   GHashTable *rate_limiter;
     79 
     80   guint pending_file_change_id;
     81   GSList *pending_file_changes; /* FileChange */
     82 
     83   GSource *timeout;
     84   guint32 timeout_fires_at;
     85 };
     86 
     87 enum {
     88   PROP_0,
     89   PROP_RATE_LIMIT,
     90   PROP_CANCELLED
     91 };
     92 
     93 static void
     94 g_file_monitor_set_property (GObject      *object,
     95                              guint         prop_id,
     96                              const GValue *value,
     97                              GParamSpec   *pspec)
     98 {
     99   GFileMonitor *monitor;
    100 
    101   monitor = G_FILE_MONITOR (object);
    102 
    103   switch (prop_id)
    104     {
    105     case PROP_RATE_LIMIT:
    106       g_file_monitor_set_rate_limit (monitor, g_value_get_int (value));
    107       break;
    108 
    109     default:
    110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    111       break;
    112     }
    113 }
    114 
    115 static void
    116 g_file_monitor_get_property (GObject    *object,
    117                              guint       prop_id,
    118                              GValue     *value,
    119                              GParamSpec *pspec)
    120 {
    121   GFileMonitor *monitor;
    122   GFileMonitorPrivate *priv;
    123 
    124   monitor = G_FILE_MONITOR (object);
    125   priv = monitor->priv;
    126 
    127   switch (prop_id)
    128     {
    129     case PROP_RATE_LIMIT:
    130       g_value_set_int (value, priv->rate_limit_msec);
    131       break;
    132 
    133     case PROP_CANCELLED:
    134       G_LOCK (cancelled);
    135       g_value_set_boolean (value, priv->cancelled);
    136       G_UNLOCK (cancelled);
    137       break;
    138 
    139     default:
    140       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    141       break;
    142     }
    143 }
    144 
    145 #define DEFAULT_RATE_LIMIT_MSECS 800
    146 #define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
    147 
    148 static guint signals[LAST_SIGNAL] = { 0 };
    149 
    150 static void
    151 rate_limiter_free (RateLimiter *limiter)
    152 {
    153   g_object_unref (limiter->file);
    154   g_slice_free (RateLimiter, limiter);
    155 }
    156 
    157 static void
    158 g_file_monitor_finalize (GObject *object)
    159 {
    160   GFileMonitor *monitor;
    161 
    162   monitor = G_FILE_MONITOR (object);
    163 
    164   if (monitor->priv->timeout)
    165     {
    166       g_source_destroy (monitor->priv->timeout);
    167       g_source_unref (monitor->priv->timeout);
    168     }
    169 
    170   g_hash_table_destroy (monitor->priv->rate_limiter);
    171 
    172   G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object);
    173 }
    174 
    175 static void
    176 g_file_monitor_dispose (GObject *object)
    177 {
    178   GFileMonitor *monitor;
    179   GFileMonitorPrivate *priv;
    180 
    181   monitor = G_FILE_MONITOR (object);
    182   priv = monitor->priv;
    183 
    184   if (priv->pending_file_change_id)
    185     {
    186       g_source_remove (priv->pending_file_change_id);
    187       priv->pending_file_change_id = 0;
    188     }
    189   g_slist_foreach (priv->pending_file_changes, (GFunc) file_change_free, NULL);
    190   g_slist_free (priv->pending_file_changes);
    191   priv->pending_file_changes = NULL;
    192 
    193   /* Make sure we cancel on last unref */
    194   g_file_monitor_cancel (monitor);
    195 
    196   G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose (object);
    197 }
    198 
    199 static void
    200 g_file_monitor_class_init (GFileMonitorClass *klass)
    201 {
    202   GObjectClass *object_class;
    203 
    204   g_type_class_add_private (klass, sizeof (GFileMonitorPrivate));
    205 
    206   object_class = G_OBJECT_CLASS (klass);
    207   object_class->finalize = g_file_monitor_finalize;
    208   object_class->dispose = g_file_monitor_dispose;
    209   object_class->get_property = g_file_monitor_get_property;
    210   object_class->set_property = g_file_monitor_set_property;
    211 
    212   /**
    213    * GFileMonitor::changed:
    214    * @monitor: a #GFileMonitor.
    215    * @file: a #GFile.
    216    * @other_file: a #GFile.
    217    * @event_type: a #GFileMonitorEvent.
    218    *
    219    * Emitted when a file has been changed.
    220    **/
    221   signals[CHANGED] =
    222     g_signal_new (I_("changed"),
    223 		  G_TYPE_FILE_MONITOR,
    224 		  G_SIGNAL_RUN_LAST,
    225 		  G_STRUCT_OFFSET (GFileMonitorClass, changed),
    226 		  NULL, NULL,
    227 		  _gio_marshal_VOID__OBJECT_OBJECT_ENUM,
    228 		  G_TYPE_NONE, 3,
    229 		  G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT);
    230 
    231   g_object_class_install_property (object_class,
    232                                    PROP_RATE_LIMIT,
    233                                    g_param_spec_int ("rate-limit",
    234                                                      P_("Rate limit"),
    235                                                      P_("The limit of the monitor to watch for changes, in milliseconds"),
    236                                                      0, G_MAXINT,
    237                                                      DEFAULT_RATE_LIMIT_MSECS,
    238                                                      G_PARAM_READWRITE|
    239                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
    240 
    241   g_object_class_install_property (object_class,
    242                                    PROP_CANCELLED,
    243                                    g_param_spec_boolean ("cancelled",
    244                                                          P_("Cancelled"),
    245                                                          P_("Whether the monitor has been cancelled"),
    246                                                          FALSE,
    247                                                          G_PARAM_READABLE|
    248                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
    249 }
    250 
    251 static void
    252 g_file_monitor_init (GFileMonitor *monitor)
    253 {
    254   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
    255 					       G_TYPE_FILE_MONITOR,
    256 					       GFileMonitorPrivate);
    257   monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
    258   monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
    259 						       NULL, (GDestroyNotify) rate_limiter_free);
    260 }
    261 
    262 /**
    263  * g_file_monitor_is_cancelled:
    264  * @monitor: a #GFileMonitor
    265  *
    266  * Returns whether the monitor is canceled.
    267  *
    268  * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
    269  **/
    270 gboolean
    271 g_file_monitor_is_cancelled (GFileMonitor *monitor)
    272 {
    273   gboolean res;
    274 
    275   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
    276 
    277   G_LOCK (cancelled);
    278   res = monitor->priv->cancelled;
    279   G_UNLOCK (cancelled);
    280 
    281   return res;
    282 }
    283 
    284 /**
    285  * g_file_monitor_cancel:
    286  * @monitor: a #GFileMonitor.
    287  *
    288  * Cancels a file monitor.
    289  *
    290  * Returns: %TRUE if monitor was cancelled.
    291  **/
    292 gboolean
    293 g_file_monitor_cancel (GFileMonitor* monitor)
    294 {
    295   GFileMonitorClass *klass;
    296 
    297   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
    298 
    299   G_LOCK (cancelled);
    300   if (monitor->priv->cancelled)
    301     {
    302       G_UNLOCK (cancelled);
    303       return TRUE;
    304     }
    305 
    306   monitor->priv->cancelled = TRUE;
    307   G_UNLOCK (cancelled);
    308 
    309   g_object_notify (G_OBJECT (monitor), "cancelled");
    310 
    311   klass = G_FILE_MONITOR_GET_CLASS (monitor);
    312   return (* klass->cancel) (monitor);
    313 }
    314 
    315 /**
    316  * g_file_monitor_set_rate_limit:
    317  * @monitor: a #GFileMonitor.
    318  * @limit_msecs: a integer with the limit in milliseconds to
    319  * poll for changes.
    320  *
    321  * Sets the rate limit to which the @monitor will report
    322  * consecutive change events to the same file.
    323  *
    324  **/
    325 void
    326 g_file_monitor_set_rate_limit (GFileMonitor *monitor,
    327 			       int           limit_msecs)
    328 {
    329   GFileMonitorPrivate *priv;
    330 
    331   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
    332 
    333   priv = monitor->priv;
    334   if (priv->rate_limit_msec != limit_msecs)
    335     {
    336       monitor->priv->rate_limit_msec = limit_msecs;
    337       g_object_notify (G_OBJECT (monitor), "rate-limit");
    338     }
    339 }
    340 
    341 struct _FileChange {
    342   GFile             *child;
    343   GFile             *other_file;
    344   GFileMonitorEvent  event_type;
    345 };
    346 
    347 static void
    348 file_change_free (FileChange *change)
    349 {
    350   g_object_unref (change->child);
    351   if (change->other_file)
    352     g_object_unref (change->other_file);
    353 
    354   g_slice_free (FileChange, change);
    355 }
    356 
    357 static gboolean
    358 emit_cb (gpointer data)
    359 {
    360   GFileMonitor *monitor = G_FILE_MONITOR (data);
    361   GSList *pending, *iter;
    362 
    363   pending = g_slist_reverse (monitor->priv->pending_file_changes);
    364   monitor->priv->pending_file_changes = NULL;
    365   monitor->priv->pending_file_change_id = 0;
    366 
    367   g_object_ref (monitor);
    368   for (iter = pending; iter; iter = iter->next)
    369     {
    370        FileChange *change = iter->data;
    371        g_signal_emit (monitor, signals[CHANGED], 0,
    372 	  	      change->child, change->other_file, change->event_type);
    373        file_change_free (change);
    374     }
    375   g_slist_free (pending);
    376   g_object_unref (monitor);
    377 
    378   return FALSE;
    379 }
    380 
    381 static void
    382 emit_in_idle (GFileMonitor      *monitor,
    383 	      GFile             *child,
    384 	      GFile             *other_file,
    385 	      GFileMonitorEvent  event_type)
    386 {
    387   GSource *source;
    388   FileChange *change;
    389   GFileMonitorPrivate *priv;
    390 
    391   priv = monitor->priv;
    392 
    393   change = g_slice_new (FileChange);
    394 
    395   change->child = g_object_ref (child);
    396   if (other_file)
    397     change->other_file = g_object_ref (other_file);
    398   else
    399     change->other_file = NULL;
    400   change->event_type = event_type;
    401 
    402   if (!priv->pending_file_change_id)
    403     {
    404       source = g_idle_source_new ();
    405       g_source_set_priority (source, 0);
    406 
    407       /* We don't ref here - instead dispose will free any
    408        * pending idles.
    409        */
    410       g_source_set_callback (source, emit_cb, monitor, NULL);
    411       priv->pending_file_change_id = g_source_attach (source, NULL);
    412       g_source_unref (source);
    413     }
    414   /* We reverse this in the processor */
    415   priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change);
    416 }
    417 
    418 static guint32
    419 get_time_msecs (void)
    420 {
    421   return g_thread_gettime() / (1000 * 1000);
    422 }
    423 
    424 static guint32
    425 time_difference (guint32 from, guint32 to)
    426 {
    427   if (from > to)
    428     return 0;
    429   return to - from;
    430 }
    431 
    432 /* Change event rate limiting support: */
    433 
    434 static RateLimiter *
    435 new_limiter (GFileMonitor *monitor,
    436 	     GFile             *file)
    437 {
    438   RateLimiter *limiter;
    439 
    440   limiter = g_slice_new0 (RateLimiter);
    441   limiter->file = g_object_ref (file);
    442   g_hash_table_insert (monitor->priv->rate_limiter, file, limiter);
    443 
    444   return limiter;
    445 }
    446 
    447 static void
    448 rate_limiter_send_virtual_changes_done_now (GFileMonitor *monitor,
    449                                             RateLimiter  *limiter)
    450 {
    451   if (limiter->send_virtual_changes_done_at != 0)
    452     {
    453       emit_in_idle (monitor, limiter->file, NULL,
    454 		    G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
    455       limiter->send_virtual_changes_done_at = 0;
    456     }
    457 }
    458 
    459 static void
    460 rate_limiter_send_delayed_change_now (GFileMonitor *monitor,
    461                                       RateLimiter *limiter,
    462                                       guint32 time_now)
    463 {
    464   if (limiter->send_delayed_change_at != 0)
    465     {
    466       emit_in_idle (monitor,
    467 		    limiter->file, NULL,
    468 		    G_FILE_MONITOR_EVENT_CHANGED);
    469       limiter->send_delayed_change_at = 0;
    470       limiter->last_sent_change_time = time_now;
    471     }
    472 }
    473 
    474 typedef struct {
    475   guint32 min_time;
    476   guint32 time_now;
    477   GFileMonitor *monitor;
    478 } ForEachData;
    479 
    480 static gboolean
    481 calc_min_time (GFileMonitor *monitor,
    482                RateLimiter *limiter,
    483                guint32 time_now,
    484                guint32 *min_time)
    485 {
    486   gboolean delete_me;
    487   guint32 expire_at;
    488 
    489   delete_me = TRUE;
    490 
    491   if (limiter->last_sent_change_time != 0)
    492     {
    493       /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventualy */
    494       expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec;
    495 
    496       if (time_difference (time_now, expire_at) > 0)
    497 	{
    498 	  delete_me = FALSE;
    499 	  *min_time = MIN (*min_time,
    500 			   time_difference (time_now, expire_at));
    501 	}
    502     }
    503 
    504   if (limiter->send_delayed_change_at != 0)
    505     {
    506       delete_me = FALSE;
    507       *min_time = MIN (*min_time,
    508 		       time_difference (time_now, limiter->send_delayed_change_at));
    509     }
    510 
    511   if (limiter->send_virtual_changes_done_at != 0)
    512     {
    513       delete_me = FALSE;
    514       *min_time = MIN (*min_time,
    515 		       time_difference (time_now, limiter->send_virtual_changes_done_at));
    516     }
    517 
    518   return delete_me;
    519 }
    520 
    521 static gboolean
    522 foreach_rate_limiter_fire (gpointer key,
    523 			   gpointer value,
    524 			   gpointer user_data)
    525 {
    526   RateLimiter *limiter = value;
    527   ForEachData *data = user_data;
    528 
    529   if (limiter->send_delayed_change_at != 0 &&
    530       time_difference (data->time_now, limiter->send_delayed_change_at) == 0)
    531     rate_limiter_send_delayed_change_now (data->monitor, limiter, data->time_now);
    532 
    533   if (limiter->send_virtual_changes_done_at != 0 &&
    534       time_difference (data->time_now, limiter->send_virtual_changes_done_at) == 0)
    535     rate_limiter_send_virtual_changes_done_now (data->monitor, limiter);
    536 
    537   return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
    538 }
    539 
    540 static gboolean
    541 rate_limiter_timeout (gpointer timeout_data)
    542 {
    543   GFileMonitor *monitor = timeout_data;
    544   ForEachData data;
    545   GSource *source;
    546 
    547   data.min_time = G_MAXUINT32;
    548   data.monitor = monitor;
    549   data.time_now = get_time_msecs ();
    550   g_hash_table_foreach_remove (monitor->priv->rate_limiter,
    551 			       foreach_rate_limiter_fire,
    552 			       &data);
    553 
    554   /* Remove old timeout */
    555   if (monitor->priv->timeout)
    556     {
    557       g_source_destroy (monitor->priv->timeout);
    558       g_source_unref (monitor->priv->timeout);
    559       monitor->priv->timeout = NULL;
    560       monitor->priv->timeout_fires_at = 0;
    561     }
    562 
    563   /* Set up new timeout */
    564   if (data.min_time != G_MAXUINT32)
    565     {
    566       source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
    567       g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
    568       g_source_attach (source, NULL);
    569 
    570       monitor->priv->timeout = source;
    571       monitor->priv->timeout_fires_at = data.time_now + data.min_time;
    572     }
    573 
    574   return FALSE;
    575 }
    576 
    577 static gboolean
    578 foreach_rate_limiter_update (gpointer key,
    579 			     gpointer value,
    580 			     gpointer user_data)
    581 {
    582   RateLimiter *limiter = value;
    583   ForEachData *data = user_data;
    584 
    585   return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
    586 }
    587 
    588 static void
    589 update_rate_limiter_timeout (GFileMonitor *monitor,
    590                              guint new_time)
    591 {
    592   ForEachData data;
    593   GSource *source;
    594 
    595   if (monitor->priv->timeout_fires_at != 0 && new_time != 0 &&
    596       time_difference (new_time, monitor->priv->timeout_fires_at) == 0)
    597     return; /* Nothing to do, we already fire earlier than that */
    598 
    599   data.min_time = G_MAXUINT32;
    600   data.monitor = monitor;
    601   data.time_now = get_time_msecs ();
    602   g_hash_table_foreach_remove (monitor->priv->rate_limiter,
    603 			       foreach_rate_limiter_update,
    604 			       &data);
    605 
    606   /* Remove old timeout */
    607   if (monitor->priv->timeout)
    608     {
    609       g_source_destroy (monitor->priv->timeout);
    610       g_source_unref (monitor->priv->timeout);
    611       monitor->priv->timeout_fires_at = 0;
    612       monitor->priv->timeout = NULL;
    613     }
    614 
    615   /* Set up new timeout */
    616   if (data.min_time != G_MAXUINT32)
    617     {
    618       source = g_timeout_source_new (data.min_time + 1);  /* + 1 to make sure we've really passed the time */
    619       g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
    620       g_source_attach (source, NULL);
    621 
    622       monitor->priv->timeout = source;
    623       monitor->priv->timeout_fires_at = data.time_now + data.min_time;
    624     }
    625 }
    626 
    627 /**
    628  * g_file_monitor_emit_event:
    629  * @monitor: a #GFileMonitor.
    630  * @child: a #GFile.
    631  * @other_file: a #GFile.
    632  * @event_type: a set of #GFileMonitorEvent flags.
    633  *
    634  * Emits the #GFileMonitor::changed signal if a change
    635  * has taken place. Should be called from file monitor
    636  * implementations only.
    637  *
    638  * The signal will be emitted from an idle handler.
    639  **/
    640 void
    641 g_file_monitor_emit_event (GFileMonitor      *monitor,
    642 			   GFile             *child,
    643 			   GFile             *other_file,
    644 			   GFileMonitorEvent  event_type)
    645 {
    646   guint32 time_now, since_last;
    647   gboolean emit_now;
    648   RateLimiter *limiter;
    649 
    650   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
    651   g_return_if_fail (G_IS_FILE (child));
    652 
    653   limiter = g_hash_table_lookup (monitor->priv->rate_limiter, child);
    654 
    655   if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
    656     {
    657       if (limiter)
    658 	{
    659 	  rate_limiter_send_delayed_change_now (monitor, limiter, get_time_msecs ());
    660 	  if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
    661 	    limiter->send_virtual_changes_done_at = 0;
    662 	  else
    663 	    rate_limiter_send_virtual_changes_done_now (monitor, limiter);
    664 	  update_rate_limiter_timeout (monitor, 0);
    665 	}
    666       emit_in_idle (monitor, child, other_file, event_type);
    667     }
    668   else
    669     {
    670       /* Changed event, rate limit */
    671       time_now = get_time_msecs ();
    672       emit_now = TRUE;
    673 
    674       if (limiter)
    675 	{
    676 	  since_last = time_difference (limiter->last_sent_change_time, time_now);
    677 	  if (since_last < monitor->priv->rate_limit_msec)
    678 	    {
    679 	      /* We ignore this change, but arm a timer so that we can fire it later if we
    680 		 don't get any other events (that kill this timeout) */
    681 	      emit_now = FALSE;
    682 	      if (limiter->send_delayed_change_at == 0)
    683 		{
    684 		  limiter->send_delayed_change_at = time_now + monitor->priv->rate_limit_msec;
    685 		  update_rate_limiter_timeout (monitor, limiter->send_delayed_change_at);
    686 		}
    687 	    }
    688 	}
    689 
    690       if (limiter == NULL)
    691 	limiter = new_limiter (monitor, child);
    692 
    693       if (emit_now)
    694 	{
    695 	  emit_in_idle (monitor, child, other_file, event_type);
    696 
    697 	  limiter->last_sent_change_time = time_now;
    698 	  limiter->send_delayed_change_at = 0;
    699 	  /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventualy */
    700 	  update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec);
    701 	}
    702 
    703       /* Schedule a virtual change done. This is removed if we get a real one, and
    704 	 postponed if we get more change events. */
    705 
    706       limiter->send_virtual_changes_done_at = time_now + DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS * 1000;
    707       update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at);
    708     }
    709 }
    710 
    711 #define __G_FILE_MONITOR_C__
    712 #include "gioaliasdef.c"
    713