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;