LCOV - code coverage report
Current view: top level - gio - glocalfilemonitor.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 85.2 % 366 312
Test Date: 2024-11-26 05:23:01 Functions: 90.2 % 41 37
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* GIO - GLib Input, Output and Streaming Library
       2                 :             :  *
       3                 :             :  * Copyright (C) 2006-2007 Red Hat, Inc.
       4                 :             :  *
       5                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :             :  *
       7                 :             :  * This library is free software; you can redistribute it and/or
       8                 :             :  * modify it under the terms of the GNU Lesser General Public
       9                 :             :  * License as published by the Free Software Foundation; either
      10                 :             :  * version 2.1 of the License, or (at your option) any later version.
      11                 :             :  *
      12                 :             :  * This library is distributed in the hope that it will be useful,
      13                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :             :  * Lesser General Public License for more details.
      16                 :             :  *
      17                 :             :  * You should have received a copy of the GNU Lesser General
      18                 :             :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19                 :             :  *
      20                 :             :  * Author: Alexander Larsson <alexl@redhat.com>
      21                 :             :  */
      22                 :             : 
      23                 :             : #include "config.h"
      24                 :             : 
      25                 :             : #include "gioenumtypes.h"
      26                 :             : #include "glocalfilemonitor.h"
      27                 :             : #include "giomodule-priv.h"
      28                 :             : #include "gioerror.h"
      29                 :             : #include "glibintl.h"
      30                 :             : #include "glocalfile.h"
      31                 :             : #include "glib-private.h"
      32                 :             : 
      33                 :             : #include <string.h>
      34                 :             : 
      35                 :             : #define DEFAULT_RATE_LIMIT                           800 * G_TIME_SPAN_MILLISECOND
      36                 :             : #define VIRTUAL_CHANGES_DONE_DELAY                     2 * G_TIME_SPAN_SECOND
      37                 :             : 
      38                 :             : /* GFileMonitorSource is a GSource responsible for emitting the changed
      39                 :             :  * signals in the owner-context of the GFileMonitor.
      40                 :             :  *
      41                 :             :  * It contains functionality for cross-thread queuing of events.  It
      42                 :             :  * also handles merging of CHANGED events and emission of CHANGES_DONE
      43                 :             :  * events.
      44                 :             :  *
      45                 :             :  * We use the "priv" pointer in the external struct to store it.
      46                 :             :  */
      47                 :             : struct _GFileMonitorSource {
      48                 :             :   GSource       source;
      49                 :             : 
      50                 :             :   GMutex        lock;
      51                 :             :   GWeakRef      instance_ref;
      52                 :             :   GFileMonitorFlags flags;
      53                 :             :   gchar        *dirname;
      54                 :             :   gchar        *basename;
      55                 :             :   gchar        *filename;
      56                 :             :   GSequence    *pending_changes; /* sorted by ready time */
      57                 :             :   GHashTable   *pending_changes_table;
      58                 :             :   GQueue        event_queue;
      59                 :             :   gint64        rate_limit;
      60                 :             : };
      61                 :             : 
      62                 :             : /* PendingChange is a struct to keep track of a file that needs to have
      63                 :             :  * (at least) a CHANGES_DONE_HINT event sent for it in the near future.
      64                 :             :  *
      65                 :             :  * If 'dirty' is TRUE then a CHANGED event also needs to be sent.
      66                 :             :  *
      67                 :             :  * last_emission is the last time a CHANGED event was emitted.  It is
      68                 :             :  * used to calculate the time to send the next event.
      69                 :             :  */
      70                 :             : typedef struct {
      71                 :             :   gchar    *child;
      72                 :             :   guint64   last_emission : 63;
      73                 :             :   guint64   dirty         :  1;
      74                 :             : } PendingChange;
      75                 :             : 
      76                 :             : /* QueuedEvent is a signal that will be sent immediately, as soon as the
      77                 :             :  * source gets a chance to dispatch.  The existence of any queued event
      78                 :             :  * implies that the source is ready now.
      79                 :             :  */
      80                 :             : typedef struct
      81                 :             : {
      82                 :             :   GFileMonitorEvent event_type;
      83                 :             :   GFile *child;
      84                 :             :   GFile *other;
      85                 :             : } QueuedEvent;
      86                 :             : 
      87                 :             : static gint64
      88                 :         114 : pending_change_get_ready_time (const PendingChange *change,
      89                 :             :                                GFileMonitorSource  *fms)
      90                 :             : {
      91                 :         114 :   if (change->dirty)
      92                 :          82 :     return change->last_emission + fms->rate_limit;
      93                 :             :   else
      94                 :          32 :     return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY;
      95                 :             : }
      96                 :             : 
      97                 :             : static int
      98                 :           0 : pending_change_compare_ready_time (gconstpointer a_p,
      99                 :             :                                    gconstpointer b_p,
     100                 :             :                                    gpointer      user_data)
     101                 :             : {
     102                 :           0 :   GFileMonitorSource *fms = user_data;
     103                 :           0 :   const PendingChange *a = a_p;
     104                 :           0 :   const PendingChange *b = b_p;
     105                 :             :   gint64 ready_time_a;
     106                 :             :   gint64 ready_time_b;
     107                 :             : 
     108                 :           0 :   ready_time_a = pending_change_get_ready_time (a, fms);
     109                 :           0 :   ready_time_b = pending_change_get_ready_time (b, fms);
     110                 :             : 
     111                 :           0 :   if (ready_time_a < ready_time_b)
     112                 :           0 :     return -1;
     113                 :             :   else
     114                 :           0 :     return ready_time_a > ready_time_b;
     115                 :             : }
     116                 :             : 
     117                 :             : static void
     118                 :          87 : pending_change_free (gpointer data)
     119                 :             : {
     120                 :          87 :   PendingChange *change = data;
     121                 :             : 
     122                 :          87 :   g_free (change->child);
     123                 :             : 
     124                 :          87 :   g_slice_free (PendingChange, change);
     125                 :          87 : }
     126                 :             : 
     127                 :             : static void
     128                 :        1057 : queued_event_free (QueuedEvent *event)
     129                 :             : {
     130                 :        1057 :   g_object_unref (event->child);
     131                 :        1057 :   if (event->other)
     132                 :           4 :     g_object_unref (event->other);
     133                 :             : 
     134                 :        1057 :   g_slice_free (QueuedEvent, event);
     135                 :        1057 : }
     136                 :             : 
     137                 :             : static gint64
     138                 :        2102 : g_file_monitor_source_get_ready_time (GFileMonitorSource *fms)
     139                 :             : {
     140                 :             :   GSequenceIter *iter;
     141                 :             : 
     142                 :        2102 :   if (fms->event_queue.length)
     143                 :        1097 :     return 0;
     144                 :             : 
     145                 :        1005 :   iter = g_sequence_get_begin_iter (fms->pending_changes);
     146                 :        1005 :   if (g_sequence_iter_is_end (iter))
     147                 :         938 :     return -1;
     148                 :             : 
     149                 :          67 :   return pending_change_get_ready_time (g_sequence_get (iter), fms);
     150                 :             : }
     151                 :             : 
     152                 :             : static void
     153                 :        2102 : g_file_monitor_source_update_ready_time (GFileMonitorSource *fms)
     154                 :             : {
     155                 :        2102 :   g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms));
     156                 :        2102 : }
     157                 :             : 
     158                 :             : static GSequenceIter *
     159                 :        1090 : g_file_monitor_source_find_pending_change (GFileMonitorSource *fms,
     160                 :             :                                            const gchar        *child)
     161                 :             : {
     162                 :        1090 :   return g_hash_table_lookup (fms->pending_changes_table, child);
     163                 :             : }
     164                 :             : 
     165                 :             : static void
     166                 :          87 : g_file_monitor_source_add_pending_change (GFileMonitorSource *fms,
     167                 :             :                                           const gchar        *child,
     168                 :             :                                           gint64              now)
     169                 :             : {
     170                 :             :   PendingChange *change;
     171                 :             :   GSequenceIter *iter;
     172                 :             : 
     173                 :          87 :   change = g_slice_new (PendingChange);
     174                 :          87 :   change->child = g_strdup (child);
     175                 :          87 :   change->last_emission = now;
     176                 :          87 :   change->dirty = FALSE;
     177                 :             : 
     178                 :          87 :   iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms);
     179                 :          87 :   g_hash_table_insert (fms->pending_changes_table, change->child, iter);
     180                 :          87 : }
     181                 :             : 
     182                 :             : static gboolean
     183                 :          56 : g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms,
     184                 :             :                                                 GSequenceIter      *iter)
     185                 :             : {
     186                 :             :   PendingChange *change;
     187                 :             : 
     188                 :          56 :   change = g_sequence_get (iter);
     189                 :             : 
     190                 :             :   /* if it was already dirty then this change is 'uninteresting' */
     191                 :          56 :   if (change->dirty)
     192                 :           3 :     return FALSE;
     193                 :             : 
     194                 :          53 :   change->dirty = TRUE;
     195                 :             : 
     196                 :          53 :   g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
     197                 :             : 
     198                 :          53 :   return TRUE;
     199                 :             : }
     200                 :             : 
     201                 :             : static gboolean
     202                 :          52 : g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms,
     203                 :             :                                                 GSequenceIter      *iter)
     204                 :             : {
     205                 :             :   PendingChange *change;
     206                 :             : 
     207                 :          52 :   change = g_sequence_get (iter);
     208                 :             : 
     209                 :          52 :   return change->dirty;
     210                 :             : }
     211                 :             : 
     212                 :             : static void
     213                 :          52 : g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms,
     214                 :             :                                              GSequenceIter      *iter,
     215                 :             :                                              const gchar        *child)
     216                 :             : {
     217                 :             :   /* must remove the hash entry first -- its key is owned by the data
     218                 :             :    * which will be freed when removing the sequence iter
     219                 :             :    */
     220                 :          52 :   g_hash_table_remove (fms->pending_changes_table, child);
     221                 :          52 :   g_sequence_remove (iter);
     222                 :          52 : }
     223                 :             : 
     224                 :             : static void
     225                 :        1057 : g_file_monitor_source_queue_event (GFileMonitorSource *fms,
     226                 :             :                                    GFileMonitorEvent   event_type,
     227                 :             :                                    const gchar        *child,
     228                 :             :                                    GFile              *other)
     229                 :             : {
     230                 :             :   QueuedEvent *event;
     231                 :             : 
     232                 :        1057 :   event = g_slice_new (QueuedEvent);
     233                 :        1057 :   event->event_type = event_type;
     234                 :        1057 :   if (child != NULL && fms->dirname != NULL)
     235                 :         770 :     event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child);
     236                 :         287 :   else if (child != NULL)
     237                 :             :     {
     238                 :           2 :       gchar *dirname = g_path_get_dirname (fms->filename);
     239                 :           2 :       event->child = g_local_file_new_from_dirname_and_basename (dirname, child);
     240                 :           2 :       g_free (dirname);
     241                 :             :     }
     242                 :         285 :   else if (fms->dirname)
     243                 :         283 :     event->child = _g_local_file_new (fms->dirname);
     244                 :           2 :   else if (fms->filename)
     245                 :           2 :     event->child = _g_local_file_new (fms->filename);
     246                 :        1057 :   event->other = other;
     247                 :        1057 :   if (other)
     248                 :           4 :     g_object_ref (other);
     249                 :             : 
     250                 :        1057 :   g_queue_push_tail (&fms->event_queue, event);
     251                 :        1057 : }
     252                 :             : 
     253                 :             : static gboolean
     254                 :          67 : g_file_monitor_source_file_changed (GFileMonitorSource *fms,
     255                 :             :                                     const gchar        *child,
     256                 :             :                                     gint64              now)
     257                 :             : {
     258                 :             :   GSequenceIter *pending;
     259                 :             :   gboolean interesting;
     260                 :             : 
     261                 :          67 :   pending = g_file_monitor_source_find_pending_change (fms, child);
     262                 :             : 
     263                 :             :   /* If there is no pending change, emit one and create a record,
     264                 :             :    * else: just mark the existing record as dirty.
     265                 :             :    */
     266                 :          67 :   if (!pending)
     267                 :             :     {
     268                 :          11 :       g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
     269                 :          11 :       g_file_monitor_source_add_pending_change (fms, child, now);
     270                 :          11 :       interesting = TRUE;
     271                 :             :     }
     272                 :             :   else
     273                 :          56 :     interesting = g_file_monitor_source_set_pending_change_dirty (fms, pending);
     274                 :             : 
     275                 :          67 :   g_file_monitor_source_update_ready_time (fms);
     276                 :             : 
     277                 :          67 :   return interesting;
     278                 :             : }
     279                 :             : 
     280                 :             : static void
     281                 :        1023 : g_file_monitor_source_file_changes_done (GFileMonitorSource *fms,
     282                 :             :                                          const gchar        *child)
     283                 :             : {
     284                 :             :   GSequenceIter *pending;
     285                 :             : 
     286                 :        1023 :   pending = g_file_monitor_source_find_pending_change (fms, child);
     287                 :        1023 :   if (pending)
     288                 :             :     {
     289                 :             :       /* If it is dirty, make sure we push out the last CHANGED event */
     290                 :          52 :       if (g_file_monitor_source_get_pending_change_dirty (fms, pending))
     291                 :          23 :         g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
     292                 :             : 
     293                 :          52 :       g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
     294                 :          52 :       g_file_monitor_source_remove_pending_change (fms, pending, child);
     295                 :             :     }
     296                 :        1023 : }
     297                 :             : 
     298                 :             : static void
     299                 :          76 : g_file_monitor_source_file_created (GFileMonitorSource *fms,
     300                 :             :                                     const gchar        *child,
     301                 :             :                                     gint64              event_time)
     302                 :             : {
     303                 :             :   /* Unlikely, but if we have pending changes for this filename, make
     304                 :             :    * sure we flush those out first, before creating the new ones.
     305                 :             :    */
     306                 :          76 :   g_file_monitor_source_file_changes_done (fms, child);
     307                 :             : 
     308                 :             :   /* Emit CREATE and add a pending changes record */
     309                 :          76 :   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
     310                 :          76 :   g_file_monitor_source_add_pending_change (fms, child, event_time);
     311                 :          76 : }
     312                 :             : 
     313                 :             : static void
     314                 :         825 : g_file_monitor_source_send_event (GFileMonitorSource *fms,
     315                 :             :                                   GFileMonitorEvent   event_type,
     316                 :             :                                   const gchar        *child,
     317                 :             :                                   GFile              *other)
     318                 :             : {
     319                 :             :   /* always flush any pending changes before we queue a new event */
     320                 :         825 :   g_file_monitor_source_file_changes_done (fms, child);
     321                 :         825 :   g_file_monitor_source_queue_event (fms, event_type, child, other);
     322                 :         825 : }
     323                 :             : 
     324                 :             : static void
     325                 :          34 : g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms,
     326                 :             :                                               const gchar        *child)
     327                 :             : {
     328                 :          34 :   g_file_monitor_source_file_changes_done (fms, child);
     329                 :          34 :   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
     330                 :          34 :   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
     331                 :          34 : }
     332                 :             : 
     333                 :             : #ifndef G_DISABLE_ASSERT
     334                 :             : static gboolean
     335                 :         804 : is_basename (const gchar *name)
     336                 :             : {
     337                 :         804 :   if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0'))
     338                 :           0 :     return FALSE;
     339                 :             : 
     340                 :         804 :   return !strchr (name, '/');
     341                 :             : }
     342                 :             : #endif  /* !G_DISABLE_ASSERT */
     343                 :             : 
     344                 :             : gboolean
     345                 :        1054 : g_file_monitor_source_handle_event (GFileMonitorSource *fms,
     346                 :             :                                     GFileMonitorEvent   event_type,
     347                 :             :                                     const gchar        *child,
     348                 :             :                                     const gchar        *rename_to,
     349                 :             :                                     GFile              *other,
     350                 :             :                                     gint64              event_time)
     351                 :             : {
     352                 :        1054 :   gboolean interesting = TRUE;
     353                 :             : 
     354                 :        1054 :   g_assert (!child || is_basename (child));
     355                 :        1054 :   g_assert (!rename_to || is_basename (rename_to));
     356                 :             : 
     357                 :        1054 :   if (fms->basename && (!child || !g_str_equal (child, fms->basename))
     358                 :          14 :                     && (!rename_to || !g_str_equal (rename_to, fms->basename)))
     359                 :           0 :     return TRUE;
     360                 :             : 
     361                 :        1054 :   g_mutex_lock (&fms->lock);
     362                 :             : 
     363                 :             :   /* NOTE:
     364                 :             :    *
     365                 :             :    * We process events even if the file monitor has already been disposed.
     366                 :             :    * The reason is that we must not take a reference to the instance here as
     367                 :             :    * destroying it from the event handling thread will lead to a deadlock when
     368                 :             :    * taking the lock in _ih_sub_cancel.
     369                 :             :    *
     370                 :             :    * This results in seemingly-unbounded growth of the `event_queue` with the
     371                 :             :    * calls to `g_file_monitor_source_queue_event()`. However, each of those sets
     372                 :             :    * the ready time on the #GSource, which means that it will be dispatched in
     373                 :             :    * a subsequent iteration of the #GMainContext it’s attached to. At that
     374                 :             :    * point, `g_file_monitor_source_dispatch()` will return %FALSE, and this will
     375                 :             :    * trigger finalisation of the source. That will clear the `event_queue`.
     376                 :             :    *
     377                 :             :    * If the source is no longer attached, this will return early to prevent
     378                 :             :    * unbounded queueing.
     379                 :             :    */
     380                 :        1054 :   if (g_source_is_destroyed ((GSource *) fms))
     381                 :             :     {
     382                 :           0 :       g_mutex_unlock (&fms->lock);
     383                 :           0 :       return TRUE;
     384                 :             :     }
     385                 :             : 
     386                 :        1054 :   switch (event_type)
     387                 :             :     {
     388                 :          76 :     case G_FILE_MONITOR_EVENT_CREATED:
     389                 :          76 :       g_assert (!other && !rename_to);
     390                 :          76 :       g_file_monitor_source_file_created (fms, child, event_time);
     391                 :          76 :       break;
     392                 :             : 
     393                 :          67 :     case G_FILE_MONITOR_EVENT_CHANGED:
     394                 :          67 :       g_assert (!other && !rename_to);
     395                 :          67 :       interesting = g_file_monitor_source_file_changed (fms, child, event_time);
     396                 :          67 :       break;
     397                 :             : 
     398                 :          85 :     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
     399                 :          85 :       g_assert (!other && !rename_to);
     400                 :          85 :       g_file_monitor_source_file_changes_done (fms, child);
     401                 :          85 :       break;
     402                 :             : 
     403                 :           2 :     case G_FILE_MONITOR_EVENT_MOVED_IN:
     404                 :           2 :       g_assert (!rename_to);
     405                 :           2 :       if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
     406                 :           1 :         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other);
     407                 :             :       else
     408                 :           1 :         g_file_monitor_source_send_synthetic_created (fms, child);
     409                 :           2 :       break;
     410                 :             : 
     411                 :           2 :     case G_FILE_MONITOR_EVENT_MOVED_OUT:
     412                 :           2 :       g_assert (!rename_to);
     413                 :           2 :       if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
     414                 :           2 :         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other);
     415                 :           0 :       else if (other && (fms->flags & G_FILE_MONITOR_SEND_MOVED))
     416                 :           0 :         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other);
     417                 :             :       else
     418                 :           0 :         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
     419                 :           2 :       break;
     420                 :             : 
     421                 :          36 :     case G_FILE_MONITOR_EVENT_RENAMED:
     422                 :          36 :       g_assert (!other && rename_to);
     423                 :          36 :       if (fms->flags & (G_FILE_MONITOR_WATCH_MOVES | G_FILE_MONITOR_SEND_MOVED))
     424                 :             :         {
     425                 :             :           GFile *other_file;
     426                 :             :           const gchar *dirname;
     427                 :           3 :           gchar *allocated_dirname = NULL;
     428                 :             :           GFileMonitorEvent event;
     429                 :             : 
     430                 :           3 :           event = (fms->flags & G_FILE_MONITOR_WATCH_MOVES) ? G_FILE_MONITOR_EVENT_RENAMED : G_FILE_MONITOR_EVENT_MOVED;
     431                 :             : 
     432                 :           3 :           if (fms->dirname != NULL)
     433                 :           2 :             dirname = fms->dirname;
     434                 :             :           else
     435                 :             :             {
     436                 :           1 :               allocated_dirname = g_path_get_dirname (fms->filename);
     437                 :           1 :               dirname = allocated_dirname;
     438                 :             :             }
     439                 :             : 
     440                 :           3 :           other_file = g_local_file_new_from_dirname_and_basename (dirname, rename_to);
     441                 :           3 :           g_file_monitor_source_file_changes_done (fms, rename_to);
     442                 :           3 :           g_file_monitor_source_send_event (fms, event, child, other_file);
     443                 :             : 
     444                 :           3 :           g_object_unref (other_file);
     445                 :           3 :           g_free (allocated_dirname);
     446                 :             :         }
     447                 :             :       else
     448                 :             :         {
     449                 :          33 :           g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
     450                 :          33 :           g_file_monitor_source_send_synthetic_created (fms, rename_to);
     451                 :             :         }
     452                 :          36 :       break;
     453                 :             : 
     454                 :         786 :     case G_FILE_MONITOR_EVENT_DELETED:
     455                 :             :     case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
     456                 :             :     case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
     457                 :             :     case G_FILE_MONITOR_EVENT_UNMOUNTED:
     458                 :         786 :       g_assert (!other && !rename_to);
     459                 :         786 :       g_file_monitor_source_send_event (fms, event_type, child, NULL);
     460                 :         786 :       break;
     461                 :             : 
     462                 :           0 :     case G_FILE_MONITOR_EVENT_MOVED:
     463                 :             :       /* was never available in this API */
     464                 :             :     default:
     465                 :             :       g_assert_not_reached ();
     466                 :             :     }
     467                 :             : 
     468                 :        1054 :   g_file_monitor_source_update_ready_time (fms);
     469                 :             : 
     470                 :        1054 :   g_mutex_unlock (&fms->lock);
     471                 :             : 
     472                 :        1054 :   return interesting;
     473                 :             : }
     474                 :             : 
     475                 :             : static gint64
     476                 :           0 : g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms)
     477                 :             : {
     478                 :             :   gint64 rate_limit;
     479                 :             : 
     480                 :           0 :   g_mutex_lock (&fms->lock);
     481                 :           0 :   rate_limit = fms->rate_limit;
     482                 :           0 :   g_mutex_unlock (&fms->lock);
     483                 :             : 
     484                 :           0 :   return rate_limit;
     485                 :             : }
     486                 :             : 
     487                 :             : static gboolean
     488                 :          12 : g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms,
     489                 :             :                                       gint64              rate_limit)
     490                 :             : {
     491                 :             :   gboolean changed;
     492                 :             : 
     493                 :          12 :   g_mutex_lock (&fms->lock);
     494                 :             : 
     495                 :          12 :   if (rate_limit != fms->rate_limit)
     496                 :             :     {
     497                 :          12 :       fms->rate_limit = rate_limit;
     498                 :             : 
     499                 :          12 :       g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms);
     500                 :          12 :       g_file_monitor_source_update_ready_time (fms);
     501                 :             : 
     502                 :          12 :       changed = TRUE;
     503                 :             :     }
     504                 :             :   else
     505                 :           0 :     changed = FALSE;
     506                 :             : 
     507                 :          12 :   g_mutex_unlock (&fms->lock);
     508                 :             : 
     509                 :          12 :   return changed;
     510                 :             : }
     511                 :             : 
     512                 :             : static gboolean
     513                 :         486 : g_file_monitor_source_dispatch (GSource     *source,
     514                 :             :                                 GSourceFunc  callback,
     515                 :             :                                 gpointer     user_data)
     516                 :             : {
     517                 :         486 :   GFileMonitorSource *fms = (GFileMonitorSource *) source;
     518                 :             :   QueuedEvent *event;
     519                 :             :   GQueue event_queue;
     520                 :             :   gint64 now;
     521                 :         486 :   GFileMonitor *instance = NULL;
     522                 :             : 
     523                 :             :   /* make sure the monitor still exists */
     524                 :         486 :   instance = g_weak_ref_get (&fms->instance_ref);
     525                 :         486 :   if (instance == NULL)
     526                 :           1 :     return FALSE;
     527                 :             : 
     528                 :         485 :   now = g_source_get_time (source);
     529                 :             : 
     530                 :             :   /* Acquire the lock once and grab all events in one go, handling the
     531                 :             :    * queued events first.  This avoids strange possibilities in cases of
     532                 :             :    * long delays, such as CHANGED events coming before CREATED events.
     533                 :             :    *
     534                 :             :    * We do this by converting the applicable pending changes into queued
     535                 :             :    * events (after the ones already queued) and then stealing the entire
     536                 :             :    * event queue in one go.
     537                 :             :    */
     538                 :         485 :   g_mutex_lock (&fms->lock);
     539                 :             : 
     540                 :             :   /* Create events for any pending changes that are due to fire */
     541                 :         487 :   while (!g_sequence_is_empty (fms->pending_changes))
     542                 :             :     {
     543                 :          47 :       GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes);
     544                 :          47 :       PendingChange *pending = g_sequence_get (iter);
     545                 :             : 
     546                 :             :       /* We've gotten to a pending change that's not ready.  Stop. */
     547                 :          47 :       if (pending_change_get_ready_time (pending, fms) > now)
     548                 :          45 :         break;
     549                 :             : 
     550                 :           2 :       if (pending->dirty)
     551                 :             :         {
     552                 :             :           /* It's time to send another CHANGED and update the record */
     553                 :           2 :           g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL);
     554                 :           2 :           pending->last_emission = now;
     555                 :           2 :           pending->dirty = FALSE;
     556                 :             : 
     557                 :           2 :           g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
     558                 :             :         }
     559                 :             :       else
     560                 :             :         {
     561                 :             :           /* It's time to send CHANGES_DONE and remove the pending record */
     562                 :           0 :           g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL);
     563                 :           0 :           g_file_monitor_source_remove_pending_change (fms, iter, pending->child);
     564                 :             :         }
     565                 :             :     }
     566                 :             : 
     567                 :             :   /* Steal the queue */
     568                 :         485 :   memcpy (&event_queue, &fms->event_queue, sizeof event_queue);
     569                 :         485 :   memset (&fms->event_queue, 0, sizeof fms->event_queue);
     570                 :             : 
     571                 :         485 :   g_file_monitor_source_update_ready_time (fms);
     572                 :             : 
     573                 :         485 :   g_mutex_unlock (&fms->lock);
     574                 :         485 :   g_clear_object (&instance);
     575                 :             : 
     576                 :             :   /* We now have our list of events to deliver */
     577                 :        1456 :   while ((event = g_queue_pop_head (&event_queue)))
     578                 :             :     {
     579                 :             :       /* an event handler could destroy 'instance', so check each time */
     580                 :         971 :       instance = g_weak_ref_get (&fms->instance_ref);
     581                 :         971 :       if (instance != NULL)
     582                 :         548 :         g_file_monitor_emit_event (instance, event->child, event->other, event->event_type);
     583                 :             : 
     584                 :         971 :       g_clear_object (&instance);
     585                 :         971 :       queued_event_free (event);
     586                 :             :     }
     587                 :             : 
     588                 :         485 :   return TRUE;
     589                 :             : }
     590                 :             : 
     591                 :             : static void
     592                 :         484 : g_file_monitor_source_dispose (GFileMonitorSource *fms)
     593                 :             : {
     594                 :             :   GHashTableIter iter;
     595                 :             :   gpointer seqiter;
     596                 :             :   QueuedEvent *event;
     597                 :             : 
     598                 :         484 :   g_mutex_lock (&fms->lock);
     599                 :             : 
     600                 :         484 :   g_hash_table_iter_init (&iter, fms->pending_changes_table);
     601                 :         519 :   while (g_hash_table_iter_next (&iter, NULL, &seqiter))
     602                 :             :     {
     603                 :          35 :       g_hash_table_iter_remove (&iter);
     604                 :          35 :       g_sequence_remove (seqiter);
     605                 :             :     }
     606                 :             : 
     607                 :         570 :   while ((event = g_queue_pop_head (&fms->event_queue)))
     608                 :          86 :     queued_event_free (event);
     609                 :             : 
     610                 :         484 :   g_assert (g_sequence_is_empty (fms->pending_changes));
     611                 :         484 :   g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
     612                 :         484 :   g_assert (fms->event_queue.length == 0);
     613                 :         484 :   g_weak_ref_set (&fms->instance_ref, NULL);
     614                 :             : 
     615                 :         484 :   g_file_monitor_source_update_ready_time (fms);
     616                 :             : 
     617                 :         484 :   g_source_destroy ((GSource *) fms);
     618                 :             : 
     619                 :         484 :   g_mutex_unlock (&fms->lock);
     620                 :         484 : }
     621                 :             : 
     622                 :             : static void
     623                 :         484 : g_file_monitor_source_finalize (GSource *source)
     624                 :             : {
     625                 :         484 :   GFileMonitorSource *fms = (GFileMonitorSource *) source;
     626                 :             : 
     627                 :             :   /* should already have been cleared in dispose of the monitor */
     628                 :         484 :   g_assert (g_weak_ref_get (&fms->instance_ref) == NULL);
     629                 :         484 :   g_weak_ref_clear (&fms->instance_ref);
     630                 :             : 
     631                 :         484 :   g_assert (g_sequence_is_empty (fms->pending_changes));
     632                 :         484 :   g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
     633                 :         484 :   g_assert (fms->event_queue.length == 0);
     634                 :             : 
     635                 :         484 :   g_hash_table_unref (fms->pending_changes_table);
     636                 :         484 :   g_sequence_free (fms->pending_changes);
     637                 :             : 
     638                 :         484 :   g_free (fms->dirname);
     639                 :         484 :   g_free (fms->basename);
     640                 :         484 :   g_free (fms->filename);
     641                 :             : 
     642                 :         484 :   g_mutex_clear (&fms->lock);
     643                 :         484 : }
     644                 :             : 
     645                 :             : static guint
     646                 :        1229 : str_hash0 (gconstpointer str)
     647                 :             : {
     648                 :        1229 :   return str ? g_str_hash (str) : 0;
     649                 :             : }
     650                 :             : 
     651                 :             : static gboolean
     652                 :         160 : str_equal0 (gconstpointer a,
     653                 :             :             gconstpointer b)
     654                 :             : {
     655                 :         160 :   return g_strcmp0 (a, b) == 0;
     656                 :             : }
     657                 :             : 
     658                 :             : static GFileMonitorSource *
     659                 :         676 : g_file_monitor_source_new (gpointer           instance,
     660                 :             :                            const gchar       *filename,
     661                 :             :                            gboolean           is_directory,
     662                 :             :                            GFileMonitorFlags  flags)
     663                 :             : {
     664                 :             :   static GSourceFuncs source_funcs = {
     665                 :             :     NULL, NULL,
     666                 :             :     g_file_monitor_source_dispatch,
     667                 :             :     g_file_monitor_source_finalize,
     668                 :             :     NULL, NULL
     669                 :             :   };
     670                 :             :   GFileMonitorSource *fms;
     671                 :             :   GSource *source;
     672                 :             : 
     673                 :         676 :   source = g_source_new (&source_funcs, sizeof (GFileMonitorSource));
     674                 :         676 :   fms = (GFileMonitorSource *) source;
     675                 :             : 
     676                 :         676 :   g_source_set_static_name (source, "GFileMonitorSource");
     677                 :             : 
     678                 :         676 :   g_mutex_init (&fms->lock);
     679                 :         676 :   g_weak_ref_init (&fms->instance_ref, instance);
     680                 :         676 :   fms->pending_changes = g_sequence_new (pending_change_free);
     681                 :         676 :   fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0);
     682                 :         676 :   fms->rate_limit = DEFAULT_RATE_LIMIT;
     683                 :         676 :   fms->flags = flags;
     684                 :             : 
     685                 :         676 :   if (is_directory)
     686                 :             :     {
     687                 :         560 :       fms->dirname = g_strdup (filename);
     688                 :         560 :       fms->basename = NULL;
     689                 :         560 :       fms->filename = NULL;
     690                 :             :     }
     691                 :         116 :   else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS)
     692                 :             :     {
     693                 :           1 :       fms->dirname = NULL;
     694                 :           1 :       fms->basename = NULL;
     695                 :           1 :       fms->filename = g_strdup (filename);
     696                 :             :     }
     697                 :             :   else
     698                 :             :     {
     699                 :         115 :       fms->dirname = g_path_get_dirname (filename);
     700                 :         115 :       fms->basename = g_path_get_basename (filename);
     701                 :         115 :       fms->filename = NULL;
     702                 :             :     }
     703                 :             : 
     704                 :         676 :   return fms;
     705                 :             : }
     706                 :             : 
     707                 :        2258 : G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR)
     708                 :             : 
     709                 :             : enum {
     710                 :             :   PROP_0,
     711                 :             :   PROP_RATE_LIMIT,
     712                 :             : };
     713                 :             : 
     714                 :             : static void
     715                 :           0 : g_local_file_monitor_get_property (GObject *object, guint prop_id,
     716                 :             :                                    GValue *value, GParamSpec *pspec)
     717                 :             : {
     718                 :           0 :   GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
     719                 :             :   gint64 rate_limit;
     720                 :             : 
     721                 :           0 :   g_assert (prop_id == PROP_RATE_LIMIT);
     722                 :             : 
     723                 :           0 :   rate_limit = g_file_monitor_source_get_rate_limit (monitor->source);
     724                 :           0 :   rate_limit /= G_TIME_SPAN_MILLISECOND;
     725                 :             : 
     726                 :           0 :   g_value_set_int (value, rate_limit);
     727                 :           0 : }
     728                 :             : 
     729                 :             : static void
     730                 :          12 : g_local_file_monitor_set_property (GObject *object, guint prop_id,
     731                 :             :                                    const GValue *value, GParamSpec *pspec)
     732                 :             : {
     733                 :          12 :   GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
     734                 :             :   gint64 rate_limit;
     735                 :             : 
     736                 :          12 :   g_assert (prop_id == PROP_RATE_LIMIT);
     737                 :             : 
     738                 :          12 :   rate_limit = g_value_get_int (value);
     739                 :          12 :   rate_limit *= G_TIME_SPAN_MILLISECOND;
     740                 :             : 
     741                 :          12 :   if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit))
     742                 :          12 :     g_object_notify (object, "rate-limit");
     743                 :          12 : }
     744                 :             : 
     745                 :             : #ifndef G_OS_WIN32
     746                 :             : static void
     747                 :           0 : g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor,
     748                 :             :                                      gpointer           user_data)
     749                 :             : {
     750                 :           0 :   GLocalFileMonitor *local_monitor = user_data;
     751                 :             :   GUnixMountEntry *mount;
     752                 :             :   gboolean is_mounted;
     753                 :             :   GFile *file;
     754                 :             : 
     755                 :             :   /* Emulate unmount detection */
     756                 :           0 :   mount = g_unix_mount_entry_at (local_monitor->source->dirname, NULL);
     757                 :             : 
     758                 :           0 :   is_mounted = mount != NULL;
     759                 :             : 
     760                 :           0 :   if (mount)
     761                 :           0 :     g_unix_mount_entry_free (mount);
     762                 :             : 
     763                 :           0 :   if (local_monitor->was_mounted != is_mounted)
     764                 :             :     {
     765                 :           0 :       if (local_monitor->was_mounted && !is_mounted)
     766                 :             :         {
     767                 :           0 :           file = g_file_new_for_path (local_monitor->source->dirname);
     768                 :           0 :           g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED);
     769                 :           0 :           g_object_unref (file);
     770                 :             :         }
     771                 :           0 :       local_monitor->was_mounted = is_mounted;
     772                 :             :     }
     773                 :           0 : }
     774                 :             : #endif
     775                 :             : 
     776                 :             : static void
     777                 :         676 : g_local_file_monitor_start (GLocalFileMonitor *local_monitor,
     778                 :             :                             const gchar       *filename,
     779                 :             :                             gboolean           is_directory,
     780                 :             :                             GFileMonitorFlags  flags,
     781                 :             :                             GMainContext      *context)
     782                 :             : {
     783                 :         676 :   GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor);
     784                 :             :   GFileMonitorSource *source;
     785                 :             : 
     786                 :         676 :   g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor));
     787                 :             : 
     788                 :         676 :   g_assert (!local_monitor->source);
     789                 :             : 
     790                 :         676 :   source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags);
     791                 :         676 :   local_monitor->source = source; /* owns the ref */
     792                 :             : 
     793                 :         676 :   if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS))
     794                 :             :     {
     795                 :             : #ifdef G_OS_WIN32
     796                 :             :       /*claim everything was mounted */
     797                 :             :       local_monitor->was_mounted = TRUE;
     798                 :             : #else
     799                 :             :       GUnixMountEntry *mount;
     800                 :             : 
     801                 :             :       /* Emulate unmount detection */
     802                 :             : 
     803                 :           0 :       mount = g_unix_mount_entry_at (local_monitor->source->dirname, NULL);
     804                 :             : 
     805                 :           0 :       local_monitor->was_mounted = mount != NULL;
     806                 :             : 
     807                 :           0 :       if (mount)
     808                 :           0 :         g_unix_mount_entry_free (mount);
     809                 :             : 
     810                 :           0 :       local_monitor->mount_monitor = g_unix_mount_monitor_get ();
     811                 :           0 :       g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed",
     812                 :             :                                G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor,
     813                 :             :                                G_CONNECT_DEFAULT);
     814                 :             : #endif
     815                 :             :     }
     816                 :             : 
     817                 :         676 :   g_source_attach ((GSource *) source, context);
     818                 :             : 
     819                 :         676 :   G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor,
     820                 :         676 :                                                          source->dirname, source->basename, source->filename,
     821                 :             :                                                          source);
     822                 :             : }
     823                 :             : 
     824                 :             : static void
     825                 :         484 : g_local_file_monitor_dispose (GObject *object)
     826                 :             : {
     827                 :         484 :   GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
     828                 :             : 
     829                 :         484 :   g_file_monitor_source_dispose (local_monitor->source);
     830                 :             : 
     831                 :         484 :   G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object);
     832                 :         484 : }
     833                 :             : 
     834                 :             : static void
     835                 :         484 : g_local_file_monitor_finalize (GObject *object)
     836                 :             : {
     837                 :         484 :   GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
     838                 :             : 
     839                 :         484 :   g_source_unref ((GSource *) local_monitor->source);
     840                 :             : 
     841                 :         484 :   G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object);
     842                 :         484 : }
     843                 :             : 
     844                 :             : static void
     845                 :         676 : g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
     846                 :             : {
     847                 :         676 : }
     848                 :             : 
     849                 :          57 : static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class)
     850                 :             : {
     851                 :          57 :   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
     852                 :             : 
     853                 :          57 :   gobject_class->get_property = g_local_file_monitor_get_property;
     854                 :          57 :   gobject_class->set_property = g_local_file_monitor_set_property;
     855                 :          57 :   gobject_class->dispose = g_local_file_monitor_dispose;
     856                 :          57 :   gobject_class->finalize = g_local_file_monitor_finalize;
     857                 :             : 
     858                 :          57 :   g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit");
     859                 :          57 : }
     860                 :             : 
     861                 :             : static GLocalFileMonitor *
     862                 :         676 : g_local_file_monitor_new (gboolean   is_remote_fs,
     863                 :             :                           gboolean   is_directory,
     864                 :             :                           GError   **error)
     865                 :             : {
     866                 :         676 :   GType type = G_TYPE_INVALID;
     867                 :             : 
     868                 :         676 :   if (is_remote_fs)
     869                 :           0 :     type = _g_io_module_get_default_type (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
     870                 :             :                                           "GIO_USE_FILE_MONITOR",
     871                 :             :                                           G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
     872                 :             : 
     873                 :             :   /* Fallback rather to poll file monitor for remote files, see gfile.c. */
     874                 :         676 :   if (type == G_TYPE_INVALID && (!is_remote_fs || is_directory))
     875                 :         676 :     type = _g_io_module_get_default_type (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
     876                 :             :                                           "GIO_USE_FILE_MONITOR",
     877                 :             :                                           G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
     878                 :             : 
     879                 :         676 :   if (type == G_TYPE_INVALID)
     880                 :             :     {
     881                 :           0 :       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     882                 :             :                            _("Unable to find default local file monitor type"));
     883                 :           0 :       return NULL;
     884                 :             :     }
     885                 :             : 
     886                 :         676 :   return g_object_new (type, NULL);
     887                 :             : }
     888                 :             : 
     889                 :             : GFileMonitor *
     890                 :         126 : g_local_file_monitor_new_for_path (const gchar        *pathname,
     891                 :             :                                    gboolean            is_directory,
     892                 :             :                                    GFileMonitorFlags   flags,
     893                 :             :                                    GError            **error)
     894                 :             : {
     895                 :             :   GLocalFileMonitor *monitor;
     896                 :             :   gboolean is_remote_fs;
     897                 :             : 
     898                 :         126 :   is_remote_fs = g_local_file_is_nfs_home (pathname);
     899                 :             : 
     900                 :         126 :   monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
     901                 :             : 
     902                 :         126 :   if (monitor)
     903                 :         126 :     g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ());
     904                 :             : 
     905                 :         126 :   return G_FILE_MONITOR (monitor);
     906                 :             : }
     907                 :             : 
     908                 :             : GFileMonitor *
     909                 :         550 : g_local_file_monitor_new_in_worker (const gchar           *pathname,
     910                 :             :                                     gboolean               is_directory,
     911                 :             :                                     GFileMonitorFlags      flags,
     912                 :             :                                     GFileMonitorCallback   callback,
     913                 :             :                                     gpointer               user_data,
     914                 :             :                                     GClosureNotify         destroy_user_data,
     915                 :             :                                     GError               **error)
     916                 :             : {
     917                 :             :   GLocalFileMonitor *monitor;
     918                 :             :   gboolean is_remote_fs;
     919                 :             : 
     920                 :         550 :   is_remote_fs = g_local_file_is_nfs_home (pathname);
     921                 :             : 
     922                 :         550 :   monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
     923                 :             : 
     924                 :         550 :   if (monitor)
     925                 :             :     {
     926                 :         550 :       if (callback)
     927                 :         550 :         g_signal_connect_data (monitor, "changed", G_CALLBACK (callback),
     928                 :             :                                user_data, destroy_user_data, G_CONNECT_DEFAULT);
     929                 :             : 
     930                 :         550 :       g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ());
     931                 :             :     }
     932                 :             : 
     933                 :         550 :   return G_FILE_MONITOR (monitor);
     934                 :             : }
        

Generated by: LCOV version 2.0-1