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