Build test subdir after . Remove gdirectorymonitor.[ch]
[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 "gvfs.h"
29 #include "glibintl.h"
30
31 #include "gioalias.h"
32
33 /**
34  * SECTION:gfilemonitor
35  * @short_description: File Monitor
36  * @include: gio.h
37  *
38  * Monitors a file or directory for changes.
39  * 
40  **/
41
42 enum {
43   CHANGED,
44   LAST_SIGNAL
45 };
46
47 G_DEFINE_ABSTRACT_TYPE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT);
48
49 typedef struct {
50   GFile *file;
51   guint32 last_sent_change_time; /* 0 == not sent */
52   guint32 send_delayed_change_at; /* 0 == never */
53   guint32 send_virtual_changes_done_at; /* 0 == never */
54 } RateLimiter;
55
56 struct _GFileMonitorPrivate {
57   gboolean cancelled;
58   int rate_limit_msec;
59
60   /* Rate limiting change events */
61   GHashTable *rate_limiter;
62
63   GSource *timeout;
64   guint32 timeout_fires_at;
65 };
66
67 enum {
68   PROP_0,
69   PROP_RATE_LIMIT,
70   PROP_CANCELLED
71 };
72
73 static void
74 g_file_monitor_set_property (GObject      *object,
75                              guint         prop_id,
76                              const GValue *value,
77                              GParamSpec   *pspec)
78 {
79   GFileMonitor *monitor;
80
81   monitor = G_FILE_MONITOR (object);
82
83   switch (prop_id)
84     {
85     case PROP_RATE_LIMIT:
86       g_file_monitor_set_rate_limit (monitor, g_value_get_int (value));
87       break;
88
89     default:
90       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
91       break;
92     }
93 }
94
95 static void
96 g_file_monitor_get_property (GObject    *object,
97                              guint       prop_id,
98                              GValue     *value,
99                              GParamSpec *pspec)
100 {
101   GFileMonitor *monitor;
102   GFileMonitorPrivate *priv;
103
104   monitor = G_FILE_MONITOR (object);
105   priv = monitor->priv;
106
107   switch (prop_id)
108     {
109     case PROP_RATE_LIMIT:
110       g_value_set_int (value, priv->rate_limit_msec);
111       break;
112
113     case PROP_CANCELLED:
114       g_value_set_boolean (value, priv->cancelled);
115       break;
116
117     default:
118       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
119       break;
120     }
121 }
122
123 #define DEFAULT_RATE_LIMIT_MSECS 800
124 #define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
125
126 static guint signals[LAST_SIGNAL] = { 0 };
127
128 static void
129 rate_limiter_free (RateLimiter *limiter)
130 {
131   g_object_unref (limiter->file);
132   g_slice_free (RateLimiter, limiter);
133 }
134
135 static void
136 g_file_monitor_finalize (GObject *object)
137 {
138   GFileMonitor *monitor;
139
140   monitor = G_FILE_MONITOR (object);
141
142   if (monitor->priv->timeout)
143     {
144       g_source_destroy (monitor->priv->timeout);
145       g_source_unref (monitor->priv->timeout);
146     }
147
148   g_hash_table_destroy (monitor->priv->rate_limiter);
149   
150   if (G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize)
151     (*G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize) (object);
152 }
153
154 static void
155 g_file_monitor_dispose (GObject *object)
156 {
157   GFileMonitor *monitor;
158   
159   monitor = G_FILE_MONITOR (object);
160
161   /* Make sure we cancel on last unref */
162   if (!monitor->priv->cancelled)
163     g_file_monitor_cancel (monitor);
164   
165   if (G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose)
166     (*G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose) (object);
167 }
168
169 static void
170 g_file_monitor_class_init (GFileMonitorClass *klass)
171 {
172   GObjectClass *object_class;
173   
174   g_type_class_add_private (klass, sizeof (GFileMonitorPrivate));
175   
176   object_class = G_OBJECT_CLASS (klass);
177   object_class->finalize = g_file_monitor_finalize;
178   object_class->dispose = g_file_monitor_dispose;
179   object_class->get_property = g_file_monitor_get_property;
180   object_class->set_property = g_file_monitor_set_property;
181
182   /**
183    * GFileMonitor::changed:
184    * @monitor: a #GFileMonitor.
185    * @file: a #GFile.
186    * @other_file: a #GFile.
187    * @event_type: a #GFileMonitorEvent.
188    * 
189    * Emitted when a file has been changed. 
190    **/
191   signals[CHANGED] =
192     g_signal_new (I_("changed"),
193                   G_TYPE_FILE_MONITOR,
194                   G_SIGNAL_RUN_LAST,
195                   G_STRUCT_OFFSET (GFileMonitorClass, changed),
196                   NULL, NULL,
197                   _gio_marshal_VOID__OBJECT_OBJECT_INT,
198                   G_TYPE_NONE, 3,
199                   G_TYPE_FILE, G_TYPE_FILE, G_TYPE_INT); 
200
201   g_object_class_install_property (object_class,
202                                    PROP_RATE_LIMIT,
203                                    g_param_spec_int ("rate-limit",
204                                                      P_("Rate limit"),
205                                                      P_("The limit of the monitor to watch for changes, in milliseconds"),
206                                                      0, G_MAXINT,
207                                                      DEFAULT_RATE_LIMIT_MSECS,
208                                                      G_PARAM_READWRITE|
209                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
210
211   g_object_class_install_property (object_class,
212                                    PROP_CANCELLED,
213                                    g_param_spec_boolean ("cancelled",
214                                                          P_("Cancelled"),
215                                                          P_("Whether the monitor has been cancelled"),
216                                                          FALSE,
217                                                          G_PARAM_READABLE|
218                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
219 }
220
221 static void
222 g_file_monitor_init (GFileMonitor *monitor)
223 {
224   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
225                                                G_TYPE_FILE_MONITOR,
226                                                GFileMonitorPrivate);
227   monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
228   monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
229                                                        NULL, (GDestroyNotify) rate_limiter_free);
230 }
231
232 /**
233  * g_file_monitor_is_cancelled:
234  * @monitor: a #GFileMonitor
235  * 
236  * Returns whether the monitor is canceled.
237  *
238  * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
239  **/
240 gboolean
241 g_file_monitor_is_cancelled (GFileMonitor *monitor)
242 {
243   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
244
245   return monitor->priv->cancelled;
246 }
247
248 /**
249  * g_file_monitor_cancel:
250  * @monitor: a #GFileMonitor.
251  * 
252  * Cancels a file monitor.
253  * 
254  * Returns: %TRUE if monitor was cancelled.
255  **/
256 gboolean
257 g_file_monitor_cancel (GFileMonitor* monitor)
258 {
259   GFileMonitorClass *klass;
260   
261   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
262   
263   if (monitor->priv->cancelled)
264     return TRUE;
265   
266   monitor->priv->cancelled = TRUE;
267   g_object_notify (G_OBJECT (monitor), "cancelled");
268
269   klass = G_FILE_MONITOR_GET_CLASS (monitor);
270   return (* klass->cancel) (monitor);
271 }
272
273 /**
274  * g_file_monitor_set_rate_limit:
275  * @monitor: a #GFileMonitor.
276  * @limit_msecs: a integer with the limit in milliseconds to 
277  * poll for changes.
278  *
279  * Sets the rate limit to which the @monitor will report
280  * consecutive change events to the same file. 
281  * 
282  **/
283 void
284 g_file_monitor_set_rate_limit (GFileMonitor *monitor,
285                                int           limit_msecs)
286 {
287   GFileMonitorPrivate *priv;
288   
289   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
290   
291   priv = monitor->priv;
292   if (priv->rate_limit_msec != limit_msecs)
293     {
294       monitor->priv->rate_limit_msec = limit_msecs;
295       g_object_notify (G_OBJECT (monitor), "rate-limit");
296     }
297 }
298
299 static guint32
300 get_time_msecs (void)
301 {
302   return g_thread_gettime() / (1000 * 1000);
303 }
304
305 static guint32
306 time_difference (guint32 from, guint32 to)
307 {
308   if (from > to)
309     return 0;
310   return to - from;
311 }
312
313 /* Change event rate limiting support: */
314
315 static RateLimiter *
316 new_limiter (GFileMonitor *monitor,
317              GFile             *file)
318 {
319   RateLimiter *limiter;
320
321   limiter = g_slice_new0 (RateLimiter);
322   limiter->file = g_object_ref (file);
323   g_hash_table_insert (monitor->priv->rate_limiter, file, limiter);
324   
325   return limiter;
326 }
327
328 static void
329 rate_limiter_send_virtual_changes_done_now (GFileMonitor *monitor, 
330                                             RateLimiter  *limiter)
331 {
332   if (limiter->send_virtual_changes_done_at != 0)
333     {
334       g_signal_emit (monitor, signals[CHANGED], 0,
335                      limiter->file, NULL,
336                      G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
337       limiter->send_virtual_changes_done_at = 0;
338     }
339 }
340
341 static void
342 rate_limiter_send_delayed_change_now (GFileMonitor *monitor, 
343                                       RateLimiter *limiter, 
344                                       guint32 time_now)
345 {
346   if (limiter->send_delayed_change_at != 0)
347     {
348       g_signal_emit (monitor, signals[CHANGED], 0,
349                      limiter->file, NULL,
350                      G_FILE_MONITOR_EVENT_CHANGED);
351       limiter->send_delayed_change_at = 0;
352       limiter->last_sent_change_time = time_now;
353     }
354 }
355
356 typedef struct {
357   guint32 min_time;
358   guint32 time_now;
359   GFileMonitor *monitor;
360 } ForEachData;
361
362 static gboolean
363 calc_min_time (GFileMonitor *monitor, 
364                RateLimiter *limiter, 
365                guint32 time_now, 
366                guint32 *min_time)
367 {
368   gboolean delete_me;
369   guint32 expire_at;
370
371   delete_me = TRUE;
372
373   if (limiter->last_sent_change_time != 0)
374     {
375       /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventualy */
376       expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec;
377
378       if (time_difference (time_now, expire_at) > 0)
379         {
380           delete_me = FALSE;
381           *min_time = MIN (*min_time,
382                            time_difference (time_now, expire_at));
383         }
384     }
385
386   if (limiter->send_delayed_change_at != 0)
387     {
388       delete_me = FALSE;
389       *min_time = MIN (*min_time,
390                        time_difference (time_now, limiter->send_delayed_change_at));
391     }
392
393   if (limiter->send_virtual_changes_done_at != 0)
394     {
395       delete_me = FALSE;
396       *min_time = MIN (*min_time,
397                        time_difference (time_now, limiter->send_virtual_changes_done_at));
398     }
399
400   return delete_me;
401 }
402
403 static gboolean
404 foreach_rate_limiter_fire (gpointer key,
405                            gpointer value,
406                            gpointer user_data)
407 {
408   RateLimiter *limiter = value;
409   ForEachData *data = user_data;
410
411   if (limiter->send_delayed_change_at != 0 &&
412       time_difference (data->time_now, limiter->send_delayed_change_at) == 0)
413     rate_limiter_send_delayed_change_now (data->monitor, limiter, data->time_now);
414   
415   if (limiter->send_virtual_changes_done_at != 0 &&
416       time_difference (data->time_now, limiter->send_virtual_changes_done_at) == 0)
417     rate_limiter_send_virtual_changes_done_now (data->monitor, limiter);
418   
419   return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
420 }
421
422 static gboolean 
423 rate_limiter_timeout (gpointer timeout_data)
424 {
425   GFileMonitor *monitor = timeout_data;
426   ForEachData data;
427   GSource *source;
428   
429   data.min_time = G_MAXUINT32;
430   data.monitor = monitor;
431   data.time_now = get_time_msecs ();
432   g_hash_table_foreach_remove (monitor->priv->rate_limiter,
433                                foreach_rate_limiter_fire,
434                                &data);
435   
436   /* Remove old timeout */
437   if (monitor->priv->timeout)
438     {
439       g_source_destroy (monitor->priv->timeout);
440       g_source_unref (monitor->priv->timeout);
441       monitor->priv->timeout = NULL;
442       monitor->priv->timeout_fires_at = 0;
443     }
444   
445   /* Set up new timeout */
446   if (data.min_time != G_MAXUINT32)
447     {
448       source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
449       g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
450       g_source_attach (source, NULL);
451       
452       monitor->priv->timeout = source;
453       monitor->priv->timeout_fires_at = data.time_now + data.min_time; 
454     }
455   
456   return FALSE;
457 }
458
459 static gboolean
460 foreach_rate_limiter_update (gpointer key,
461                              gpointer value,
462                              gpointer user_data)
463 {
464   RateLimiter *limiter = value;
465   ForEachData *data = user_data;
466
467   return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
468 }
469
470 static void
471 update_rate_limiter_timeout (GFileMonitor *monitor, 
472                              guint new_time)
473 {
474   ForEachData data;
475   GSource *source;
476   
477   if (monitor->priv->timeout_fires_at != 0 && new_time != 0 &&
478       time_difference (new_time, monitor->priv->timeout_fires_at) == 0)
479     return; /* Nothing to do, we already fire earlier than that */
480
481   data.min_time = G_MAXUINT32;
482   data.monitor = monitor;
483   data.time_now = get_time_msecs ();
484   g_hash_table_foreach_remove (monitor->priv->rate_limiter,
485                                foreach_rate_limiter_update,
486                                &data);
487
488   /* Remove old timeout */
489   if (monitor->priv->timeout)
490     {
491       g_source_destroy (monitor->priv->timeout);
492       g_source_unref (monitor->priv->timeout);
493       monitor->priv->timeout_fires_at = 0;
494       monitor->priv->timeout = NULL;
495     }
496
497   /* Set up new timeout */
498   if (data.min_time != G_MAXUINT32)
499     {
500       source = g_timeout_source_new (data.min_time + 1);  /* + 1 to make sure we've really passed the time */
501       g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
502       g_source_attach (source, NULL);
503       
504       monitor->priv->timeout = source;
505       monitor->priv->timeout_fires_at = data.time_now + data.min_time; 
506     }
507 }
508
509 /**
510  * g_file_monitor_emit_event:
511  * @monitor: a #GFileMonitor.
512  * @child: a #GFile.
513  * @other_file: a #GFile.
514  * @event_type: a set of #GFileMonitorEvent flags.
515  * 
516  * Emits the #GFileMonitor::changed signal if a change
517  * has taken place. Should be called from file monitor 
518  * implementations only.
519  **/
520 void
521 g_file_monitor_emit_event (GFileMonitor *monitor,
522                            GFile *child,
523                            GFile *other_file,
524                            GFileMonitorEvent event_type)
525 {
526   guint32 time_now, since_last;
527   gboolean emit_now;
528   RateLimiter *limiter;
529
530   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
531   g_return_if_fail (G_IS_FILE (child));
532
533   limiter = g_hash_table_lookup (monitor->priv->rate_limiter, child);
534
535   if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
536     {
537       if (limiter)
538         {
539           rate_limiter_send_delayed_change_now (monitor, limiter, get_time_msecs ());
540           if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
541             limiter->send_virtual_changes_done_at = 0;
542           else
543             rate_limiter_send_virtual_changes_done_now (monitor, limiter);
544           update_rate_limiter_timeout (monitor, 0);
545         }
546       g_signal_emit (monitor, signals[CHANGED], 0, child, other_file, event_type);
547     }
548   else
549     {
550       /* Changed event, rate limit */
551       time_now = get_time_msecs ();
552       emit_now = TRUE;
553       
554       if (limiter)
555         {
556           since_last = time_difference (limiter->last_sent_change_time, time_now);
557           if (since_last < monitor->priv->rate_limit_msec)
558             {
559               /* We ignore this change, but arm a timer so that we can fire it later if we
560                  don't get any other events (that kill this timeout) */
561               emit_now = FALSE;
562               if (limiter->send_delayed_change_at == 0)
563                 {
564                   limiter->send_delayed_change_at = time_now + monitor->priv->rate_limit_msec;
565                   update_rate_limiter_timeout (monitor, limiter->send_delayed_change_at);
566                 }
567             }
568         }
569       
570       if (limiter == NULL)
571         limiter = new_limiter (monitor, child);
572       
573       if (emit_now)
574         {
575           g_signal_emit (monitor, signals[CHANGED], 0, child, other_file, event_type);
576           
577           limiter->last_sent_change_time = time_now;
578           limiter->send_delayed_change_at = 0;
579           /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventualy */
580           update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec);
581         }
582       
583       /* Schedule a virtual change done. This is removed if we get a real one, and
584          postponed if we get more change events. */
585       
586       limiter->send_virtual_changes_done_at = time_now + DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS * 1000;
587       update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at);
588     }
589 }
590
591 #define __G_FILE_MONITOR_C__
592 #include "gioaliasdef.c"