Blob Blame History Raw
diff -up evolution-data-server-3.12.11/libebackend/e-source-registry-server.c.goa-source-change-removed evolution-data-server-3.12.11/libebackend/e-source-registry-server.c
--- evolution-data-server-3.12.11/libebackend/e-source-registry-server.c.goa-source-change-removed	2014-11-11 18:40:25.000000000 +0100
+++ evolution-data-server-3.12.11/libebackend/e-source-registry-server.c	2015-08-17 16:55:07.401935846 +0200
@@ -87,6 +87,10 @@ struct _ESourceRegistryServerPrivate {
 	GHashTable *waiting_auths;
 
 	guint authentication_count;
+
+	GMutex file_monitor_lock;
+	GHashTable *file_monitor_events; /* gchar *uid ~> FileEventData * */
+	GSource *file_monitor_source;
 };
 
 struct _AuthRequest {
@@ -899,51 +903,121 @@ source_registry_server_reload_cb (EDBusS
 	return TRUE;
 }
 
+typedef struct _FileEventData {
+	GFile *file;
+	GFileMonitorEvent event_type;
+} FileEventData;
+
+static FileEventData *
+file_event_data_new (GFile *file,
+		     GFileMonitorEvent event_type)
+{
+	FileEventData *fed;
+
+	fed = g_new0 (FileEventData, 1);
+	fed->file = g_object_ref (file);
+	fed->event_type = event_type;
+
+	return fed;
+}
+
 static void
-source_registry_server_monitor_changed_cb (GFileMonitor *monitor,
-                                           GFile *file,
-                                           GFile *other_file,
-                                           GFileMonitorEvent event_type,
-                                           ESourceRegistryServer *server)
+file_event_data_free (gpointer ptr)
 {
+	FileEventData *fed = ptr;
+
+	if (fed) {
+		g_clear_object (&fed->file);
+		g_free (fed);
+	}
+}
+
+static void
+source_registry_server_process_file_monitor_event (gpointer key,
+						   gpointer value,
+						   gpointer user_data)
+{
+	const gchar *uid = key;
+	const FileEventData *fed = value;
+	ESourceRegistryServer *server = user_data;
+	GFileMonitorEvent event_type;
+
+	g_return_if_fail (uid != NULL);
+	g_return_if_fail (fed != NULL);
+
+	event_type = fed->event_type;
+
+	if (e_source_registry_debug_enabled ()) {
+		e_source_registry_debug_print ("Processing file monitor event %s (%u) for UID: %s\n",
+			event_type == G_FILE_MONITOR_EVENT_CHANGED ? "CHANGED" :
+			event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT ? "CHANGES_DONE_HINT" :
+			event_type == G_FILE_MONITOR_EVENT_DELETED ? "DELETED" :
+			event_type == G_FILE_MONITOR_EVENT_CREATED ? "CREATED" :
+			event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED ? "ATTRIBUTE_CHANGED" :
+			event_type == G_FILE_MONITOR_EVENT_PRE_UNMOUNT ? "PRE_UNMOUNT" :
+			event_type == G_FILE_MONITOR_EVENT_UNMOUNTED ? "UNMOUNTED" :
+			event_type == G_FILE_MONITOR_EVENT_MOVED ? "MOVED" : "???",
+			event_type,
+			uid);
+	}
+
+	if (event_type == G_FILE_MONITOR_EVENT_CHANGED ||
+	    event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
+		ESource *source;
+		GError *error = NULL;
+
+		source = e_source_registry_server_ref_source (server, uid);
+
+		/* If the source does not exist, create it; parsing may have
+		 * failed when the file was originally created. This can happen
+		 * if the file is created (empty), then e-source-registry-server
+		 * detects it, then it’s populated and made valid.
+		 *
+		 * Otherwise, reload the file since it has changed. */
+		if (source == NULL) {
+			event_type = G_FILE_MONITOR_EVENT_CREATED;
+		} else if (!e_server_side_source_load (E_SERVER_SIDE_SOURCE (source), NULL, &error)) {
+			g_warning ("Error reloading source ‘%s’: %s", uid, error->message);
+
+			g_error_free (error);
+			g_object_unref (source);
+
+			return;
+		}
+
+		g_object_unref (source);
+	}
+
 	if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
 		ESource *source;
 		GError *error = NULL;
 
-		/* it can return NULL source for hidden files */
-		source = e_server_side_source_new (server, file, &error);
+		source = e_source_registry_server_ref_source (server, uid);
+
+		if (!source) {
+			/* it can return NULL source for hidden files */
+			source = e_server_side_source_new (server, fed->file, &error);
+		}
 
 		if (!error && source) {
 			/* File monitors are only placed on directories
 			 * where data sources are writable and removable,
 			 * so it should be safe to assume these flags. */
-			e_server_side_source_set_writable (
-				E_SERVER_SIDE_SOURCE (source), TRUE);
-			e_server_side_source_set_removable (
-				E_SERVER_SIDE_SOURCE (source), TRUE);
+			e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
+			e_server_side_source_set_removable (E_SERVER_SIDE_SOURCE (source), TRUE);
 
 			e_source_registry_server_add_source (server, source);
-			g_object_unref (source);
 		} else if (error) {
-			e_source_registry_server_load_error (
-				server, file, error);
+			e_source_registry_server_load_error (server, fed->file, error);
 			g_error_free (error);
 		}
 	}
 
 	if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
 		ESource *source;
-		gchar *uid;
-
-		uid = e_server_side_source_uid_from_file (file, NULL);
-
-		if (uid == NULL)
-			return;
 
 		source = e_source_registry_server_ref_source (server, uid);
 
-		g_free (uid);
-
 		if (source == NULL)
 			return;
 
@@ -958,6 +1032,87 @@ source_registry_server_monitor_changed_c
 }
 
 static gboolean
+source_registry_server_process_file_monitor_events_cb (gpointer user_data)
+{
+	ESourceRegistryServer *server = user_data;
+	GHashTable *events;
+
+	if (g_source_is_destroyed (g_main_current_source ()))
+		return FALSE;
+
+	g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), FALSE);
+
+	g_mutex_lock (&server->priv->file_monitor_lock);
+	events = server->priv->file_monitor_events;
+	server->priv->file_monitor_events = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, file_event_data_free);
+	g_mutex_unlock (&server->priv->file_monitor_lock);
+
+	g_hash_table_foreach (events, source_registry_server_process_file_monitor_event, server);
+	g_hash_table_destroy (events);
+
+	return FALSE;
+}
+
+static void
+source_registry_server_monitor_changed_cb (GFileMonitor *monitor,
+                                           GFile *file,
+                                           GFile *other_file,
+                                           GFileMonitorEvent event_type,
+                                           ESourceRegistryServer *server)
+{
+	if (e_source_registry_debug_enabled ()) {
+		gchar *uri;
+
+		uri = g_file_get_uri (file);
+		e_source_registry_debug_print ("Handling file monitor event %s (%u) for URI: %s\n",
+			event_type == G_FILE_MONITOR_EVENT_CHANGED ? "CHANGED" :
+			event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT ? "CHANGES_DONE_HINT" :
+			event_type == G_FILE_MONITOR_EVENT_DELETED ? "DELETED" :
+			event_type == G_FILE_MONITOR_EVENT_CREATED ? "CREATED" :
+			event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED ? "ATTRIBUTE_CHANGED" :
+			event_type == G_FILE_MONITOR_EVENT_PRE_UNMOUNT ? "PRE_UNMOUNT" :
+			event_type == G_FILE_MONITOR_EVENT_UNMOUNTED ? "UNMOUNTED" :
+			event_type == G_FILE_MONITOR_EVENT_MOVED ? "MOVED" : "???",
+			event_type,
+			uri);
+		g_free (uri);
+	}
+
+	if (event_type == G_FILE_MONITOR_EVENT_CHANGED ||
+	    event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT ||
+	    event_type == G_FILE_MONITOR_EVENT_CREATED ||
+	    event_type == G_FILE_MONITOR_EVENT_DELETED) {
+		gchar *uid;
+
+		uid = e_server_side_source_uid_from_file (file, NULL);
+
+		if (uid == NULL)
+			return;
+
+		g_mutex_lock (&server->priv->file_monitor_lock);
+		/* This overwrites any previous events, aka the last wins
+		   (overwrite can be DELETE + CREATE, which handles it correctly). */
+		g_hash_table_insert (server->priv->file_monitor_events, uid, file_event_data_new (file, event_type));
+
+		if (server->priv->file_monitor_source) {
+			g_source_destroy (server->priv->file_monitor_source);
+			g_source_unref (server->priv->file_monitor_source);
+		}
+
+		server->priv->file_monitor_source = g_timeout_source_new_seconds (3);
+		g_source_set_callback (
+			server->priv->file_monitor_source,
+			source_registry_server_process_file_monitor_events_cb,
+			server, NULL);
+		g_source_attach (
+			server->priv->file_monitor_source,
+			server->priv->main_context);
+
+		g_mutex_unlock (&server->priv->file_monitor_lock);
+	}
+}
+
+static gboolean
 source_registry_server_traverse_cb (GNode *node,
                                     GQueue *queue)
 {
@@ -1047,6 +1202,14 @@ source_registry_server_dispose (GObject
 
 	priv = E_SOURCE_REGISTRY_SERVER_GET_PRIVATE (object);
 
+	g_mutex_lock (&priv->file_monitor_lock);
+	if (priv->file_monitor_source) {
+		g_source_destroy (priv->file_monitor_source);
+		g_source_unref (priv->file_monitor_source);
+		priv->file_monitor_source = NULL;
+	}
+	g_mutex_unlock (&priv->file_monitor_lock);
+
 	if (priv->main_context != NULL) {
 		g_main_context_unref (priv->main_context);
 		priv->main_context = NULL;
@@ -1065,6 +1228,7 @@ source_registry_server_dispose (GObject
 	g_hash_table_remove_all (priv->sources);
 	g_hash_table_remove_all (priv->orphans);
 	g_hash_table_remove_all (priv->monitors);
+	g_hash_table_remove_all (priv->file_monitor_events);
 
 	g_hash_table_remove_all (priv->running_auths);
 	g_hash_table_remove_all (priv->waiting_auths);
@@ -1084,9 +1248,11 @@ source_registry_server_finalize (GObject
 	g_hash_table_destroy (priv->sources);
 	g_hash_table_destroy (priv->orphans);
 	g_hash_table_destroy (priv->monitors);
+	g_hash_table_destroy (priv->file_monitor_events);
 
 	g_mutex_clear (&priv->sources_lock);
 	g_mutex_clear (&priv->orphans_lock);
+	g_mutex_clear (&priv->file_monitor_lock);
 
 	g_mutex_clear (&priv->auth_lock);
 	g_hash_table_destroy (priv->running_auths);
@@ -1444,8 +1444,10 @@ e_source_registry_server_init (ESourceRegistryServ
 	g_mutex_init (&server->priv->sources_lock);
 	g_mutex_init (&server->priv->orphans_lock);
 	g_mutex_init (&server->priv->auth_lock);
+	g_mutex_init (&server->priv->file_monitor_lock);
 	server->priv->waiting_auths = waiting_auths;
 	server->priv->running_auths = running_auths;
+	server->priv->file_monitor_events = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, file_event_data_free);
 
 	g_signal_connect (
 		source_manager, "handle-allow-auth-prompt-all",
diff -up evolution-data-server-3.12.11/libedataserver/e-source.c.goa-source-change-removed evolution-data-server-3.12.11/libedataserver/e-source.c
--- evolution-data-server-3.12.11/libedataserver/e-source.c.goa-source-change-removed	2015-08-17 16:55:07.344936312 +0200
+++ evolution-data-server-3.12.11/libedataserver/e-source.c	2015-08-17 16:55:07.402935838 +0200
@@ -126,9 +126,6 @@ struct _ESourcePrivate {
 	GSource *changed;
 	GMutex changed_lock;
 
-	GSource *data_change;
-	GMutex data_change_lock;
-
 	GMutex property_lock;
 
 	gchar *display_name;
@@ -790,26 +787,13 @@ source_parse_dbus_data (ESource *source,
 	return TRUE;
 }
 
-static gboolean
-source_idle_data_change_cb (gpointer user_data)
+static void
+source_notify_dbus_data_cb (EDBusSource *dbus_source,
+                            GParamSpec *pspec,
+                            ESource *source)
 {
-	ESource *source = E_SOURCE (user_data);
 	GError *local_error = NULL;
 
-	/* If the ESource is still initializing itself in a different
-	 * thread, skip the signal emission and try again on the next
-	 * main loop iteration. This is a busy wait but it should be
-	 * a very short wait. */
-	if (!source->priv->initialized)
-		return TRUE;
-
-	g_mutex_lock (&source->priv->data_change_lock);
-	if (source->priv->data_change != NULL) {
-		g_source_unref (source->priv->data_change);
-		source->priv->data_change = NULL;
-	}
-	g_mutex_unlock (&source->priv->data_change_lock);
-
 	g_rec_mutex_lock (&source->priv->lock);
 
 	/* Since the source data came from a GKeyFile structure on the
@@ -823,27 +807,6 @@ source_idle_data_change_cb (gpointer use
 	}
 
 	g_rec_mutex_unlock (&source->priv->lock);
-
-	return FALSE;
-}
-
-static void
-source_notify_dbus_data_cb (EDBusSource *dbus_source,
-                            GParamSpec *pspec,
-                            ESource *source)
-{
-	g_mutex_lock (&source->priv->data_change_lock);
-	if (source->priv->data_change == NULL) {
-		source->priv->data_change = g_idle_source_new ();
-		g_source_set_callback (
-			source->priv->data_change,
-			source_idle_data_change_cb,
-			source, NULL);
-		g_source_attach (
-			source->priv->data_change,
-			source->priv->main_context);
-	}
-	g_mutex_unlock (&source->priv->data_change_lock);
 }
 
 static gboolean
@@ -1066,14 +1029,6 @@ source_dispose (GObject *object)
 	}
 	g_mutex_unlock (&priv->changed_lock);
 
-	g_mutex_lock (&priv->data_change_lock);
-	if (priv->data_change != NULL) {
-		g_source_destroy (priv->data_change);
-		g_source_unref (priv->data_change);
-		priv->data_change = NULL;
-	}
-	g_mutex_unlock (&priv->data_change_lock);
-
 	g_hash_table_remove_all (priv->extensions);
 
 	/* Chain up to parent's dispose() method. */
@@ -1088,7 +1043,6 @@ source_finalize (GObject *object)
 	priv = E_SOURCE_GET_PRIVATE (object);
 
 	g_mutex_clear (&priv->changed_lock);
-	g_mutex_clear (&priv->data_change_lock);
 	g_mutex_clear (&priv->property_lock);
 
 	g_free (priv->display_name);
@@ -2092,7 +2046,6 @@ e_source_init (ESource *source)
 
 	source->priv = E_SOURCE_GET_PRIVATE (source);
 	g_mutex_init (&source->priv->changed_lock);
-	g_mutex_init (&source->priv->data_change_lock);
 	g_mutex_init (&source->priv->property_lock);
 	source->priv->key_file = g_key_file_new ();
 	source->priv->extensions = extensions;