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