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@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
35struct _FileChange;
36typedef struct _FileChange FileChange;
37static 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
54G_LOCK_DEFINE_STATIC(cancelled);
55
56enum {
57  CHANGED,
58  LAST_SIGNAL
59};
60
61/* work around a limitation of the aliasing foo */
62#undef g_file_monitor
63
64G_DEFINE_ABSTRACT_TYPE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT);
65
66typedef 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
73struct _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
87enum {
88  PROP_0,
89  PROP_RATE_LIMIT,
90  PROP_CANCELLED
91};
92
93static void
94g_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
115static void
116g_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
148static guint signals[LAST_SIGNAL] = { 0 };
149
150static void
151rate_limiter_free (RateLimiter *limiter)
152{
153  g_object_unref (limiter->file);
154  g_slice_free (RateLimiter, limiter);
155}
156
157static void
158g_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
175static void
176g_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
199static void
200g_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
251static void
252g_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 **/
270gboolean
271g_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 **/
292gboolean
293g_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 **/
325void
326g_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
341struct _FileChange {
342  GFile             *child;
343  GFile             *other_file;
344  GFileMonitorEvent  event_type;
345};
346
347static void
348file_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
357static gboolean
358emit_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
381static void
382emit_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
418static guint32
419get_time_msecs (void)
420{
421  return g_thread_gettime() / (1000 * 1000);
422}
423
424static guint32
425time_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
434static RateLimiter *
435new_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
447static void
448rate_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
459static void
460rate_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
474typedef struct {
475  guint32 min_time;
476  guint32 time_now;
477  GFileMonitor *monitor;
478} ForEachData;
479
480static gboolean
481calc_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
521static gboolean
522foreach_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
540static gboolean
541rate_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
577static gboolean
578foreach_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
588static void
589update_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 **/
640void
641g_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