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