[evolution-data-server/wip/offline-cache] Handle ECache revision change automatically



commit af76ef7f4e0ac0deb6944e05617b487c4249619e
Author: Milan Crha <mcrha redhat com>
Date:   Wed May 17 09:42:41 2017 +0200

    Handle ECache revision change automatically

 src/addressbook/libedata-book/e-book-cache.c       |    4 +
 .../libedata-book/e-book-meta-backend.c            |   33 ++++
 src/calendar/libedata-cal/e-cal-cache.c            |    4 +
 src/calendar/libedata-cal/e-cal-meta-backend.c     |   33 ++++
 src/libebackend/e-cache.c                          |  163 +++++++++++++++++++-
 src/libebackend/e-cache.h                          |    6 +
 6 files changed, 240 insertions(+), 3 deletions(-)
---
diff --git a/src/addressbook/libedata-book/e-book-cache.c b/src/addressbook/libedata-book/e-book-cache.c
index 8b1916a..cb1fda2 100644
--- a/src/addressbook/libedata-book/e-book-cache.c
+++ b/src/addressbook/libedata-book/e-book-cache.c
@@ -4802,6 +4802,7 @@ e_book_cache_put_contacts (EBookCache *book_cache,
        other_columns = e_cache_column_values_new ();
 
        e_cache_lock (cache, E_CACHE_LOCK_WRITE);
+       e_cache_freeze_revision_change (cache);
 
        for (clink = contacts, elink = extras; clink; clink = g_slist_next (clink), elink = g_slist_next 
(elink)) {
                EContact *contact = clink->data;
@@ -4833,6 +4834,7 @@ e_book_cache_put_contacts (EBookCache *book_cache,
                        break;
        }
 
+       e_cache_thaw_revision_change (cache);
        e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
        e_cache_column_values_free (other_columns);
@@ -4907,6 +4909,7 @@ e_book_cache_remove_contacts (EBookCache *book_cache,
        cache = E_CACHE (book_cache);
 
        e_cache_lock (cache, E_CACHE_LOCK_WRITE);
+       e_cache_freeze_revision_change (cache);
 
        for (link = uids; success && link; link = g_slist_next (link)) {
                const gchar *uid = link->data;
@@ -4914,6 +4917,7 @@ e_book_cache_remove_contacts (EBookCache *book_cache,
                success = e_cache_remove (cache, uid, offline_flag, cancellable, error);
        }
 
+       e_cache_thaw_revision_change (cache);
        e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
        return success;
diff --git a/src/addressbook/libedata-book/e-book-meta-backend.c 
b/src/addressbook/libedata-book/e-book-meta-backend.c
index edcff27..bde296b 100644
--- a/src/addressbook/libedata-book/e-book-meta-backend.c
+++ b/src/addressbook/libedata-book/e-book-meta-backend.c
@@ -66,6 +66,7 @@ struct _EBookMetaBackendPrivate {
                                                   used to detect false notifications on EBackend::online */
        gulong source_changed_id;
        gulong notify_online_id;
+       gulong revision_changed_id;
        guint refresh_timeout_id;
 
        gboolean refresh_after_authenticate;
@@ -2484,6 +2485,12 @@ e_book_meta_backend_dispose (GObject *object)
                meta_backend->priv->notify_online_id = 0;
        }
 
+       if (meta_backend->priv->revision_changed_id) {
+               if (meta_backend->priv->cache)
+                       g_signal_handler_disconnect (meta_backend->priv->cache, 
meta_backend->priv->revision_changed_id);
+               meta_backend->priv->revision_changed_id = 0;
+       }
+
        g_hash_table_foreach (meta_backend->priv->view_cancellables, ebmb_cancel_view_cb, NULL);
 
        if (meta_backend->priv->refresh_cancellable) {
@@ -2787,6 +2794,24 @@ e_book_meta_backend_get_connected_writable (EBookMetaBackend *meta_backend)
        return result;
 }
 
+static void
+ebmb_cache_revision_changed_cb (ECache *cache,
+                               gpointer user_data)
+{
+       EBookMetaBackend *meta_backend = user_data;
+       gchar *revision;
+
+       g_return_if_fail (E_IS_CACHE (cache));
+       g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
+
+       revision = e_cache_dup_revision (cache);
+       if (revision) {
+               e_book_backend_notify_property_changed (E_BOOK_BACKEND (meta_backend),
+                       BOOK_BACKEND_PROPERTY_REVISION, revision);
+               g_free (revision);
+       }
+}
+
 /**
  * e_book_meta_backend_set_cache:
  * @meta_backend: an #EBookMetaBackend
@@ -2817,9 +2842,17 @@ e_book_meta_backend_set_cache (EBookMetaBackend *meta_backend,
 
        g_clear_error (&meta_backend->priv->create_cache_error);
 
+       if (meta_backend->priv->cache) {
+               g_signal_handler_disconnect (meta_backend->priv->cache,
+                       meta_backend->priv->revision_changed_id);
+       }
+
        g_clear_object (&meta_backend->priv->cache);
        meta_backend->priv->cache = g_object_ref (cache);
 
+       meta_backend->priv->revision_changed_id = g_signal_connect_object (meta_backend->priv->cache,
+               "revision-changed", G_CALLBACK (ebmb_cache_revision_changed_cb), meta_backend, 0);
+
        g_mutex_unlock (&meta_backend->priv->property_lock);
 
        g_object_notify (G_OBJECT (meta_backend), "cache");
diff --git a/src/calendar/libedata-cal/e-cal-cache.c b/src/calendar/libedata-cal/e-cal-cache.c
index 8a186ed..24783bf 100644
--- a/src/calendar/libedata-cal/e-cal-cache.c
+++ b/src/calendar/libedata-cal/e-cal-cache.c
@@ -1945,6 +1945,7 @@ e_cal_cache_put_components (ECalCache *cal_cache,
        other_columns = e_cache_column_values_new ();
 
        e_cache_lock (cache, E_CACHE_LOCK_WRITE);
+       e_cache_freeze_revision_change (cache);
 
        for (clink = components, elink = extras; clink; clink = g_slist_next (clink), elink = g_slist_next 
(elink)) {
                ECalComponent *component = clink->data;
@@ -1983,6 +1984,7 @@ e_cal_cache_put_components (ECalCache *cal_cache,
                        break;
        }
 
+       e_cache_thaw_revision_change (cache);
        e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
        e_cache_column_values_free (other_columns);
@@ -2065,6 +2067,7 @@ e_cal_cache_remove_components (ECalCache *cal_cache,
        cache = E_CACHE (cal_cache);
 
        e_cache_lock (cache, E_CACHE_LOCK_WRITE);
+       e_cache_freeze_revision_change (cache);
 
        for (link = ids; success && link; link = g_slist_next (link)) {
                const ECalComponentId *id = link->data;
@@ -2082,6 +2085,7 @@ e_cal_cache_remove_components (ECalCache *cal_cache,
                g_free (uid);
        }
 
+       e_cache_thaw_revision_change (cache);
        e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
        return success;
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index 47e58de..49ee547 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -63,6 +63,7 @@ struct _ECalMetaBackendPrivate {
                                                   used to detect false notifications on EBackend::online */
        gulong source_changed_id;
        gulong notify_online_id;
+       gulong revision_changed_id;
        guint refresh_timeout_id;
 
        gboolean refresh_after_authenticate;
@@ -3178,6 +3179,12 @@ e_cal_meta_backend_dispose (GObject *object)
                meta_backend->priv->notify_online_id = 0;
        }
 
+       if (meta_backend->priv->revision_changed_id) {
+               if (meta_backend->priv->cache)
+                       g_signal_handler_disconnect (meta_backend->priv->cache, 
meta_backend->priv->revision_changed_id);
+               meta_backend->priv->revision_changed_id = 0;
+       }
+
        g_hash_table_foreach (meta_backend->priv->view_cancellables, ecmb_cancel_view_cb, NULL);
 
        if (meta_backend->priv->refresh_cancellable) {
@@ -3482,6 +3489,24 @@ e_cal_meta_backend_get_connected_writable (ECalMetaBackend *meta_backend)
        return result;
 }
 
+static void
+ecmb_cache_revision_changed_cb (ECache *cache,
+                               gpointer user_data)
+{
+       ECalMetaBackend *meta_backend = user_data;
+       gchar *revision;
+
+       g_return_if_fail (E_IS_CACHE (cache));
+       g_return_if_fail (E_IS_CAL_META_BACKEND (meta_backend));
+
+       revision = e_cache_dup_revision (cache);
+       if (revision) {
+               e_cal_backend_notify_property_changed (E_CAL_BACKEND (meta_backend),
+                       CAL_BACKEND_PROPERTY_REVISION, revision);
+               g_free (revision);
+       }
+}
+
 /**
  * e_cal_meta_backend_set_cache:
  * @meta_backend: an #ECalMetaBackend
@@ -3512,9 +3537,17 @@ e_cal_meta_backend_set_cache (ECalMetaBackend *meta_backend,
 
        g_clear_error (&meta_backend->priv->create_cache_error);
 
+       if (meta_backend->priv->cache) {
+               g_signal_handler_disconnect (meta_backend->priv->cache,
+                       meta_backend->priv->revision_changed_id);
+       }
+
        g_clear_object (&meta_backend->priv->cache);
        meta_backend->priv->cache = g_object_ref (cache);
 
+       meta_backend->priv->revision_changed_id = g_signal_connect_object (meta_backend->priv->cache,
+               "revision-changed", G_CALLBACK (ecmb_cache_revision_changed_cb), meta_backend, 0);
+
        g_mutex_unlock (&meta_backend->priv->property_lock);
 
        g_object_notify (G_OBJECT (meta_backend), "cache");
diff --git a/src/libebackend/e-cache.c b/src/libebackend/e-cache.c
index 20d26bd..f35e0f0 100644
--- a/src/libebackend/e-cache.c
+++ b/src/libebackend/e-cache.c
@@ -64,11 +64,17 @@ struct _ECachePrivate {
        guint32 in_transaction;         /* Nested transaction counter */
        ECacheLockType lock_type;       /* The lock type acquired for the current transaction */
        GCancellable *cancellable;      /* User passed GCancellable, we abort an operation if cancelled */
+
+       guint32 revision_change_frozen;
+       gint revision_counter;
+       gint64 last_revision_time;
+       gboolean needs_revision_change;
 };
 
 enum {
        BEFORE_PUT,
        BEFORE_REMOVE,
+       REVISION_CHANGED,
        LAST_SIGNAL
 };
 
@@ -1042,8 +1048,9 @@ e_cache_dup_revision (ECache *cache)
  * @cache: an #ECache
  * @revision: (nullable): a revision to set; use %NULL to unset it
  *
- * Sets the @revision of the whole @cache. This is meant to be
- * used by the descendants.
+ * Sets the @revision of the whole @cache. This is not meant to be
+ * used by the descendants, because the revision is updated automatically
+ * when needed. The descendants can listen to "revision-changed" signal.
  *
  * Since: 3.26
  **/
@@ -1054,6 +1061,133 @@ e_cache_set_revision (ECache *cache,
        g_return_if_fail (E_IS_CACHE (cache));
 
        e_cache_set_key_internal (cache, FALSE, E_CACHE_KEY_REVISION, revision, NULL);
+
+       g_signal_emit (cache, signals[REVISION_CHANGED], 0, NULL);
+}
+
+/**
+ * e_cache_change_revision:
+ * @cache: an #ECache
+ *
+ * Instructs the @cache to change its revision. In case the revision
+ * change is frozen with e_cache_freeze_revision_change() it notes to
+ * change the revision once the revision change is fully thaw.
+ *
+ * Since: 3.26
+ **/
+void
+e_cache_change_revision (ECache *cache)
+{
+       g_return_if_fail (E_IS_CACHE (cache));
+
+       g_rec_mutex_lock (&cache->priv->lock);
+
+       if (e_cache_is_revision_change_frozen (cache)) {
+               cache->priv->needs_revision_change = TRUE;
+       } else {
+               gchar time_string[100] = { 0 };
+               const struct tm *tm = NULL;
+               time_t t;
+               gint64 revision_time;
+               gchar *revision;
+
+               revision_time = g_get_real_time () / (1000 * 1000);
+               t = (time_t) revision_time;
+
+               if (revision_time != cache->priv->last_revision_time) {
+                       cache->priv->revision_counter = 0;
+                       cache->priv->last_revision_time = revision_time;
+               }
+
+               tm = gmtime (&t);
+               if (tm)
+                       strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
+
+               revision = g_strdup_printf ("%s(%d)", time_string, cache->priv->revision_counter++);
+
+               e_cache_set_revision (cache, revision);
+
+               g_free (revision);
+       }
+
+       g_rec_mutex_unlock (&cache->priv->lock);
+}
+
+/**
+ * e_cache_freeze_revision_change:
+ * @cache: an #ECache
+ *
+ * Freezes automatic revision change for the @cache. The function
+ * can be called multiple times, but each such call requires its
+ * pair function e_cache_thaw_revision_change() call. See also
+ * e_cache_change_revision().
+ *
+ * Since: 3.26
+ **/
+void
+e_cache_freeze_revision_change (ECache *cache)
+{
+       g_return_if_fail (E_IS_CACHE (cache));
+
+       g_rec_mutex_lock (&cache->priv->lock);
+
+       cache->priv->revision_change_frozen++;
+       g_warn_if_fail (cache->priv->revision_change_frozen != 0);
+
+       g_rec_mutex_unlock (&cache->priv->lock);
+}
+
+/**
+ * e_cache_thaw_revision_change:
+ * @cache: an #ECache
+ *
+ * Thaws automatic revision change for the @cache. It's the pair
+ * function of e_cache_freeze_revision_change().
+ *
+ * Since: 3.26
+ **/
+void
+e_cache_thaw_revision_change (ECache *cache)
+{
+       g_return_if_fail (E_IS_CACHE (cache));
+
+       g_rec_mutex_lock (&cache->priv->lock);
+
+       if (!cache->priv->revision_change_frozen) {
+               g_warn_if_fail (cache->priv->revision_change_frozen > 0);
+       } else {
+               cache->priv->revision_change_frozen--;
+               if (!cache->priv->revision_change_frozen &&
+                   cache->priv->needs_revision_change) {
+                       cache->priv->needs_revision_change = FALSE;
+                       e_cache_change_revision (cache);
+               }
+       }
+
+       g_rec_mutex_unlock (&cache->priv->lock);
+}
+
+/**
+ * e_cache_is_revision_change_frozen:
+ * @cache: an #ECache
+ *
+ * Returns: Whether automatic revision change for the @cache
+ *    is currently frozen.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_cache_is_revision_change_frozen (ECache *cache)
+{
+       gboolean frozen;
+
+       g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
+
+       g_rec_mutex_lock (&cache->priv->lock);
+       frozen = cache->priv->revision_change_frozen > 0;
+       g_rec_mutex_unlock (&cache->priv->lock);
+
+       return frozen;
 }
 
 /**
@@ -1292,6 +1426,9 @@ e_cache_put_locked (ECache *cache,
                g_return_val_if_fail (klass->put_locked != NULL, FALSE);
 
                success = klass->put_locked (cache, uid, revision, object, other_columns, offline_state, 
is_replace, cancellable, error);
+
+               if (success)
+                       e_cache_change_revision (cache);
        }
 
        e_cache_column_values_free (my_other_columns);
@@ -1429,6 +1566,9 @@ e_cache_remove (ECache *cache,
                }
        }
 
+       if (success)
+               e_cache_change_revision (cache);
+
        e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
        return success;
@@ -1468,8 +1608,10 @@ e_cache_remove_all (ECache *cache,
        if (success && uids)
                success = klass->remove_all_locked (cache, uids, cancellable, error);
 
-       if (success)
+       if (success) {
                e_cache_sqlite_maybe_vacuum (cache, cancellable, NULL);
+               e_cache_change_revision (cache);
+       }
 
        e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
 
@@ -2894,6 +3036,17 @@ e_cache_class_init (ECacheClass *klass)
                G_TYPE_CANCELLABLE,
                G_TYPE_POINTER);
 
+       signals[REVISION_CHANGED] = g_signal_new (
+               "revision-changed",
+               G_OBJECT_CLASS_TYPE (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (ECacheClass, revision_changed),
+               NULL,
+               NULL,
+               g_cclosure_marshal_generic,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
        e_sqlite3_vfs_init ();
 }
 
@@ -2906,6 +3059,10 @@ e_cache_init (ECache *cache)
        cache->priv->db = NULL;
        cache->priv->cancellable = NULL;
        cache->priv->in_transaction = 0;
+       cache->priv->revision_change_frozen = 0;
+       cache->priv->revision_counter = 0;
+       cache->priv->last_revision_time = 0;
+       cache->priv->needs_revision_change = FALSE;
 
        g_rec_mutex_init (&cache->priv->lock);
 }
diff --git a/src/libebackend/e-cache.h b/src/libebackend/e-cache.h
index c8dac06..18b3ed7 100644
--- a/src/libebackend/e-cache.h
+++ b/src/libebackend/e-cache.h
@@ -374,6 +374,7 @@ struct _ECacheClass {
                                                 const gchar *uid,
                                                 GCancellable *cancellable,
                                                 GError **error);
+       void            (* revision_changed)    (ECache *cache);
 
        /* Padding for future expansion */
        gpointer reserved[10];
@@ -393,6 +394,11 @@ void               e_cache_set_version             (ECache *cache,
 gchar *                e_cache_dup_revision            (ECache *cache);
 void           e_cache_set_revision            (ECache *cache,
                                                 const gchar *revision);
+void           e_cache_change_revision         (ECache *cache);
+void           e_cache_freeze_revision_change  (ECache *cache);
+void           e_cache_thaw_revision_change    (ECache *cache);
+gboolean       e_cache_is_revision_change_frozen
+                                               (ECache *cache);
 void           e_cache_erase                   (ECache *cache);
 gboolean       e_cache_contains                (ECache *cache,
                                                 const gchar *uid,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]