| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | /* GIO - GLib Input, Output and Streaming Library
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |  * Copyright (C) 2006-2007 Red Hat, Inc. | 
					
						
							|  |  |  |  |  * | 
					
						
							| 
									
										
										
										
											2022-05-18 09:12:45 +01:00
										 |  |  |  |  * SPDX-License-Identifier: LGPL-2.1-or-later | 
					
						
							|  |  |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |  * This library is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  |  * modify it under the terms of the GNU Lesser General Public | 
					
						
							|  |  |  |  |  * License as published by the Free Software Foundation; either | 
					
						
							| 
									
										
										
										
											2017-05-27 18:21:30 +02:00
										 |  |  |  |  * version 2.1 of the License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |  * | 
					
						
							|  |  |  |  |  * This library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  |  * Lesser General Public License for more details. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * You should have received a copy of the GNU Lesser General | 
					
						
							| 
									
										
										
										
											2014-01-23 12:58:29 +01:00
										 |  |  |  |  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |  * | 
					
						
							|  |  |  |  |  * Author: Alexander Larsson <alexl@redhat.com> | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-22 15:10:51 +00:00
										 |  |  |  | #include "config.h"
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-18 15:43:45 +01:00
										 |  |  |  | #include "gioenumtypes.h"
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | #include "glocalfilemonitor.h"
 | 
					
						
							| 
									
										
										
										
											2007-12-05 11:10:21 +00:00
										 |  |  |  | #include "giomodule-priv.h"
 | 
					
						
							| 
									
										
										
										
											2008-07-01 06:32:35 +00:00
										 |  |  |  | #include "gioerror.h"
 | 
					
						
							| 
									
										
										
										
											2008-01-07 05:18:17 +00:00
										 |  |  |  | #include "glibintl.h"
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | #include "glocalfile.h"
 | 
					
						
							|  |  |  |  | #include "glib-private.h"
 | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | #include <string.h>
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | #define DEFAULT_RATE_LIMIT                           800 * G_TIME_SPAN_MILLISECOND
 | 
					
						
							|  |  |  |  | #define VIRTUAL_CHANGES_DONE_DELAY                     2 * G_TIME_SPAN_SECOND
 | 
					
						
							| 
									
										
										
										
											2007-11-28 12:39:07 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | /* GFileMonitorSource is a GSource responsible for emitting the changed
 | 
					
						
							|  |  |  |  |  * signals in the owner-context of the GFileMonitor. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * It contains functionality for cross-thread queuing of events.  It | 
					
						
							|  |  |  |  |  * also handles merging of CHANGED events and emission of CHANGES_DONE | 
					
						
							|  |  |  |  |  * events. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * We use the "priv" pointer in the external struct to store it. | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | struct _GFileMonitorSource { | 
					
						
							|  |  |  |  |   GSource       source; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   GMutex        lock; | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   GWeakRef      instance_ref; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   GFileMonitorFlags flags; | 
					
						
							|  |  |  |  |   gchar        *dirname; | 
					
						
							|  |  |  |  |   gchar        *basename; | 
					
						
							|  |  |  |  |   gchar        *filename; | 
					
						
							|  |  |  |  |   GSequence    *pending_changes; /* sorted by ready time */ | 
					
						
							|  |  |  |  |   GHashTable   *pending_changes_table; | 
					
						
							|  |  |  |  |   GQueue        event_queue; | 
					
						
							|  |  |  |  |   gint64        rate_limit; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | /* PendingChange is a struct to keep track of a file that needs to have
 | 
					
						
							|  |  |  |  |  * (at least) a CHANGES_DONE_HINT event sent for it in the near future. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * If 'dirty' is TRUE then a CHANGED event also needs to be sent. | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * last_emission is the last time a CHANGED event was emitted.  It is | 
					
						
							|  |  |  |  |  * used to calculate the time to send the next event. | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | typedef struct { | 
					
						
							|  |  |  |  |   gchar    *child; | 
					
						
							|  |  |  |  |   guint64   last_emission : 63; | 
					
						
							|  |  |  |  |   guint64   dirty         :  1; | 
					
						
							|  |  |  |  | } PendingChange; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* QueuedEvent is a signal that will be sent immediately, as soon as the
 | 
					
						
							|  |  |  |  |  * source gets a chance to dispatch.  The existence of any queued event | 
					
						
							|  |  |  |  |  * implies that the source is ready now. | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | typedef struct | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFileMonitorEvent event_type; | 
					
						
							|  |  |  |  |   GFile *child; | 
					
						
							|  |  |  |  |   GFile *other; | 
					
						
							|  |  |  |  | } QueuedEvent; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gint64 | 
					
						
							|  |  |  |  | pending_change_get_ready_time (const PendingChange *change, | 
					
						
							|  |  |  |  |                                GFileMonitorSource  *fms) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   if (change->dirty) | 
					
						
							|  |  |  |  |     return change->last_emission + fms->rate_limit; | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static int | 
					
						
							|  |  |  |  | pending_change_compare_ready_time (gconstpointer a_p, | 
					
						
							|  |  |  |  |                                    gconstpointer b_p, | 
					
						
							|  |  |  |  |                                    gpointer      user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFileMonitorSource *fms = user_data; | 
					
						
							|  |  |  |  |   const PendingChange *a = a_p; | 
					
						
							|  |  |  |  |   const PendingChange *b = b_p; | 
					
						
							|  |  |  |  |   gint64 ready_time_a; | 
					
						
							|  |  |  |  |   gint64 ready_time_b; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ready_time_a = pending_change_get_ready_time (a, fms); | 
					
						
							|  |  |  |  |   ready_time_b = pending_change_get_ready_time (b, fms); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (ready_time_a < ready_time_b) | 
					
						
							|  |  |  |  |     return -1; | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     return ready_time_a > ready_time_b; | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | pending_change_free (gpointer data) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   PendingChange *change = data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_free (change->child); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_slice_free (PendingChange, change); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | queued_event_free (QueuedEvent *event) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_object_unref (event->child); | 
					
						
							|  |  |  |  |   if (event->other) | 
					
						
							|  |  |  |  |     g_object_unref (event->other); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_slice_free (QueuedEvent, event); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gint64 | 
					
						
							|  |  |  |  | g_file_monitor_source_get_ready_time (GFileMonitorSource *fms) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GSequenceIter *iter; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (fms->event_queue.length) | 
					
						
							|  |  |  |  |     return 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   iter = g_sequence_get_begin_iter (fms->pending_changes); | 
					
						
							|  |  |  |  |   if (g_sequence_iter_is_end (iter)) | 
					
						
							|  |  |  |  |     return -1; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return pending_change_get_ready_time (g_sequence_get (iter), fms); | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-07-25 13:29:16 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_update_ready_time (GFileMonitorSource *fms) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms)); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static GSequenceIter * | 
					
						
							|  |  |  |  | g_file_monitor_source_find_pending_change (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                            const gchar        *child) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   return g_hash_table_lookup (fms->pending_changes_table, child); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_add_pending_change (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                           const gchar        *child, | 
					
						
							|  |  |  |  |                                           gint64              now) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   PendingChange *change; | 
					
						
							|  |  |  |  |   GSequenceIter *iter; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   change = g_slice_new (PendingChange); | 
					
						
							|  |  |  |  |   change->child = g_strdup (child); | 
					
						
							|  |  |  |  |   change->last_emission = now; | 
					
						
							|  |  |  |  |   change->dirty = FALSE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms); | 
					
						
							|  |  |  |  |   g_hash_table_insert (fms->pending_changes_table, change->child, iter); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  | static gboolean | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                                 GSequenceIter      *iter) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   PendingChange *change; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   change = g_sequence_get (iter); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |   /* if it was already dirty then this change is 'uninteresting' */ | 
					
						
							|  |  |  |  |   if (change->dirty) | 
					
						
							|  |  |  |  |     return FALSE; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |   change->dirty = TRUE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return TRUE; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                                 GSequenceIter      *iter) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   PendingChange *change; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   change = g_sequence_get (iter); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return change->dirty; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                              GSequenceIter      *iter, | 
					
						
							|  |  |  |  |                                              const gchar        *child) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   /* must remove the hash entry first -- its key is owned by the data
 | 
					
						
							|  |  |  |  |    * which will be freed when removing the sequence iter | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   g_hash_table_remove (fms->pending_changes_table, child); | 
					
						
							|  |  |  |  |   g_sequence_remove (iter); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_queue_event (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                    GFileMonitorEvent   event_type, | 
					
						
							|  |  |  |  |                                    const gchar        *child, | 
					
						
							|  |  |  |  |                                    GFile              *other) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   QueuedEvent *event; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   event = g_slice_new (QueuedEvent); | 
					
						
							|  |  |  |  |   event->event_type = event_type; | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |   if (child != NULL && fms->dirname != NULL) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child); | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |   else if (child != NULL) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       gchar *dirname = g_path_get_dirname (fms->filename); | 
					
						
							|  |  |  |  |       event->child = g_local_file_new_from_dirname_and_basename (dirname, child); | 
					
						
							|  |  |  |  |       g_free (dirname); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   else if (fms->dirname) | 
					
						
							|  |  |  |  |     event->child = _g_local_file_new (fms->dirname); | 
					
						
							|  |  |  |  |   else if (fms->filename) | 
					
						
							|  |  |  |  |     event->child = _g_local_file_new (fms->filename); | 
					
						
							|  |  |  |  |   event->other = other; | 
					
						
							|  |  |  |  |   if (other) | 
					
						
							|  |  |  |  |     g_object_ref (other); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_queue_push_tail (&fms->event_queue, event); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  | static gboolean | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | g_file_monitor_source_file_changed (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                     const gchar        *child, | 
					
						
							|  |  |  |  |                                     gint64              now) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GSequenceIter *pending; | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |   gboolean interesting; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   pending = g_file_monitor_source_find_pending_change (fms, child); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* If there is no pending change, emit one and create a record,
 | 
					
						
							|  |  |  |  |    * else: just mark the existing record as dirty. | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   if (!pending) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL); | 
					
						
							|  |  |  |  |       g_file_monitor_source_add_pending_change (fms, child, now); | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |       interesting = TRUE; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   else | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |     interesting = g_file_monitor_source_set_pending_change_dirty (fms, pending); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_file_monitor_source_update_ready_time (fms); | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   return interesting; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_file_changes_done (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                          const gchar        *child) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GSequenceIter *pending; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   pending = g_file_monitor_source_find_pending_change (fms, child); | 
					
						
							|  |  |  |  |   if (pending) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       /* If it is dirty, make sure we push out the last CHANGED event */ | 
					
						
							|  |  |  |  |       if (g_file_monitor_source_get_pending_change_dirty (fms, pending)) | 
					
						
							|  |  |  |  |         g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL); | 
					
						
							|  |  |  |  |       g_file_monitor_source_remove_pending_change (fms, pending, child); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_file_created (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                     const gchar        *child, | 
					
						
							|  |  |  |  |                                     gint64              event_time) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   /* Unlikely, but if we have pending changes for this filename, make
 | 
					
						
							|  |  |  |  |    * sure we flush those out first, before creating the new ones. | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   g_file_monitor_source_file_changes_done (fms, child); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Emit CREATE and add a pending changes record */ | 
					
						
							|  |  |  |  |   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL); | 
					
						
							|  |  |  |  |   g_file_monitor_source_add_pending_change (fms, child, event_time); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_send_event (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                   GFileMonitorEvent   event_type, | 
					
						
							|  |  |  |  |                                   const gchar        *child, | 
					
						
							|  |  |  |  |                                   GFile              *other) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   /* always flush any pending changes before we queue a new event */ | 
					
						
							|  |  |  |  |   g_file_monitor_source_file_changes_done (fms, child); | 
					
						
							|  |  |  |  |   g_file_monitor_source_queue_event (fms, event_type, child, other); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                               const gchar        *child) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_file_monitor_source_file_changes_done (fms, child); | 
					
						
							|  |  |  |  |   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL); | 
					
						
							|  |  |  |  |   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-05 12:22:51 +00:00
										 |  |  |  | #ifndef G_DISABLE_ASSERT
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | static gboolean | 
					
						
							|  |  |  |  | is_basename (const gchar *name) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0')) | 
					
						
							|  |  |  |  |     return FALSE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return !strchr (name, '/'); | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-05 12:22:51 +00:00
										 |  |  |  | #endif  /* !G_DISABLE_ASSERT */
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  | gboolean | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | g_file_monitor_source_handle_event (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                     GFileMonitorEvent   event_type, | 
					
						
							|  |  |  |  |                                     const gchar        *child, | 
					
						
							|  |  |  |  |                                     const gchar        *rename_to, | 
					
						
							|  |  |  |  |                                     GFile              *other, | 
					
						
							|  |  |  |  |                                     gint64              event_time) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |   gboolean interesting = TRUE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_assert (!child || is_basename (child)); | 
					
						
							|  |  |  |  |   g_assert (!rename_to || is_basename (rename_to)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:01 -04:00
										 |  |  |  |   if (fms->basename && (!child || !g_str_equal (child, fms->basename)) | 
					
						
							|  |  |  |  |                     && (!rename_to || !g_str_equal (rename_to, fms->basename))) | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |     return TRUE; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_lock (&fms->lock); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 17:54:18 +01:00
										 |  |  |  |   /* NOTE:
 | 
					
						
							|  |  |  |  |    * | 
					
						
							|  |  |  |  |    * We process events even if the file monitor has already been disposed. | 
					
						
							|  |  |  |  |    * The reason is that we must not take a reference to the instance here as | 
					
						
							|  |  |  |  |    * destroying it from the event handling thread will lead to a deadlock when | 
					
						
							|  |  |  |  |    * taking the lock in _ih_sub_cancel. | 
					
						
							|  |  |  |  |    * | 
					
						
							|  |  |  |  |    * This results in seemingly-unbounded growth of the `event_queue` with the | 
					
						
							|  |  |  |  |    * calls to `g_file_monitor_source_queue_event()`. However, each of those sets | 
					
						
							|  |  |  |  |    * the ready time on the #GSource, which means that it will be dispatched in | 
					
						
							|  |  |  |  |    * a subsequent iteration of the #GMainContext it’s attached to. At that | 
					
						
							|  |  |  |  |    * point, `g_file_monitor_source_dispatch()` will return %FALSE, and this will | 
					
						
							|  |  |  |  |    * trigger finalisation of the source. That will clear the `event_queue`. | 
					
						
							|  |  |  |  |    * | 
					
						
							|  |  |  |  |    * If the source is no longer attached, this will return early to prevent | 
					
						
							|  |  |  |  |    * unbounded queueing. | 
					
						
							| 
									
										
											  
											
												glocalfilemonitor: Avoid file monitor destruction from event thread
Taking a reference to the GFileMonitor when handling events may cause
object destruction from th worker thread that calls the function. This
condition happens if the surrounding code drops the otherwise last
reference ot the GFileMonitor. The series of events causes destruction
from an unrelated worker thread and also triggers g_file_monitor_cancel
to be called from g_file_monitor_source_handle_event.
For the inotify backend, this results in a deadlock as cancellation
needs to take a lock that protects data structures from being modified
while events are dispatched.
One alternative to this approach might be to add an RCU (release, copy,
update) approach to the lists contained in the wd_dir_hash and
wd_file_hash hash tables.
Fixes: #1941
An example stack trace of this happening is:
Thread 2 (Thread 0x7fea68b1d640 (LWP 260961) "gmain"):
 #0  syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
 #1  0x00007fea692215dc in g_mutex_lock_slowpath (mutex=mutex@entry=0x7fea6911e148 <g.inotify_lock_lock>) at ../glib/gthread-posix.c:1493
 #2  0x00007fea69222062 in g_mutex_lock (mutex=mutex@entry=0x7fea6911e148 <g.inotify_lock_lock>) at ../glib/gthread-posix.c:1517
 #3  0x00007fea6908025a in _ih_sub_cancel (sub=0x1492620) at ../gio/inotify/inotify-helper.c:131
 #4  0x00007fea6907f9da in g_inotify_file_monitor_cancel (monitor=0x14a3550) at ../gio/inotify/ginotifyfilemonitor.c:75
 #5  0x00007fea68fae959 in g_file_monitor_cancel (monitor=0x14a3550) at ../gio/gfilemonitor.c:241
 #6  0x00007fea68fae9dc in g_file_monitor_dispose (object=0x14a3550) at ../gio/gfilemonitor.c:123
 #7  0x00007fea69139341 in g_object_unref (_object=<optimized out>) at ../gobject/gobject.c:3636
 #8  g_object_unref (_object=0x14a3550) at ../gobject/gobject.c:3553
 #9  0x00007fea6907507a in g_file_monitor_source_handle_event (fms=0x14c3560, event_type=<optimized out>, child=0x7fea64001460 "spawned-1", rename_to=rename_to@entry=0x0, other=other@entry=0x0, event_time=<optimized out>) at ../gio/glocalfilemonitor.c:457
 #10 0x00007fea6907fe0e in ih_event_callback (event=0x7fea64001420, sub=0x1492620, file_event=<optimized out>) at ../gio/inotify/inotify-helper.c:218
 #11 0x00007fea6908075c in ip_event_dispatch (dir_list=dir_list@entry=0x14c14c0, file_list=0x0, event=event@entry=0x7fea64001420) at ../gio/inotify/inotify-path.c:493
 #12 0x00007fea6908094e in ip_event_dispatch (event=0x7fea64001420, file_list=<optimized out>, dir_list=0x14c14c0) at ../gio/inotify/inotify-path.c:448
 #13 ip_event_callback (event=0x7fea64001420) at ../gio/inotify/inotify-path.c:548
 #14 ip_event_callback (event=0x7fea64001420) at ../gio/inotify/inotify-path.c:530
 #15 0x00007fea69081391 in ik_source_dispatch (source=0x14a2bf0, func=0x7fea69080890 <ip_event_callback>, user_data=<optimized out>) at ../gio/inotify/inotify-kernel.c:327
 #16 0x00007fea691d0824 in g_main_dispatch (context=0x14a2cc0) at ../glib/gmain.c:3417
 #17 g_main_context_dispatch (context=0x14a2cc0) at ../glib/gmain.c:4135
 #18 0x00007fea691d0b88 in g_main_context_iterate (context=context@entry=0x14a2cc0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:4211
 #19 0x00007fea691d0c2f in g_main_context_iteration (context=0x14a2cc0, may_block=may_block@entry=1) at ../glib/gmain.c:4276
 #20 0x00007fea691d0c81 in glib_worker_main (data=<optimized out>) at ../glib/gmain.c:6176
 #21 0x00007fea691f9c2d in g_thread_proxy (data=0x1487cc0) at ../glib/gthread.c:827
 #22 0x00007fea68d93b1a in start_thread (arg=<optimized out>) at pthread_create.c:443
 #23 0x00007fea68e18650 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
											
										 
											2022-03-18 12:16:12 +01:00
										 |  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2022-05-30 17:54:18 +01:00
										 |  |  |  |   if (g_source_is_destroyed ((GSource *) fms)) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       g_mutex_unlock (&fms->lock); | 
					
						
							|  |  |  |  |       return TRUE; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (event_type) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_CREATED: | 
					
						
							|  |  |  |  |       g_assert (!other && !rename_to); | 
					
						
							|  |  |  |  |       g_file_monitor_source_file_created (fms, child, event_time); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2013-07-25 13:29:16 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     case G_FILE_MONITOR_EVENT_CHANGED: | 
					
						
							|  |  |  |  |       g_assert (!other && !rename_to); | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  |       interesting = g_file_monitor_source_file_changed (fms, child, event_time); | 
					
						
							| 
									
										
										
										
											2010-02-18 15:43:45 +01:00
										 |  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2013-07-25 13:29:16 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: | 
					
						
							|  |  |  |  |       g_assert (!other && !rename_to); | 
					
						
							|  |  |  |  |       g_file_monitor_source_file_changes_done (fms, child); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_MOVED_IN: | 
					
						
							|  |  |  |  |       g_assert (!rename_to); | 
					
						
							|  |  |  |  |       if (fms->flags & G_FILE_MONITOR_WATCH_MOVES) | 
					
						
							|  |  |  |  |         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other); | 
					
						
							|  |  |  |  |       else | 
					
						
							|  |  |  |  |         g_file_monitor_source_send_synthetic_created (fms, child); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_MOVED_OUT: | 
					
						
							|  |  |  |  |       g_assert (!rename_to); | 
					
						
							|  |  |  |  |       if (fms->flags & G_FILE_MONITOR_WATCH_MOVES) | 
					
						
							|  |  |  |  |         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other); | 
					
						
							| 
									
										
										
										
											2015-07-06 10:21:33 -04:00
										 |  |  |  |       else if (other && (fms->flags & G_FILE_MONITOR_SEND_MOVED)) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other); | 
					
						
							|  |  |  |  |       else | 
					
						
							|  |  |  |  |         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_RENAMED: | 
					
						
							|  |  |  |  |       g_assert (!other && rename_to); | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |       if (fms->flags & (G_FILE_MONITOR_WATCH_MOVES | G_FILE_MONITOR_SEND_MOVED)) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-01-19 18:45:46 +01:00
										 |  |  |  |           GFile *other_file; | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |           const gchar *dirname; | 
					
						
							|  |  |  |  |           gchar *allocated_dirname = NULL; | 
					
						
							|  |  |  |  |           GFileMonitorEvent event; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |           event = (fms->flags & G_FILE_MONITOR_WATCH_MOVES) ? G_FILE_MONITOR_EVENT_RENAMED : G_FILE_MONITOR_EVENT_MOVED; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |           if (fms->dirname != NULL) | 
					
						
							|  |  |  |  |             dirname = fms->dirname; | 
					
						
							|  |  |  |  |           else | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |               allocated_dirname = g_path_get_dirname (fms->filename); | 
					
						
							|  |  |  |  |               dirname = allocated_dirname; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-19 18:45:46 +01:00
										 |  |  |  |           other_file = g_local_file_new_from_dirname_and_basename (dirname, rename_to); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |           g_file_monitor_source_file_changes_done (fms, rename_to); | 
					
						
							| 
									
										
										
										
											2022-01-19 18:45:46 +01:00
										 |  |  |  |           g_file_monitor_source_send_event (fms, event, child, other_file); | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-19 18:45:46 +01:00
										 |  |  |  |           g_object_unref (other_file); | 
					
						
							| 
									
										
										
										
											2018-02-26 13:35:07 +00:00
										 |  |  |  |           g_free (allocated_dirname); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  |       else | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL); | 
					
						
							| 
									
										
										
										
											2015-03-25 23:08:38 -04:00
										 |  |  |  |           g_file_monitor_source_send_synthetic_created (fms, rename_to); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_DELETED: | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_UNMOUNTED: | 
					
						
							|  |  |  |  |       g_assert (!other && !rename_to); | 
					
						
							|  |  |  |  |       g_file_monitor_source_send_event (fms, event_type, child, NULL); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     case G_FILE_MONITOR_EVENT_MOVED: | 
					
						
							|  |  |  |  |       /* was never available in this API */ | 
					
						
							|  |  |  |  |     default: | 
					
						
							|  |  |  |  |       g_assert_not_reached (); | 
					
						
							| 
									
										
										
										
											2013-07-25 13:29:16 -04:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_file_monitor_source_update_ready_time (fms); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_unlock (&fms->lock); | 
					
						
							| 
									
										
										
										
											2015-01-15 15:39:43 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   return interesting; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | static gint64 | 
					
						
							|  |  |  |  | g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   gint64 rate_limit; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_lock (&fms->lock); | 
					
						
							|  |  |  |  |   rate_limit = fms->rate_limit; | 
					
						
							|  |  |  |  |   g_mutex_unlock (&fms->lock); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return rate_limit; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms, | 
					
						
							|  |  |  |  |                                       gint64              rate_limit) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   gboolean changed; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_lock (&fms->lock); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (rate_limit != fms->rate_limit) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       fms->rate_limit = rate_limit; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms); | 
					
						
							|  |  |  |  |       g_file_monitor_source_update_ready_time (fms); | 
					
						
							| 
									
										
										
										
											2015-05-03 12:17:10 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       changed = TRUE; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     changed = FALSE; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_mutex_unlock (&fms->lock); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   return changed; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | g_file_monitor_source_dispatch (GSource     *source, | 
					
						
							|  |  |  |  |                                 GSourceFunc  callback, | 
					
						
							|  |  |  |  |                                 gpointer     user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFileMonitorSource *fms = (GFileMonitorSource *) source; | 
					
						
							|  |  |  |  |   QueuedEvent *event; | 
					
						
							|  |  |  |  |   GQueue event_queue; | 
					
						
							|  |  |  |  |   gint64 now; | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   GFileMonitor *instance = NULL; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* make sure the monitor still exists */ | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   instance = g_weak_ref_get (&fms->instance_ref); | 
					
						
							|  |  |  |  |   if (instance == NULL) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     return FALSE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   now = g_source_get_time (source); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Acquire the lock once and grab all events in one go, handling the
 | 
					
						
							|  |  |  |  |    * queued events first.  This avoids strange possibilities in cases of | 
					
						
							|  |  |  |  |    * long delays, such as CHANGED events coming before CREATED events. | 
					
						
							|  |  |  |  |    * | 
					
						
							|  |  |  |  |    * We do this by converting the applicable pending changes into queued | 
					
						
							|  |  |  |  |    * events (after the ones already queued) and then stealing the entire | 
					
						
							|  |  |  |  |    * event queue in one go. | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   g_mutex_lock (&fms->lock); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Create events for any pending changes that are due to fire */ | 
					
						
							| 
									
										
										
										
											2015-10-30 10:02:06 -04:00
										 |  |  |  |   while (!g_sequence_is_empty (fms->pending_changes)) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     { | 
					
						
							|  |  |  |  |       GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes); | 
					
						
							|  |  |  |  |       PendingChange *pending = g_sequence_get (iter); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* We've gotten to a pending change that's not ready.  Stop. */ | 
					
						
							|  |  |  |  |       if (pending_change_get_ready_time (pending, fms) > now) | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       if (pending->dirty) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           /* It's time to send another CHANGED and update the record */ | 
					
						
							|  |  |  |  |           g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL); | 
					
						
							|  |  |  |  |           pending->last_emission = now; | 
					
						
							|  |  |  |  |           pending->dirty = FALSE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       else | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           /* It's time to send CHANGES_DONE and remove the pending record */ | 
					
						
							|  |  |  |  |           g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL); | 
					
						
							|  |  |  |  |           g_file_monitor_source_remove_pending_change (fms, iter, pending->child); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Steal the queue */ | 
					
						
							|  |  |  |  |   memcpy (&event_queue, &fms->event_queue, sizeof event_queue); | 
					
						
							|  |  |  |  |   memset (&fms->event_queue, 0, sizeof fms->event_queue); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_file_monitor_source_update_ready_time (fms); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_unlock (&fms->lock); | 
					
						
							| 
									
										
										
										
											2021-06-14 15:02:59 +01:00
										 |  |  |  |   g_clear_object (&instance); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* We now have our list of events to deliver */ | 
					
						
							|  |  |  |  |   while ((event = g_queue_pop_head (&event_queue))) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       /* an event handler could destroy 'instance', so check each time */ | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |       instance = g_weak_ref_get (&fms->instance_ref); | 
					
						
							|  |  |  |  |       if (instance != NULL) | 
					
						
							|  |  |  |  |         g_file_monitor_emit_event (instance, event->child, event->other, event->event_type); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |       g_clear_object (&instance); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |       queued_event_free (event); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return TRUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_dispose (GFileMonitorSource *fms) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   GHashTableIter iter; | 
					
						
							|  |  |  |  |   gpointer seqiter; | 
					
						
							|  |  |  |  |   QueuedEvent *event; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_mutex_lock (&fms->lock); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   g_hash_table_iter_init (&iter, fms->pending_changes_table); | 
					
						
							|  |  |  |  |   while (g_hash_table_iter_next (&iter, NULL, &seqiter)) | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |       g_hash_table_iter_remove (&iter); | 
					
						
							|  |  |  |  |       g_sequence_remove (seqiter); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   while ((event = g_queue_pop_head (&fms->event_queue))) | 
					
						
							|  |  |  |  |     queued_event_free (event); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   g_assert (g_sequence_is_empty (fms->pending_changes)); | 
					
						
							|  |  |  |  |   g_assert (g_hash_table_size (fms->pending_changes_table) == 0); | 
					
						
							|  |  |  |  |   g_assert (fms->event_queue.length == 0); | 
					
						
							|  |  |  |  |   g_weak_ref_set (&fms->instance_ref, NULL); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   g_file_monitor_source_update_ready_time (fms); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_source_destroy ((GSource *) fms); | 
					
						
							| 
									
										
										
										
											2022-05-30 17:54:18 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_unlock (&fms->lock); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_file_monitor_source_finalize (GSource *source) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFileMonitorSource *fms = (GFileMonitorSource *) source; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* should already have been cleared in dispose of the monitor */ | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   g_assert (g_weak_ref_get (&fms->instance_ref) == NULL); | 
					
						
							|  |  |  |  |   g_weak_ref_clear (&fms->instance_ref); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-30 10:02:06 -04:00
										 |  |  |  |   g_assert (g_sequence_is_empty (fms->pending_changes)); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_assert (g_hash_table_size (fms->pending_changes_table) == 0); | 
					
						
							|  |  |  |  |   g_assert (fms->event_queue.length == 0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_hash_table_unref (fms->pending_changes_table); | 
					
						
							|  |  |  |  |   g_sequence_free (fms->pending_changes); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_free (fms->dirname); | 
					
						
							|  |  |  |  |   g_free (fms->basename); | 
					
						
							|  |  |  |  |   g_free (fms->filename); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_mutex_clear (&fms->lock); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static guint | 
					
						
							|  |  |  |  | str_hash0 (gconstpointer str) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   return str ? g_str_hash (str) : 0; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | str_equal0 (gconstpointer a, | 
					
						
							|  |  |  |  |             gconstpointer b) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   return g_strcmp0 (a, b) == 0; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static GFileMonitorSource * | 
					
						
							|  |  |  |  | g_file_monitor_source_new (gpointer           instance, | 
					
						
							|  |  |  |  |                            const gchar       *filename, | 
					
						
							|  |  |  |  |                            gboolean           is_directory, | 
					
						
							|  |  |  |  |                            GFileMonitorFlags  flags) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   static GSourceFuncs source_funcs = { | 
					
						
							|  |  |  |  |     NULL, NULL, | 
					
						
							|  |  |  |  |     g_file_monitor_source_dispatch, | 
					
						
							| 
									
										
										
										
											2020-11-17 00:59:06 +01:00
										 |  |  |  |     g_file_monitor_source_finalize, | 
					
						
							|  |  |  |  |     NULL, NULL | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   }; | 
					
						
							|  |  |  |  |   GFileMonitorSource *fms; | 
					
						
							|  |  |  |  |   GSource *source; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   source = g_source_new (&source_funcs, sizeof (GFileMonitorSource)); | 
					
						
							|  |  |  |  |   fms = (GFileMonitorSource *) source; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 10:53:02 +01:00
										 |  |  |  |   g_source_set_static_name (source, "GFileMonitorSource"); | 
					
						
							| 
									
										
										
										
											2016-06-16 18:31:42 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_mutex_init (&fms->lock); | 
					
						
							| 
									
										
										
										
											2019-10-11 22:22:46 +01:00
										 |  |  |  |   g_weak_ref_init (&fms->instance_ref, instance); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   fms->pending_changes = g_sequence_new (pending_change_free); | 
					
						
							|  |  |  |  |   fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0); | 
					
						
							|  |  |  |  |   fms->rate_limit = DEFAULT_RATE_LIMIT; | 
					
						
							|  |  |  |  |   fms->flags = flags; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (is_directory) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       fms->dirname = g_strdup (filename); | 
					
						
							|  |  |  |  |       fms->basename = NULL; | 
					
						
							|  |  |  |  |       fms->filename = NULL; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       fms->dirname = NULL; | 
					
						
							|  |  |  |  |       fms->basename = NULL; | 
					
						
							|  |  |  |  |       fms->filename = g_strdup (filename); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       fms->dirname = g_path_get_dirname (filename); | 
					
						
							|  |  |  |  |       fms->basename = g_path_get_basename (filename); | 
					
						
							|  |  |  |  |       fms->filename = NULL; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return fms; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | enum { | 
					
						
							|  |  |  |  |   PROP_0, | 
					
						
							|  |  |  |  |   PROP_RATE_LIMIT, | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_get_property (GObject *object, guint prop_id, | 
					
						
							|  |  |  |  |                                    GValue *value, GParamSpec *pspec) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object); | 
					
						
							|  |  |  |  |   gint64 rate_limit; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_assert (prop_id == PROP_RATE_LIMIT); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   rate_limit = g_file_monitor_source_get_rate_limit (monitor->source); | 
					
						
							|  |  |  |  |   rate_limit /= G_TIME_SPAN_MILLISECOND; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_value_set_int (value, rate_limit); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_set_property (GObject *object, guint prop_id, | 
					
						
							|  |  |  |  |                                    const GValue *value, GParamSpec *pspec) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object); | 
					
						
							|  |  |  |  |   gint64 rate_limit; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_assert (prop_id == PROP_RATE_LIMIT); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   rate_limit = g_value_get_int (value); | 
					
						
							|  |  |  |  |   rate_limit *= G_TIME_SPAN_MILLISECOND; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit)) | 
					
						
							|  |  |  |  |     g_object_notify (object, "rate-limit"); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #ifndef G_OS_WIN32
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor, | 
					
						
							|  |  |  |  |                                      gpointer           user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *local_monitor = user_data; | 
					
						
							|  |  |  |  |   GUnixMountEntry *mount; | 
					
						
							|  |  |  |  |   gboolean is_mounted; | 
					
						
							|  |  |  |  |   GFile *file; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Emulate unmount detection */ | 
					
						
							| 
									
										
										
										
											2024-10-18 10:31:12 +01:00
										 |  |  |  |   mount = g_unix_mount_entry_at (local_monitor->source->dirname, NULL); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   is_mounted = mount != NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (mount) | 
					
						
							| 
									
										
										
										
											2024-10-18 10:31:12 +01:00
										 |  |  |  |     g_unix_mount_entry_free (mount); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (local_monitor->was_mounted != is_mounted) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       if (local_monitor->was_mounted && !is_mounted) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           file = g_file_new_for_path (local_monitor->source->dirname); | 
					
						
							|  |  |  |  |           g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED); | 
					
						
							|  |  |  |  |           g_object_unref (file); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       local_monitor->was_mounted = is_mounted; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_start (GLocalFileMonitor *local_monitor, | 
					
						
							|  |  |  |  |                             const gchar       *filename, | 
					
						
							|  |  |  |  |                             gboolean           is_directory, | 
					
						
							|  |  |  |  |                             GFileMonitorFlags  flags, | 
					
						
							|  |  |  |  |                             GMainContext      *context) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor); | 
					
						
							|  |  |  |  |   GFileMonitorSource *source; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_assert (!local_monitor->source); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-30 10:13:46 -05:00
										 |  |  |  |   source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags); | 
					
						
							|  |  |  |  |   local_monitor->source = source; /* owns the ref */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS)) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  | #ifdef G_OS_WIN32
 | 
					
						
							|  |  |  |  |       /*claim everything was mounted */ | 
					
						
							|  |  |  |  |       local_monitor->was_mounted = TRUE; | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |       GUnixMountEntry *mount; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Emulate unmount detection */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-18 10:31:12 +01:00
										 |  |  |  |       mount = g_unix_mount_entry_at (local_monitor->source->dirname, NULL); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       local_monitor->was_mounted = mount != NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       if (mount) | 
					
						
							| 
									
										
										
										
											2024-10-18 10:31:12 +01:00
										 |  |  |  |         g_unix_mount_entry_free (mount); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       local_monitor->mount_monitor = g_unix_mount_monitor_get (); | 
					
						
							|  |  |  |  |       g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed", | 
					
						
							| 
									
										
										
										
											2022-06-23 09:41:21 +01:00
										 |  |  |  |                                G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor, | 
					
						
							|  |  |  |  |                                G_CONNECT_DEFAULT); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | #endif
 | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-22 00:00:00 +00:00
										 |  |  |  |   g_source_attach ((GSource *) source, context); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor, | 
					
						
							|  |  |  |  |                                                          source->dirname, source->basename, source->filename, | 
					
						
							|  |  |  |  |                                                          source); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_dispose (GObject *object) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_file_monitor_source_dispose (local_monitor->source); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_finalize (GObject *object) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object); | 
					
						
							| 
									
										
										
										
											2013-07-25 13:29:16 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_source_unref ((GSource *) local_monitor->source); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-16 09:54:04 +00:00
										 |  |  |  |   G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | static void | 
					
						
							|  |  |  |  | g_local_file_monitor_init (GLocalFileMonitor* local_monitor) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class) | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   GObjectClass *gobject_class = G_OBJECT_CLASS (class); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   gobject_class->get_property = g_local_file_monitor_get_property; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |   gobject_class->set_property = g_local_file_monitor_set_property; | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   gobject_class->dispose = g_local_file_monitor_dispose; | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  |   gobject_class->finalize = g_local_file_monitor_finalize; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit"); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static GLocalFileMonitor * | 
					
						
							|  |  |  |  | g_local_file_monitor_new (gboolean   is_remote_fs, | 
					
						
							| 
									
										
										
										
											2018-06-15 14:29:30 +02:00
										 |  |  |  |                           gboolean   is_directory, | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |                           GError   **error) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-18 18:44:11 -05:00
										 |  |  |  |   GType type = G_TYPE_INVALID; | 
					
						
							| 
									
										
										
										
											2007-12-19 16:08:55 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 18:44:11 -05:00
										 |  |  |  |   if (is_remote_fs) | 
					
						
							|  |  |  |  |     type = _g_io_module_get_default_type (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME, | 
					
						
							|  |  |  |  |                                           "GIO_USE_FILE_MONITOR", | 
					
						
							|  |  |  |  |                                           G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-15 14:29:30 +02:00
										 |  |  |  |   /* Fallback rather to poll file monitor for remote files, see gfile.c. */ | 
					
						
							|  |  |  |  |   if (type == G_TYPE_INVALID && (!is_remote_fs || is_directory)) | 
					
						
							| 
									
										
										
										
											2013-01-18 18:44:11 -05:00
										 |  |  |  |     type = _g_io_module_get_default_type (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, | 
					
						
							|  |  |  |  |                                           "GIO_USE_FILE_MONITOR", | 
					
						
							|  |  |  |  |                                           G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported)); | 
					
						
							| 
									
										
										
										
											2007-12-19 16:08:55 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   if (type == G_TYPE_INVALID) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, | 
					
						
							|  |  |  |  |                            _("Unable to find default local file monitor type")); | 
					
						
							|  |  |  |  |       return NULL; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return g_object_new (type, NULL); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | GFileMonitor * | 
					
						
							|  |  |  |  | g_local_file_monitor_new_for_path (const gchar        *pathname, | 
					
						
							|  |  |  |  |                                    gboolean            is_directory, | 
					
						
							|  |  |  |  |                                    GFileMonitorFlags   flags, | 
					
						
							|  |  |  |  |                                    GError            **error) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *monitor; | 
					
						
							|  |  |  |  |   gboolean is_remote_fs; | 
					
						
							| 
									
										
										
										
											2007-12-19 16:08:55 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-15 17:44:26 +02:00
										 |  |  |  |   is_remote_fs = g_local_file_is_nfs_home (pathname); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-15 14:29:30 +02:00
										 |  |  |  |   monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (monitor) | 
					
						
							|  |  |  |  |     g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_FILE_MONITOR (monitor); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | GFileMonitor * | 
					
						
							|  |  |  |  | g_local_file_monitor_new_in_worker (const gchar           *pathname, | 
					
						
							|  |  |  |  |                                     gboolean               is_directory, | 
					
						
							|  |  |  |  |                                     GFileMonitorFlags      flags, | 
					
						
							|  |  |  |  |                                     GFileMonitorCallback   callback, | 
					
						
							|  |  |  |  |                                     gpointer               user_data, | 
					
						
							| 
									
										
										
										
											2019-10-11 21:33:47 +01:00
										 |  |  |  |                                     GClosureNotify         destroy_user_data, | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |                                     GError               **error) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GLocalFileMonitor *monitor; | 
					
						
							|  |  |  |  |   gboolean is_remote_fs; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-15 17:44:26 +02:00
										 |  |  |  |   is_remote_fs = g_local_file_is_nfs_home (pathname); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-15 14:29:30 +02:00
										 |  |  |  |   monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (monitor) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       if (callback) | 
					
						
							| 
									
										
										
										
											2019-10-11 21:33:47 +01:00
										 |  |  |  |         g_signal_connect_data (monitor, "changed", G_CALLBACK (callback), | 
					
						
							| 
									
										
										
										
											2022-06-23 09:41:21 +01:00
										 |  |  |  |                                user_data, destroy_user_data, G_CONNECT_DEFAULT); | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ()); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-07-25 13:29:16 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 14:59:35 -05:00
										 |  |  |  |   return G_FILE_MONITOR (monitor); | 
					
						
							| 
									
										
										
										
											2007-11-26 16:13:05 +00:00
										 |  |  |  | } |