1 /* GIO - GLib Input, Output and Streaming Library
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>
26 #include "gfilemonitor.h"
27 #include "gioenumtypes.h"
34 typedef struct _FileChange FileChange
;
35 static void file_change_free (FileChange
*change
);
38 * SECTION:gfilemonitor
39 * @short_description: File Monitor
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).
58 G_LOCK_DEFINE_STATIC(cancelled
);
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 */
72 struct _GFileMonitorPrivate
{
76 /* Rate limiting change events */
77 GHashTable
*rate_limiter
;
80 GSource
*pending_file_change_source
;
81 GSList
*pending_file_changes
; /* FileChange */
84 guint32 timeout_fires_at
;
86 GMainContext
*context
;
96 /* work around a limitation of the aliasing foo */
99 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GFileMonitor
, g_file_monitor
, G_TYPE_OBJECT
)
102 g_file_monitor_set_property (GObject
*object
,
107 GFileMonitor
*monitor
;
109 monitor
= G_FILE_MONITOR (object
);
113 case PROP_RATE_LIMIT
:
114 g_file_monitor_set_rate_limit (monitor
, g_value_get_int (value
));
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 ();
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
130 g_file_monitor_get_property (GObject
*object
,
135 GFileMonitor
*monitor
;
136 GFileMonitorPrivate
*priv
;
138 monitor
= G_FILE_MONITOR (object
);
139 priv
= monitor
->priv
;
143 case PROP_RATE_LIMIT
:
144 g_value_set_int (value
, priv
->rate_limit_msec
);
149 g_value_set_boolean (value
, priv
->cancelled
);
150 G_UNLOCK (cancelled
);
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
159 #define DEFAULT_RATE_LIMIT_MSECS 800
160 #define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
162 static guint signals
[LAST_SIGNAL
] = { 0 };
165 rate_limiter_free (RateLimiter
*limiter
)
167 g_object_unref (limiter
->file
);
168 g_slice_free (RateLimiter
, limiter
);
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
);
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
);
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.
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.
243 g_signal_new (I_("changed"),
246 G_STRUCT_OFFSET (GFileMonitorClass
, changed
),
250 G_TYPE_FILE
, G_TYPE_FILE
, G_TYPE_FILE_MONITOR_EVENT
);
252 g_object_class_install_property (object_class
,
254 g_param_spec_int ("rate-limit",
256 P_("The limit of the monitor to watch for changes, in milliseconds"),
258 DEFAULT_RATE_LIMIT_MSECS
,
260 G_PARAM_STATIC_NAME
|G_PARAM_STATIC_NICK
|G_PARAM_STATIC_BLURB
));
262 g_object_class_install_property (object_class
,
264 g_param_spec_boolean ("cancelled",
266 P_("Whether the monitor has been cancelled"),
269 G_PARAM_STATIC_NAME
|G_PARAM_STATIC_NICK
|G_PARAM_STATIC_BLURB
));
271 g_object_class_install_property (object_class
,
273 g_param_spec_boxed ("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
));
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.
299 g_file_monitor_is_cancelled (GFileMonitor
*monitor
)
303 g_return_val_if_fail (G_IS_FILE_MONITOR (monitor
), FALSE
);
306 res
= monitor
->priv
->cancelled
;
307 G_UNLOCK (cancelled
);
313 * g_file_monitor_cancel:
314 * @monitor: a #GFileMonitor.
316 * Cancels a file monitor.
318 * Returns: %TRUE if monitor was cancelled.
321 g_file_monitor_cancel (GFileMonitor
* monitor
)
323 GFileMonitorClass
*klass
;
325 g_return_val_if_fail (G_IS_FILE_MONITOR (monitor
), FALSE
);
328 if (monitor
->priv
->cancelled
)
330 G_UNLOCK (cancelled
);
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.
353 g_file_monitor_set_rate_limit (GFileMonitor
*monitor
,
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");
372 GFileMonitorEvent event_type
;
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
);
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
);
417 emit_in_idle (GFileMonitor
*monitor
,
420 GFileMonitorEvent event_type
)
424 GFileMonitorPrivate
*priv
;
426 priv
= monitor
->priv
;
428 change
= g_slice_new (FileChange
);
430 change
->child
= g_object_ref (child
);
432 change
->other_file
= g_object_ref (other_file
);
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
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
);
456 get_time_msecs (void)
458 return g_get_monotonic_time () / G_TIME_SPAN_MILLISECOND
;
462 time_difference (guint32 from
, guint32 to
)
469 /* Change event rate limiting support: */
472 new_limiter (GFileMonitor
*monitor
,
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
);
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;
497 rate_limiter_send_delayed_change_now (GFileMonitor
*monitor
,
498 RateLimiter
*limiter
,
501 if (limiter
->send_delayed_change_at
!= 0)
503 emit_in_idle (monitor
,
505 G_FILE_MONITOR_EVENT_CHANGED
);
506 limiter
->send_delayed_change_at
= 0;
507 limiter
->last_sent_change_time
= time_now
;
514 GFileMonitor
*monitor
;
518 calc_min_time (GFileMonitor
*monitor
,
519 RateLimiter
*limiter
,
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)
536 *min_time
= MIN (*min_time
,
537 time_difference (time_now
, expire_at
));
541 if (limiter
->send_delayed_change_at
!= 0)
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)
551 *min_time
= MIN (*min_time
,
552 time_difference (time_now
, limiter
->send_virtual_changes_done_at
));
559 foreach_rate_limiter_fire (gpointer key
,
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
);
578 rate_limiter_timeout (gpointer timeout_data
)
580 GFileMonitor
*monitor
= timeout_data
;
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
,
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
;
615 foreach_rate_limiter_update (gpointer key
,
619 RateLimiter
*limiter
= value
;
620 ForEachData
*data
= user_data
;
622 return calc_min_time (data
->monitor
, limiter
, data
->time_now
, &data
->min_time
);
626 update_rate_limiter_timeout (GFileMonitor
*monitor
,
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
,
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.
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
680 g_file_monitor_emit_event (GFileMonitor
*monitor
,
683 GFileMonitorEvent event_type
)
685 guint32 time_now
, since_last
;
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
)
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;
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
);
709 /* Changed event, rate limit */
710 time_now
= get_time_msecs ();
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) */
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
);
730 limiter
= new_limiter (monitor
, child
);
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
);