Add g_key_file_save_to_file()
[glib.git] / gio / gfilemonitor.c
blobd8e974fd235dc33d73e9b5bb838377477ad17a64
1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
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.
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.
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.
20 * Author: Alexander Larsson <alexl@redhat.com>
23 #include "config.h"
24 #include <string.h>
26 #include "gfilemonitor.h"
27 #include "gioenumtypes.h"
28 #include "gfile.h"
29 #include "gvfs.h"
30 #include "glibintl.h"
33 struct _FileChange;
34 typedef struct _FileChange FileChange;
35 static void file_change_free (FileChange *change);
37 /**
38 * SECTION:gfilemonitor
39 * @short_description: File Monitor
40 * @include: gio/gio.h
42 * Monitors a file or directory for changes.
44 * To obtain a #GFileMonitor for a file or directory, use
45 * g_file_monitor(), g_file_monitor_file(), or
46 * g_file_monitor_directory().
48 * To get informed about changes to the file or directory you are
49 * monitoring, connect to the #GFileMonitor::changed signal. The
50 * signal will be emitted in the <link
51 * linkend="g-main-context-push-thread-default">thread-default main
52 * context</link> of the thread that the monitor was created in
53 * (though if the global default main context is blocked, this may
54 * cause notifications to be blocked even if the thread-default
55 * context is still running).
56 **/
58 G_LOCK_DEFINE_STATIC(cancelled);
60 enum {
61 CHANGED,
62 LAST_SIGNAL
65 typedef struct {
66 GFile *file;
67 guint32 last_sent_change_time; /* 0 == not sent */
68 guint32 send_delayed_change_at; /* 0 == never */
69 guint32 send_virtual_changes_done_at; /* 0 == never */
70 } RateLimiter;
72 struct _GFileMonitorPrivate {
73 gboolean cancelled;
74 int rate_limit_msec;
76 /* Rate limiting change events */
77 GHashTable *rate_limiter;
79 GMutex mutex;
80 GSource *pending_file_change_source;
81 GSList *pending_file_changes; /* FileChange */
83 GSource *timeout;
84 guint32 timeout_fires_at;
86 GMainContext *context;
89 enum {
90 PROP_0,
91 PROP_RATE_LIMIT,
92 PROP_CANCELLED,
93 PROP_CONTEXT
96 /* work around a limitation of the aliasing foo */
97 #undef g_file_monitor
99 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT)
101 static void
102 g_file_monitor_set_property (GObject *object,
103 guint prop_id,
104 const GValue *value,
105 GParamSpec *pspec)
107 GFileMonitor *monitor;
109 monitor = G_FILE_MONITOR (object);
111 switch (prop_id)
113 case PROP_RATE_LIMIT:
114 g_file_monitor_set_rate_limit (monitor, g_value_get_int (value));
115 break;
117 case PROP_CONTEXT:
118 monitor->priv->context = g_value_dup_boxed (value);
119 if (monitor->priv->context == NULL)
120 monitor->priv->context = g_main_context_ref_thread_default ();
121 break;
123 default:
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
125 break;
129 static void
130 g_file_monitor_get_property (GObject *object,
131 guint prop_id,
132 GValue *value,
133 GParamSpec *pspec)
135 GFileMonitor *monitor;
136 GFileMonitorPrivate *priv;
138 monitor = G_FILE_MONITOR (object);
139 priv = monitor->priv;
141 switch (prop_id)
143 case PROP_RATE_LIMIT:
144 g_value_set_int (value, priv->rate_limit_msec);
145 break;
147 case PROP_CANCELLED:
148 G_LOCK (cancelled);
149 g_value_set_boolean (value, priv->cancelled);
150 G_UNLOCK (cancelled);
151 break;
153 default:
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
155 break;
159 #define DEFAULT_RATE_LIMIT_MSECS 800
160 #define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
162 static guint signals[LAST_SIGNAL] = { 0 };
164 static void
165 rate_limiter_free (RateLimiter *limiter)
167 g_object_unref (limiter->file);
168 g_slice_free (RateLimiter, limiter);
171 static void
172 g_file_monitor_finalize (GObject *object)
174 GFileMonitor *monitor;
176 monitor = G_FILE_MONITOR (object);
178 if (monitor->priv->timeout)
180 g_source_destroy (monitor->priv->timeout);
181 g_source_unref (monitor->priv->timeout);
184 g_hash_table_destroy (monitor->priv->rate_limiter);
186 g_main_context_unref (monitor->priv->context);
187 g_mutex_clear (&monitor->priv->mutex);
189 G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object);
192 static void
193 g_file_monitor_dispose (GObject *object)
195 GFileMonitor *monitor;
196 GFileMonitorPrivate *priv;
198 monitor = G_FILE_MONITOR (object);
199 priv = monitor->priv;
201 if (priv->pending_file_change_source)
203 g_source_destroy (priv->pending_file_change_source);
204 g_source_unref (priv->pending_file_change_source);
205 priv->pending_file_change_source = NULL;
207 g_slist_free_full (priv->pending_file_changes, (GDestroyNotify) file_change_free);
208 priv->pending_file_changes = NULL;
210 /* Make sure we cancel on last unref */
211 g_file_monitor_cancel (monitor);
213 G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose (object);
216 static void
217 g_file_monitor_class_init (GFileMonitorClass *klass)
219 GObjectClass *object_class;
221 object_class = G_OBJECT_CLASS (klass);
222 object_class->finalize = g_file_monitor_finalize;
223 object_class->dispose = g_file_monitor_dispose;
224 object_class->get_property = g_file_monitor_get_property;
225 object_class->set_property = g_file_monitor_set_property;
228 * GFileMonitor::changed:
229 * @monitor: a #GFileMonitor.
230 * @file: a #GFile.
231 * @other_file: (allow-none): a #GFile or #NULL.
232 * @event_type: a #GFileMonitorEvent.
234 * Emitted when @file has been changed.
236 * If using #G_FILE_MONITOR_SEND_MOVED flag and @event_type is
237 * #G_FILE_MONITOR_EVENT_MOVED, @file will be set to a #GFile containing the
238 * old path, and @other_file will be set to a #GFile containing the new path.
240 * In all the other cases, @other_file will be set to #NULL.
242 signals[CHANGED] =
243 g_signal_new (I_("changed"),
244 G_TYPE_FILE_MONITOR,
245 G_SIGNAL_RUN_LAST,
246 G_STRUCT_OFFSET (GFileMonitorClass, changed),
247 NULL, NULL,
248 NULL,
249 G_TYPE_NONE, 3,
250 G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT);
252 g_object_class_install_property (object_class,
253 PROP_RATE_LIMIT,
254 g_param_spec_int ("rate-limit",
255 P_("Rate limit"),
256 P_("The limit of the monitor to watch for changes, in milliseconds"),
257 0, G_MAXINT,
258 DEFAULT_RATE_LIMIT_MSECS,
259 G_PARAM_READWRITE|
260 G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
262 g_object_class_install_property (object_class,
263 PROP_CANCELLED,
264 g_param_spec_boolean ("cancelled",
265 P_("Cancelled"),
266 P_("Whether the monitor has been cancelled"),
267 FALSE,
268 G_PARAM_READABLE|
269 G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
271 g_object_class_install_property (object_class,
272 PROP_CONTEXT,
273 g_param_spec_boxed ("context",
274 P_("Context"),
275 P_("The main context to dispatch from"),
276 G_TYPE_MAIN_CONTEXT, G_PARAM_WRITABLE |
277 G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
280 static void
281 g_file_monitor_init (GFileMonitor *monitor)
283 monitor->priv = g_file_monitor_get_instance_private (monitor);
284 monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
285 monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
286 NULL, (GDestroyNotify) rate_limiter_free);
287 g_mutex_init (&monitor->priv->mutex);
291 * g_file_monitor_is_cancelled:
292 * @monitor: a #GFileMonitor
294 * Returns whether the monitor is canceled.
296 * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
298 gboolean
299 g_file_monitor_is_cancelled (GFileMonitor *monitor)
301 gboolean res;
303 g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
305 G_LOCK (cancelled);
306 res = monitor->priv->cancelled;
307 G_UNLOCK (cancelled);
309 return res;
313 * g_file_monitor_cancel:
314 * @monitor: a #GFileMonitor.
316 * Cancels a file monitor.
318 * Returns: %TRUE if monitor was cancelled.
320 gboolean
321 g_file_monitor_cancel (GFileMonitor* monitor)
323 GFileMonitorClass *klass;
325 g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
327 G_LOCK (cancelled);
328 if (monitor->priv->cancelled)
330 G_UNLOCK (cancelled);
331 return TRUE;
334 monitor->priv->cancelled = TRUE;
335 G_UNLOCK (cancelled);
337 g_object_notify (G_OBJECT (monitor), "cancelled");
339 klass = G_FILE_MONITOR_GET_CLASS (monitor);
340 return (* klass->cancel) (monitor);
344 * g_file_monitor_set_rate_limit:
345 * @monitor: a #GFileMonitor.
346 * @limit_msecs: a non-negative integer with the limit in milliseconds
347 * to poll for changes
349 * Sets the rate limit to which the @monitor will report
350 * consecutive change events to the same file.
352 void
353 g_file_monitor_set_rate_limit (GFileMonitor *monitor,
354 gint limit_msecs)
356 GFileMonitorPrivate *priv;
358 g_return_if_fail (G_IS_FILE_MONITOR (monitor));
359 g_return_if_fail (limit_msecs >= 0);
361 priv = monitor->priv;
362 if (priv->rate_limit_msec != limit_msecs)
364 monitor->priv->rate_limit_msec = limit_msecs;
365 g_object_notify (G_OBJECT (monitor), "rate-limit");
369 struct _FileChange {
370 GFile *child;
371 GFile *other_file;
372 GFileMonitorEvent event_type;
375 static void
376 file_change_free (FileChange *change)
378 g_object_unref (change->child);
379 if (change->other_file)
380 g_object_unref (change->other_file);
382 g_slice_free (FileChange, change);
385 static gboolean
386 emit_cb (gpointer data)
388 GFileMonitor *monitor = G_FILE_MONITOR (data);
389 GSList *pending, *iter;
391 g_mutex_lock (&monitor->priv->mutex);
392 pending = g_slist_reverse (monitor->priv->pending_file_changes);
393 monitor->priv->pending_file_changes = NULL;
394 if (monitor->priv->pending_file_change_source)
396 g_source_unref (monitor->priv->pending_file_change_source);
397 monitor->priv->pending_file_change_source = NULL;
399 g_mutex_unlock (&monitor->priv->mutex);
401 g_object_ref (monitor);
402 for (iter = pending; iter; iter = iter->next)
404 FileChange *change = iter->data;
406 g_signal_emit (monitor, signals[CHANGED], 0,
407 change->child, change->other_file, change->event_type);
408 file_change_free (change);
410 g_slist_free (pending);
411 g_object_unref (monitor);
413 return FALSE;
416 static void
417 emit_in_idle (GFileMonitor *monitor,
418 GFile *child,
419 GFile *other_file,
420 GFileMonitorEvent event_type)
422 GSource *source;
423 FileChange *change;
424 GFileMonitorPrivate *priv;
426 priv = monitor->priv;
428 change = g_slice_new (FileChange);
430 change->child = g_object_ref (child);
431 if (other_file)
432 change->other_file = g_object_ref (other_file);
433 else
434 change->other_file = NULL;
435 change->event_type = event_type;
437 g_mutex_lock (&monitor->priv->mutex);
438 if (!priv->pending_file_change_source)
440 source = g_idle_source_new ();
441 priv->pending_file_change_source = source;
442 g_source_set_priority (source, 0);
444 /* We don't ref monitor here - instead dispose will free any
445 * pending idles.
447 g_source_set_callback (source, emit_cb, monitor, NULL);
448 g_source_attach (source, monitor->priv->context);
450 /* We reverse this in the processor */
451 priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change);
452 g_mutex_unlock (&monitor->priv->mutex);
455 static guint32
456 get_time_msecs (void)
458 return g_get_monotonic_time () / G_TIME_SPAN_MILLISECOND;
461 static guint32
462 time_difference (guint32 from, guint32 to)
464 if (from > to)
465 return 0;
466 return to - from;
469 /* Change event rate limiting support: */
471 static RateLimiter *
472 new_limiter (GFileMonitor *monitor,
473 GFile *file)
475 RateLimiter *limiter;
477 limiter = g_slice_new0 (RateLimiter);
478 limiter->file = g_object_ref (file);
479 g_hash_table_insert (monitor->priv->rate_limiter, file, limiter);
481 return limiter;
484 static void
485 rate_limiter_send_virtual_changes_done_now (GFileMonitor *monitor,
486 RateLimiter *limiter)
488 if (limiter->send_virtual_changes_done_at != 0)
490 emit_in_idle (monitor, limiter->file, NULL,
491 G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
492 limiter->send_virtual_changes_done_at = 0;
496 static void
497 rate_limiter_send_delayed_change_now (GFileMonitor *monitor,
498 RateLimiter *limiter,
499 guint32 time_now)
501 if (limiter->send_delayed_change_at != 0)
503 emit_in_idle (monitor,
504 limiter->file, NULL,
505 G_FILE_MONITOR_EVENT_CHANGED);
506 limiter->send_delayed_change_at = 0;
507 limiter->last_sent_change_time = time_now;
511 typedef struct {
512 guint32 min_time;
513 guint32 time_now;
514 GFileMonitor *monitor;
515 } ForEachData;
517 static gboolean
518 calc_min_time (GFileMonitor *monitor,
519 RateLimiter *limiter,
520 guint32 time_now,
521 guint32 *min_time)
523 gboolean delete_me;
524 guint32 expire_at;
526 delete_me = TRUE;
528 if (limiter->last_sent_change_time != 0)
530 /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventually */
531 expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec;
533 if (time_difference (time_now, expire_at) > 0)
535 delete_me = FALSE;
536 *min_time = MIN (*min_time,
537 time_difference (time_now, expire_at));
541 if (limiter->send_delayed_change_at != 0)
543 delete_me = FALSE;
544 *min_time = MIN (*min_time,
545 time_difference (time_now, limiter->send_delayed_change_at));
548 if (limiter->send_virtual_changes_done_at != 0)
550 delete_me = FALSE;
551 *min_time = MIN (*min_time,
552 time_difference (time_now, limiter->send_virtual_changes_done_at));
555 return delete_me;
558 static gboolean
559 foreach_rate_limiter_fire (gpointer key,
560 gpointer value,
561 gpointer user_data)
563 RateLimiter *limiter = value;
564 ForEachData *data = user_data;
566 if (limiter->send_delayed_change_at != 0 &&
567 time_difference (data->time_now, limiter->send_delayed_change_at) == 0)
568 rate_limiter_send_delayed_change_now (data->monitor, limiter, data->time_now);
570 if (limiter->send_virtual_changes_done_at != 0 &&
571 time_difference (data->time_now, limiter->send_virtual_changes_done_at) == 0)
572 rate_limiter_send_virtual_changes_done_now (data->monitor, limiter);
574 return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
577 static gboolean
578 rate_limiter_timeout (gpointer timeout_data)
580 GFileMonitor *monitor = timeout_data;
581 ForEachData data;
582 GSource *source;
584 data.min_time = G_MAXUINT32;
585 data.monitor = monitor;
586 data.time_now = get_time_msecs ();
587 g_hash_table_foreach_remove (monitor->priv->rate_limiter,
588 foreach_rate_limiter_fire,
589 &data);
591 /* Remove old timeout */
592 if (monitor->priv->timeout)
594 g_source_destroy (monitor->priv->timeout);
595 g_source_unref (monitor->priv->timeout);
596 monitor->priv->timeout = NULL;
597 monitor->priv->timeout_fires_at = 0;
600 /* Set up new timeout */
601 if (data.min_time != G_MAXUINT32)
603 source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
604 g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
605 g_source_attach (source, monitor->priv->context);
607 monitor->priv->timeout = source;
608 monitor->priv->timeout_fires_at = data.time_now + data.min_time;
611 return FALSE;
614 static gboolean
615 foreach_rate_limiter_update (gpointer key,
616 gpointer value,
617 gpointer user_data)
619 RateLimiter *limiter = value;
620 ForEachData *data = user_data;
622 return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
625 static void
626 update_rate_limiter_timeout (GFileMonitor *monitor,
627 guint new_time)
629 ForEachData data;
630 GSource *source;
632 if (monitor->priv->timeout_fires_at != 0 && new_time != 0 &&
633 time_difference (new_time, monitor->priv->timeout_fires_at) == 0)
634 return; /* Nothing to do, we already fire earlier than that */
636 data.min_time = G_MAXUINT32;
637 data.monitor = monitor;
638 data.time_now = get_time_msecs ();
639 g_hash_table_foreach_remove (monitor->priv->rate_limiter,
640 foreach_rate_limiter_update,
641 &data);
643 /* Remove old timeout */
644 if (monitor->priv->timeout)
646 g_source_destroy (monitor->priv->timeout);
647 g_source_unref (monitor->priv->timeout);
648 monitor->priv->timeout_fires_at = 0;
649 monitor->priv->timeout = NULL;
652 /* Set up new timeout */
653 if (data.min_time != G_MAXUINT32)
655 source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
656 g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
657 g_source_attach (source, monitor->priv->context);
659 monitor->priv->timeout = source;
660 monitor->priv->timeout_fires_at = data.time_now + data.min_time;
665 * g_file_monitor_emit_event:
666 * @monitor: a #GFileMonitor.
667 * @child: a #GFile.
668 * @other_file: a #GFile.
669 * @event_type: a set of #GFileMonitorEvent flags.
671 * Emits the #GFileMonitor::changed signal if a change
672 * has taken place. Should be called from file monitor
673 * implementations only.
675 * The signal will be emitted from an idle handler (in the <link
676 * linkend="g-main-context-push-thread-default">thread-default main
677 * context</link>).
679 void
680 g_file_monitor_emit_event (GFileMonitor *monitor,
681 GFile *child,
682 GFile *other_file,
683 GFileMonitorEvent event_type)
685 guint32 time_now, since_last;
686 gboolean emit_now;
687 RateLimiter *limiter;
689 g_return_if_fail (G_IS_FILE_MONITOR (monitor));
690 g_return_if_fail (G_IS_FILE (child));
692 limiter = g_hash_table_lookup (monitor->priv->rate_limiter, child);
694 if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
696 if (limiter)
698 rate_limiter_send_delayed_change_now (monitor, limiter, get_time_msecs ());
699 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
700 limiter->send_virtual_changes_done_at = 0;
701 else
702 rate_limiter_send_virtual_changes_done_now (monitor, limiter);
703 update_rate_limiter_timeout (monitor, 0);
705 emit_in_idle (monitor, child, other_file, event_type);
707 else
709 /* Changed event, rate limit */
710 time_now = get_time_msecs ();
711 emit_now = TRUE;
713 if (limiter)
715 since_last = time_difference (limiter->last_sent_change_time, time_now);
716 if (since_last < monitor->priv->rate_limit_msec)
718 /* We ignore this change, but arm a timer so that we can fire it later if we
719 don't get any other events (that kill this timeout) */
720 emit_now = FALSE;
721 if (limiter->send_delayed_change_at == 0)
723 limiter->send_delayed_change_at = time_now + monitor->priv->rate_limit_msec;
724 update_rate_limiter_timeout (monitor, limiter->send_delayed_change_at);
729 if (limiter == NULL)
730 limiter = new_limiter (monitor, child);
732 if (emit_now)
734 emit_in_idle (monitor, child, other_file, event_type);
736 limiter->last_sent_change_time = time_now;
737 limiter->send_delayed_change_at = 0;
738 /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventually */
739 update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec);
742 /* Schedule a virtual change done. This is removed if we get a real one, and
743 postponed if we get more change events. */
745 limiter->send_virtual_changes_done_at = time_now + DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS * 1000;
746 update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at);