Use low-level GSource methods in a few more places
[platform/upstream/glib.git] / gio / gfilemonitor.c
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
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   GSource *pending_file_change_source;
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_source)
185     {
186       g_source_destroy (priv->pending_file_change_source);
187       g_source_unref (priv->pending_file_change_source);
188       priv->pending_file_change_source = NULL;
189     }
190   g_slist_foreach (priv->pending_file_changes, (GFunc) file_change_free, NULL);
191   g_slist_free (priv->pending_file_changes);
192   priv->pending_file_changes = NULL;
193
194   /* Make sure we cancel on last unref */
195   g_file_monitor_cancel (monitor);
196
197   G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose (object);
198 }
199
200 static void
201 g_file_monitor_class_init (GFileMonitorClass *klass)
202 {
203   GObjectClass *object_class;
204   
205   g_type_class_add_private (klass, sizeof (GFileMonitorPrivate));
206   
207   object_class = G_OBJECT_CLASS (klass);
208   object_class->finalize = g_file_monitor_finalize;
209   object_class->dispose = g_file_monitor_dispose;
210   object_class->get_property = g_file_monitor_get_property;
211   object_class->set_property = g_file_monitor_set_property;
212
213   /**
214    * GFileMonitor::changed:
215    * @monitor: a #GFileMonitor.
216    * @file: a #GFile.
217    * @other_file: a #GFile.
218    * @event_type: a #GFileMonitorEvent.
219    * 
220    * Emitted when a file has been changed. 
221    **/
222   signals[CHANGED] =
223     g_signal_new (I_("changed"),
224                   G_TYPE_FILE_MONITOR,
225                   G_SIGNAL_RUN_LAST,
226                   G_STRUCT_OFFSET (GFileMonitorClass, changed),
227                   NULL, NULL,
228                   _gio_marshal_VOID__OBJECT_OBJECT_ENUM,
229                   G_TYPE_NONE, 3,
230                   G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT);
231
232   g_object_class_install_property (object_class,
233                                    PROP_RATE_LIMIT,
234                                    g_param_spec_int ("rate-limit",
235                                                      P_("Rate limit"),
236                                                      P_("The limit of the monitor to watch for changes, in milliseconds"),
237                                                      0, G_MAXINT,
238                                                      DEFAULT_RATE_LIMIT_MSECS,
239                                                      G_PARAM_READWRITE|
240                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
241
242   g_object_class_install_property (object_class,
243                                    PROP_CANCELLED,
244                                    g_param_spec_boolean ("cancelled",
245                                                          P_("Cancelled"),
246                                                          P_("Whether the monitor has been cancelled"),
247                                                          FALSE,
248                                                          G_PARAM_READABLE|
249                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
250 }
251
252 static void
253 g_file_monitor_init (GFileMonitor *monitor)
254 {
255   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
256                                                G_TYPE_FILE_MONITOR,
257                                                GFileMonitorPrivate);
258   monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
259   monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
260                                                        NULL, (GDestroyNotify) rate_limiter_free);
261 }
262
263 /**
264  * g_file_monitor_is_cancelled:
265  * @monitor: a #GFileMonitor
266  * 
267  * Returns whether the monitor is canceled.
268  *
269  * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
270  **/
271 gboolean
272 g_file_monitor_is_cancelled (GFileMonitor *monitor)
273 {
274   gboolean res;
275
276   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
277
278   G_LOCK (cancelled);
279   res = monitor->priv->cancelled;
280   G_UNLOCK (cancelled);
281   
282   return res;
283 }
284
285 /**
286  * g_file_monitor_cancel:
287  * @monitor: a #GFileMonitor.
288  * 
289  * Cancels a file monitor.
290  * 
291  * Returns: %TRUE if monitor was cancelled.
292  **/
293 gboolean
294 g_file_monitor_cancel (GFileMonitor* monitor)
295 {
296   GFileMonitorClass *klass;
297   
298   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
299   
300   G_LOCK (cancelled);
301   if (monitor->priv->cancelled)
302     {
303       G_UNLOCK (cancelled);
304       return TRUE;
305     }
306   
307   monitor->priv->cancelled = TRUE;
308   G_UNLOCK (cancelled);
309   
310   g_object_notify (G_OBJECT (monitor), "cancelled");
311
312   klass = G_FILE_MONITOR_GET_CLASS (monitor);
313   return (* klass->cancel) (monitor);
314 }
315
316 /**
317  * g_file_monitor_set_rate_limit:
318  * @monitor: a #GFileMonitor.
319  * @limit_msecs: a integer with the limit in milliseconds to 
320  * poll for changes.
321  *
322  * Sets the rate limit to which the @monitor will report
323  * consecutive change events to the same file. 
324  * 
325  **/
326 void
327 g_file_monitor_set_rate_limit (GFileMonitor *monitor,
328                                int           limit_msecs)
329 {
330   GFileMonitorPrivate *priv;
331   
332   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
333   
334   priv = monitor->priv;
335   if (priv->rate_limit_msec != limit_msecs)
336     {
337       monitor->priv->rate_limit_msec = limit_msecs;
338       g_object_notify (G_OBJECT (monitor), "rate-limit");
339     }
340 }
341
342 struct _FileChange {
343   GFile             *child;
344   GFile             *other_file;
345   GFileMonitorEvent  event_type;
346 };
347
348 static void
349 file_change_free (FileChange *change)
350 {
351   g_object_unref (change->child);
352   if (change->other_file)
353     g_object_unref (change->other_file);
354   
355   g_slice_free (FileChange, change);
356 }
357
358 static gboolean
359 emit_cb (gpointer data)
360 {
361   GFileMonitor *monitor = G_FILE_MONITOR (data);
362   GSList *pending, *iter;
363   
364   pending = g_slist_reverse (monitor->priv->pending_file_changes);
365   monitor->priv->pending_file_changes = NULL;
366   if (monitor->priv->pending_file_change_source)
367     {
368       g_source_unref (monitor->priv->pending_file_change_source);
369       monitor->priv->pending_file_change_source = NULL;
370     }
371
372   g_object_ref (monitor);
373   for (iter = pending; iter; iter = iter->next)
374     {
375        FileChange *change = iter->data;
376        g_signal_emit (monitor, signals[CHANGED], 0,
377                       change->child, change->other_file, change->event_type);
378        file_change_free (change);
379     }
380   g_slist_free (pending);
381   g_object_unref (monitor);
382
383   return FALSE;
384 }
385
386 static void
387 emit_in_idle (GFileMonitor      *monitor,
388               GFile             *child,
389               GFile             *other_file,
390               GFileMonitorEvent  event_type)
391 {
392   GSource *source;
393   FileChange *change;
394   GFileMonitorPrivate *priv;
395
396   priv = monitor->priv;
397
398   change = g_slice_new (FileChange);
399
400   change->child = g_object_ref (child);
401   if (other_file)
402     change->other_file = g_object_ref (other_file);
403   else
404     change->other_file = NULL;
405   change->event_type = event_type;
406
407   if (!priv->pending_file_change_source)
408     {
409       source = g_idle_source_new ();
410       priv->pending_file_change_source = source;
411       g_source_set_priority (source, 0);
412
413       /* We don't ref monitor here - instead dispose will free any
414        * pending idles.
415        */
416       g_source_set_callback (source, emit_cb, monitor, NULL);
417       g_source_attach (source, NULL);
418     }
419   /* We reverse this in the processor */
420   priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change);
421 }
422
423 static guint32
424 get_time_msecs (void)
425 {
426   return g_thread_gettime() / (1000 * 1000);
427 }
428
429 static guint32
430 time_difference (guint32 from, guint32 to)
431 {
432   if (from > to)
433     return 0;
434   return to - from;
435 }
436
437 /* Change event rate limiting support: */
438
439 static RateLimiter *
440 new_limiter (GFileMonitor *monitor,
441              GFile             *file)
442 {
443   RateLimiter *limiter;
444
445   limiter = g_slice_new0 (RateLimiter);
446   limiter->file = g_object_ref (file);
447   g_hash_table_insert (monitor->priv->rate_limiter, file, limiter);
448   
449   return limiter;
450 }
451
452 static void
453 rate_limiter_send_virtual_changes_done_now (GFileMonitor *monitor, 
454                                             RateLimiter  *limiter)
455 {
456   if (limiter->send_virtual_changes_done_at != 0)
457     {
458       emit_in_idle (monitor, limiter->file, NULL,
459                     G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
460       limiter->send_virtual_changes_done_at = 0;
461     }
462 }
463
464 static void
465 rate_limiter_send_delayed_change_now (GFileMonitor *monitor, 
466                                       RateLimiter *limiter, 
467                                       guint32 time_now)
468 {
469   if (limiter->send_delayed_change_at != 0)
470     {
471       emit_in_idle (monitor, 
472                     limiter->file, NULL,
473                     G_FILE_MONITOR_EVENT_CHANGED);
474       limiter->send_delayed_change_at = 0;
475       limiter->last_sent_change_time = time_now;
476     }
477 }
478
479 typedef struct {
480   guint32 min_time;
481   guint32 time_now;
482   GFileMonitor *monitor;
483 } ForEachData;
484
485 static gboolean
486 calc_min_time (GFileMonitor *monitor, 
487                RateLimiter *limiter, 
488                guint32 time_now, 
489                guint32 *min_time)
490 {
491   gboolean delete_me;
492   guint32 expire_at;
493
494   delete_me = TRUE;
495
496   if (limiter->last_sent_change_time != 0)
497     {
498       /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventualy */
499       expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec;
500
501       if (time_difference (time_now, expire_at) > 0)
502         {
503           delete_me = FALSE;
504           *min_time = MIN (*min_time,
505                            time_difference (time_now, expire_at));
506         }
507     }
508
509   if (limiter->send_delayed_change_at != 0)
510     {
511       delete_me = FALSE;
512       *min_time = MIN (*min_time,
513                        time_difference (time_now, limiter->send_delayed_change_at));
514     }
515
516   if (limiter->send_virtual_changes_done_at != 0)
517     {
518       delete_me = FALSE;
519       *min_time = MIN (*min_time,
520                        time_difference (time_now, limiter->send_virtual_changes_done_at));
521     }
522
523   return delete_me;
524 }
525
526 static gboolean
527 foreach_rate_limiter_fire (gpointer key,
528                            gpointer value,
529                            gpointer user_data)
530 {
531   RateLimiter *limiter = value;
532   ForEachData *data = user_data;
533
534   if (limiter->send_delayed_change_at != 0 &&
535       time_difference (data->time_now, limiter->send_delayed_change_at) == 0)
536     rate_limiter_send_delayed_change_now (data->monitor, limiter, data->time_now);
537   
538   if (limiter->send_virtual_changes_done_at != 0 &&
539       time_difference (data->time_now, limiter->send_virtual_changes_done_at) == 0)
540     rate_limiter_send_virtual_changes_done_now (data->monitor, limiter);
541   
542   return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
543 }
544
545 static gboolean 
546 rate_limiter_timeout (gpointer timeout_data)
547 {
548   GFileMonitor *monitor = timeout_data;
549   ForEachData data;
550   GSource *source;
551   
552   data.min_time = G_MAXUINT32;
553   data.monitor = monitor;
554   data.time_now = get_time_msecs ();
555   g_hash_table_foreach_remove (monitor->priv->rate_limiter,
556                                foreach_rate_limiter_fire,
557                                &data);
558   
559   /* Remove old timeout */
560   if (monitor->priv->timeout)
561     {
562       g_source_destroy (monitor->priv->timeout);
563       g_source_unref (monitor->priv->timeout);
564       monitor->priv->timeout = NULL;
565       monitor->priv->timeout_fires_at = 0;
566     }
567   
568   /* Set up new timeout */
569   if (data.min_time != G_MAXUINT32)
570     {
571       source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
572       g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
573       g_source_attach (source, NULL);
574       
575       monitor->priv->timeout = source;
576       monitor->priv->timeout_fires_at = data.time_now + data.min_time; 
577     }
578   
579   return FALSE;
580 }
581
582 static gboolean
583 foreach_rate_limiter_update (gpointer key,
584                              gpointer value,
585                              gpointer user_data)
586 {
587   RateLimiter *limiter = value;
588   ForEachData *data = user_data;
589
590   return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
591 }
592
593 static void
594 update_rate_limiter_timeout (GFileMonitor *monitor, 
595                              guint new_time)
596 {
597   ForEachData data;
598   GSource *source;
599   
600   if (monitor->priv->timeout_fires_at != 0 && new_time != 0 &&
601       time_difference (new_time, monitor->priv->timeout_fires_at) == 0)
602     return; /* Nothing to do, we already fire earlier than that */
603
604   data.min_time = G_MAXUINT32;
605   data.monitor = monitor;
606   data.time_now = get_time_msecs ();
607   g_hash_table_foreach_remove (monitor->priv->rate_limiter,
608                                foreach_rate_limiter_update,
609                                &data);
610
611   /* Remove old timeout */
612   if (monitor->priv->timeout)
613     {
614       g_source_destroy (monitor->priv->timeout);
615       g_source_unref (monitor->priv->timeout);
616       monitor->priv->timeout_fires_at = 0;
617       monitor->priv->timeout = NULL;
618     }
619
620   /* Set up new timeout */
621   if (data.min_time != G_MAXUINT32)
622     {
623       source = g_timeout_source_new (data.min_time + 1);  /* + 1 to make sure we've really passed the time */
624       g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
625       g_source_attach (source, NULL);
626       
627       monitor->priv->timeout = source;
628       monitor->priv->timeout_fires_at = data.time_now + data.min_time; 
629     }
630 }
631
632 /**
633  * g_file_monitor_emit_event:
634  * @monitor: a #GFileMonitor.
635  * @child: a #GFile.
636  * @other_file: a #GFile.
637  * @event_type: a set of #GFileMonitorEvent flags.
638  * 
639  * Emits the #GFileMonitor::changed signal if a change
640  * has taken place. Should be called from file monitor 
641  * implementations only.
642  *
643  * The signal will be emitted from an idle handler.
644  **/
645 void
646 g_file_monitor_emit_event (GFileMonitor      *monitor,
647                            GFile             *child,
648                            GFile             *other_file,
649                            GFileMonitorEvent  event_type)
650 {
651   guint32 time_now, since_last;
652   gboolean emit_now;
653   RateLimiter *limiter;
654
655   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
656   g_return_if_fail (G_IS_FILE (child));
657
658   limiter = g_hash_table_lookup (monitor->priv->rate_limiter, child);
659
660   if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
661     {
662       if (limiter)
663         {
664           rate_limiter_send_delayed_change_now (monitor, limiter, get_time_msecs ());
665           if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
666             limiter->send_virtual_changes_done_at = 0;
667           else
668             rate_limiter_send_virtual_changes_done_now (monitor, limiter);
669           update_rate_limiter_timeout (monitor, 0);
670         }
671       emit_in_idle (monitor, child, other_file, event_type);
672     }
673   else
674     {
675       /* Changed event, rate limit */
676       time_now = get_time_msecs ();
677       emit_now = TRUE;
678       
679       if (limiter)
680         {
681           since_last = time_difference (limiter->last_sent_change_time, time_now);
682           if (since_last < monitor->priv->rate_limit_msec)
683             {
684               /* We ignore this change, but arm a timer so that we can fire it later if we
685                  don't get any other events (that kill this timeout) */
686               emit_now = FALSE;
687               if (limiter->send_delayed_change_at == 0)
688                 {
689                   limiter->send_delayed_change_at = time_now + monitor->priv->rate_limit_msec;
690                   update_rate_limiter_timeout (monitor, limiter->send_delayed_change_at);
691                 }
692             }
693         }
694       
695       if (limiter == NULL)
696         limiter = new_limiter (monitor, child);
697       
698       if (emit_now)
699         {
700           emit_in_idle (monitor, child, other_file, event_type);
701           
702           limiter->last_sent_change_time = time_now;
703           limiter->send_delayed_change_at = 0;
704           /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventualy */
705           update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec);
706         }
707       
708       /* Schedule a virtual change done. This is removed if we get a real one, and
709          postponed if we get more change events. */
710       
711       limiter->send_virtual_changes_done_at = time_now + DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS * 1000;
712       update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at);
713     }
714 }
715
716 #define __G_FILE_MONITOR_C__
717 #include "gioaliasdef.c"