[PATCH 1/2] lib: Replace freeze/thaw functionality with single sync function

Michael Forney mforney at mforney.org
Mon Dec 10 22:54:52 PST 2012


In all cases I could find, message tags were being modified with
freeze/thaw. This commit changes the default operation of
notmuch_message_{add_tag,remove_tag,remove_all_tags} to *not* commit
changes until notmuch_message_sync is called.
---
 bindings/go/src/notmuch/notmuch.go      |  69 ++++------------
 bindings/python/docs/source/message.rst |   4 +-
 bindings/python/notmuch/__init__.py     |   1 -
 bindings/python/notmuch/errors.py       |   7 --
 bindings/python/notmuch/message.py      | 139 ++++++++------------------------
 bindings/ruby/defs.h                    |   6 +-
 bindings/ruby/init.c                    |  12 +--
 bindings/ruby/message.c                 |  27 +------
 bindings/ruby/status.c                  |   2 -
 contrib/notmuch-deliver/src/main.c      |  10 +++
 lib/database.cc                         |  14 ++--
 lib/message.cc                          |  92 +++++----------------
 lib/notmuch-private.h                   |   7 +-
 lib/notmuch.h                           |  87 ++++++--------------
 notmuch-new.c                           |   4 +-
 notmuch-tag.c                           |   4 +-
 tag-util.c                              |  10 +--
 17 files changed, 123 insertions(+), 372 deletions(-)

diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go
index 00bd53a..56eb710 100644
--- a/bindings/go/src/notmuch/notmuch.go
+++ b/bindings/go/src/notmuch/notmuch.go
@@ -26,7 +26,6 @@ const (
 	STATUS_DUPLICATE_MESSAGE_ID
 	STATUS_NULL_POINTER
 	STATUS_TAG_TOO_LONG
-	STATUS_UNBALANCED_FREEZE_THAW
 	STATUS_UNBALANCED_ATOMIC
 
 	STATUS_LAST_STATUS
@@ -926,7 +925,7 @@ func (self *Message) RemoveTag(tag string) Status {
 
 /* Remove all tags from the given message.
  *
- * See notmuch_message_freeze for an example showing how to safely
+ * See notmuch_message_sync for an example showing how to safely
  * replace tag values.
  *
  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
@@ -939,79 +938,39 @@ func (self *Message) RemoveAllTags() Status {
 	return Status(C.notmuch_message_remove_all_tags(self.message))
 }
 
-/* Freeze the current state of 'message' within the database.
+/* Synchronize the current state of 'message' into the database.
  *
- * This means that changes to the message state, (via
+ * This will commit any changes made to the message state, (via
  * notmuch_message_add_tag, notmuch_message_remove_tag, and
- * notmuch_message_remove_all_tags), will not be committed to the
- * database until the message is thawed with notmuch_message_thaw.
+ * notmuch_message_remove_all_tags), to the database.
  *
- * Multiple calls to freeze/thaw are valid and these calls will
- * "stack". That is there must be as many calls to thaw as to freeze
- * before a message is actually thawed.
- *
- * The ability to do freeze/thaw allows for safe transactions to
- * change tag values. For example, explicitly setting a message to
- * have a given set of tags might look like this:
- *
- *    notmuch_message_freeze (message);
+ * If this method succeeds, the message in the database is guaranteed to
+ * have the full set of changes made to the message committed to the
+ * database. For example, explicitly setting a message to have a given
+ * set of tags might look like this:
  *
  *    notmuch_message_remove_all_tags (message);
  *
  *    for (i = 0; i < NUM_TAGS; i++)
  *        notmuch_message_add_tag (message, tags[i]);
  *
- *    notmuch_message_thaw (message);
+ *    notmuch_message_sync (message);
  *
- * With freeze/thaw used like this, the message in the database is
- * guaranteed to have either the full set of original tag values, or
- * the full set of new tag values, but nothing in between.
- *
- * Imagine the example above without freeze/thaw and the operation
- * somehow getting interrupted. This could result in the message being
- * left with no tags if the interruption happened after
- * notmuch_message_remove_all_tags but before notmuch_message_add_tag.
+ * This method only works if the database associated with 'message' was
+ * opened in read-write mode.
  *
  * Return value:
  *
- * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
+ * NOTMUCH_STATUS_SUCCESS: Message successfully synchronized.
  *
  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
  *	mode so message cannot be modified.
  */
-func (self *Message) Freeze() Status {
-	if self.message == nil {
-		return STATUS_NULL_POINTER
-	}
-	return Status(C.notmuch_message_freeze(self.message))
-}
-
-/* Thaw the current 'message', synchronizing any changes that may have
- * occurred while 'message' was frozen into the notmuch database.
- *
- * See notmuch_message_freeze for an example of how to use this
- * function to safely provide tag changes.
- *
- * Multiple calls to freeze/thaw are valid and these calls with
- * "stack". That is there must be as many calls to thaw as to freeze
- * before a message is actually thawed.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
- *	its frozen count has successfully been reduced by 1).
- *
- * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw
- *	an unfrozen message. That is, there have been an unbalanced
- *	number of calls to notmuch_message_freeze and
- *	notmuch_message_thaw.
- */
-func (self *Message) Thaw() Status {
+func (self *Message) Sync() Status {
 	if self.message == nil {
 		return STATUS_NULL_POINTER
 	}
-
-	return Status(C.notmuch_message_thaw(self.message))
+	return Status(C.notmuch_message_sync(self.message))
 }
 
 /* Destroy a notmuch_message_t object.
diff --git a/bindings/python/docs/source/message.rst b/bindings/python/docs/source/message.rst
index 1a6cc3d..d3ada71 100644
--- a/bindings/python/docs/source/message.rst
+++ b/bindings/python/docs/source/message.rst
@@ -43,8 +43,6 @@
 
    .. automethod:: remove_all_tags
 
-   .. automethod:: freeze
-
-   .. automethod:: thaw
+   .. automethod:: sync
 
    .. automethod:: __str__
diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py
index 5561624..edd7cbd 100644
--- a/bindings/python/notmuch/__init__.py
+++ b/bindings/python/notmuch/__init__.py
@@ -72,7 +72,6 @@ from .errors import (
     DuplicateMessageIdError,
     NullPointerError,
     TagTooLongError,
-    UnbalancedFreezeThawError,
     UnbalancedAtomicError,
     NotInitializedError,
 )
diff --git a/bindings/python/notmuch/errors.py b/bindings/python/notmuch/errors.py
index f153a9c..cb748d6 100644
--- a/bindings/python/notmuch/errors.py
+++ b/bindings/python/notmuch/errors.py
@@ -54,7 +54,6 @@ STATUS = Status(['SUCCESS',
   'DUPLICATE_MESSAGE_ID',
   'NULL_POINTER',
   'TAG_TOO_LONG',
-  'UNBALANCED_FREEZE_THAW',
   'UNBALANCED_ATOMIC',
   'NOT_INITIALIZED'])
 """STATUS is a class, whose attributes provide constants that serve as return
@@ -71,7 +70,6 @@ description.
   * DUPLICATE_MESSAGE_ID
   * NULL_POINTER
   * TAG_TOO_LONG
-  * UNBALANCED_FREEZE_THAW
   * UNBALANCED_ATOMIC
   * NOT_INITIALIZED
 
@@ -99,7 +97,6 @@ class NotmuchError(Exception, Python3StringMixIn):
             STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError,
             STATUS.NULL_POINTER: NullPointerError,
             STATUS.TAG_TOO_LONG: TagTooLongError,
-            STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
             STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
             STATUS.NOT_INITIALIZED: NotInitializedError,
         }
@@ -167,10 +164,6 @@ class TagTooLongError(NotmuchError):
     status = STATUS.TAG_TOO_LONG
 
 
-class UnbalancedFreezeThawError(NotmuchError):
-    status = STATUS.UNBALANCED_FREEZE_THAW
-
-
 class UnbalancedAtomicError(NotmuchError):
     status = STATUS.UNBALANCED_ATOMIC
 
diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index d1c1b58..600c01e 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -308,7 +308,7 @@ class Message(Python3StringMixIn):
     _add_tag.argtypes = [NotmuchMessageP, c_char_p]
     _add_tag.restype = c_uint
 
-    def add_tag(self, tag, sync_maildir_flags=False):
+    def add_tag(self, tag):
         """Adds a tag to the given message
 
         Adds a tag to the current message. The maximal tag length is defined in
@@ -316,14 +316,6 @@ class Message(Python3StringMixIn):
 
         :param tag: String with a 'tag' to be added.
 
-        :param sync_maildir_flags: If notmuch configuration is set to do
-            this, add maildir flags corresponding to notmuch tags. See
-            underlying method :meth:`tags_to_maildir_flags`. Use False
-            if you want to add/remove many tags on a message without
-            having to physically rename the file every time. Do note,
-            that this will do nothing when a message is frozen, as tag
-            changes will not be committed to the database yet.
-
         :returns: STATUS.SUCCESS if the tag was successfully added.
                   Raises an exception otherwise.
         :raises: :exc:`NullPointerError` if the `tag` argument is NULL
@@ -342,29 +334,19 @@ class Message(Python3StringMixIn):
         # bail out on failure
         if status != STATUS.SUCCESS:
             raise NotmuchError(status)
-
-        if sync_maildir_flags:
-            self.tags_to_maildir_flags()
         return STATUS.SUCCESS
 
     _remove_tag = nmlib.notmuch_message_remove_tag
     _remove_tag.argtypes = [NotmuchMessageP, c_char_p]
     _remove_tag.restype = c_uint
 
-    def remove_tag(self, tag, sync_maildir_flags=False):
+    def remove_tag(self, tag):
         """Removes a tag from the given message
 
         If the message has no such tag, this is a non-operation and
         will report success anyway.
 
         :param tag: String with a 'tag' to be removed.
-        :param sync_maildir_flags: If notmuch configuration is set to do
-            this, add maildir flags corresponding to notmuch tags. See
-            underlying method :meth:`tags_to_maildir_flags`. Use False
-            if you want to add/remove many tags on a message without
-            having to physically rename the file every time. Do note,
-            that this will do nothing when a message is frozen, as tag
-            changes will not be committed to the database yet.
 
         :returns: STATUS.SUCCESS if the tag was successfully removed or if
                   the message had no such tag.
@@ -385,29 +367,18 @@ class Message(Python3StringMixIn):
         if status != STATUS.SUCCESS:
             raise NotmuchError(status)
 
-        if sync_maildir_flags:
-            self.tags_to_maildir_flags()
         return STATUS.SUCCESS
 
     _remove_all_tags = nmlib.notmuch_message_remove_all_tags
     _remove_all_tags.argtypes = [NotmuchMessageP]
     _remove_all_tags.restype = c_uint
 
-    def remove_all_tags(self, sync_maildir_flags=False):
+    def remove_all_tags(self):
         """Removes all tags from the given message.
 
-        See :meth:`freeze` for an example showing how to safely
+        See :meth:`sync` for an example showing how to safely
         replace tag values.
 
-
-        :param sync_maildir_flags: If notmuch configuration is set to do
-            this, add maildir flags corresponding to notmuch tags. See
-            :meth:`tags_to_maildir_flags`. Use False if you want to
-            add/remove many tags on a message without having to
-            physically rename the file every time. Do note, that this
-            will do nothing when a message is frozen, as tag changes
-            will not be committed to the database yet.
-
         :returns: STATUS.SUCCESS if the tags were successfully removed.
                   Raises an exception otherwise.
         :raises: :exc:`ReadOnlyDatabaseError` if the database was opened
@@ -424,46 +395,40 @@ class Message(Python3StringMixIn):
         if status != STATUS.SUCCESS:
             raise NotmuchError(status)
 
-        if sync_maildir_flags:
-            self.tags_to_maildir_flags()
         return STATUS.SUCCESS
 
-    _freeze = nmlib.notmuch_message_freeze
-    _freeze.argtypes = [NotmuchMessageP]
-    _freeze.restype = c_uint
-
-    def freeze(self):
-        """Freezes the current state of 'message' within the database
+    _sync = nmlib.notmuch_message_sync
+    _sync.argtypes = [NotmuchMessageP]
+    _sync.restype = c_uint
 
-        This means that changes to the message state, (via :meth:`add_tag`,
-        :meth:`remove_tag`, and :meth:`remove_all_tags`), will not be
-        committed to the database until the message is :meth:`thaw` ed.
+    def sync(self, sync_maildir_flags=False):
+        """Synchronize the current state of 'message' into the database.
 
-        Multiple calls to freeze/thaw are valid and these calls will
-        "stack". That is there must be as many calls to thaw as to freeze
-        before a message is actually thawed.
+        This will commit any changes made to the message state, (via
+        :meth:`add_tag`, :meth:`remove_tag`, and :meth:`remove_all_tags`), to
+        the database.
 
-        The ability to do freeze/thaw allows for safe transactions to
-        change tag values. For example, explicitly setting a message to
-        have a given set of tags might look like this::
+        If this method succeeds, the message in the database is guaranteed to
+        have the full set of changes made to the message committed to the
+        database. For example, explicitly setting a message to have a given set
+        of tags might look like this:
 
-          msg.freeze()
           msg.remove_all_tags(False)
           for tag in new_tags:
               msg.add_tag(tag, False)
-          msg.thaw()
+          msg.sync()
           msg.tags_to_maildir_flags()
 
-        With freeze/thaw used like this, the message in the database is
-        guaranteed to have either the full set of original tag values, or
-        the full set of new tag values, but nothing in between.
+        This method only works if the database associated with 'message' was
+        opened with NOTMUCH_DATABASE_MODE_READ_WRITE.
 
-        Imagine the example above without freeze/thaw and the operation
-        somehow getting interrupted. This could result in the message being
-        left with no tags if the interruption happened after
-        :meth:`remove_all_tags` but before :meth:`add_tag`.
+        :param sync_maildir_flags: If notmuch configuration is set to do
+            this, add maildir flags corresponding to notmuch tags. See
+            :meth:`tags_to_maildir_flags`. Use False if you want to
+            add/remove many tags on a message without having to
+            physically rename the file every time.
 
-        :returns: STATUS.SUCCESS if the message was successfully frozen.
+        :returns: STATUS.SUCCESS if the message was successfully synchronized.
                   Raises an exception otherwise.
         :raises: :exc:`ReadOnlyDatabaseError` if the database was opened
                  in read-only mode so message cannot be modified
@@ -473,50 +438,14 @@ class Message(Python3StringMixIn):
         if not self._msg:
             raise NotInitializedError()
 
-        status = self._freeze(self._msg)
-
-        if STATUS.SUCCESS == status:
-            # return on success
-            return status
-
-        raise NotmuchError(status)
-
-    _thaw = nmlib.notmuch_message_thaw
-    _thaw.argtypes = [NotmuchMessageP]
-    _thaw.restype = c_uint
-
-    def thaw(self):
-        """Thaws the current 'message'
+        status = self._sync(self._msg)
 
-        Thaw the current 'message', synchronizing any changes that may have
-        occurred while 'message' was frozen into the notmuch database.
-
-        See :meth:`freeze` for an example of how to use this
-        function to safely provide tag changes.
-
-        Multiple calls to freeze/thaw are valid and these calls with
-        "stack". That is there must be as many calls to thaw as to freeze
-        before a message is actually thawed.
-
-        :returns: STATUS.SUCCESS if the message was successfully frozen.
-                  Raises an exception otherwise.
-        :raises: :exc:`UnbalancedFreezeThawError` if an attempt was made
-                 to thaw an unfrozen message. That is, there have been
-                 an unbalanced number of calls to :meth:`freeze` and
-                 :meth:`thaw`.
-        :raises: :exc:`NotInitializedError` if message has not been
-                 initialized
-        """
-        if not self._msg:
-            raise NotInitializedError()
-
-        status = self._thaw(self._msg)
-
-        if STATUS.SUCCESS == status:
-            # return on success
-            return status
+        if status != STATUS.SUCCESS:
+            raise NotmuchError(status)
 
-        raise NotmuchError(status)
+        if sync_maildir_flags:
+            self.tags_to_maildir_flags()
+        raise STATUS.SUCCESS
 
     def is_match(self):
         """(Not implemented)"""
@@ -537,9 +466,9 @@ class Message(Python3StringMixIn):
         Also, if this filename is in a directory named "new", rename it
         to be within the neighboring directory named "cur".
 
-        Do note that calling this method while a message is frozen might
-        not work yet, as the modified tags have not been committed yet
-        to the database.
+        Do note that calling this method before :meth:`sync` is called
+        might not work yet, as the modified tags have not been committed
+        yet to the database.
 
         :returns: a :class:`STATUS` value. In short, you want to see
             notmuch.STATUS.SUCCESS here. See there for details."""
diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h
index fe81b3f..de36936 100644
--- a/bindings/ruby/defs.h
+++ b/bindings/ruby/defs.h
@@ -43,7 +43,6 @@ extern VALUE notmuch_rb_eFileError;
 extern VALUE notmuch_rb_eFileNotEmailError;
 extern VALUE notmuch_rb_eNullPointerError;
 extern VALUE notmuch_rb_eTagTooLongError;
-extern VALUE notmuch_rb_eUnbalancedFreezeThawError;
 extern VALUE notmuch_rb_eUnbalancedAtomicError;
 
 extern ID ID_call;
@@ -329,10 +328,7 @@ VALUE
 notmuch_rb_message_tags_to_maildir_flags (VALUE self);
 
 VALUE
-notmuch_rb_message_freeze (VALUE self);
-
-VALUE
-notmuch_rb_message_thaw (VALUE self);
+notmuch_rb_message_sync (VALUE self);
 
 /* tags.c */
 VALUE
diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c
index f4931d3..b7a7456 100644
--- a/bindings/ruby/init.c
+++ b/bindings/ruby/init.c
@@ -39,7 +39,6 @@ VALUE notmuch_rb_eFileError;
 VALUE notmuch_rb_eFileNotEmailError;
 VALUE notmuch_rb_eNullPointerError;
 VALUE notmuch_rb_eTagTooLongError;
-VALUE notmuch_rb_eUnbalancedFreezeThawError;
 VALUE notmuch_rb_eUnbalancedAtomicError;
 
 ID ID_call;
@@ -191,14 +190,6 @@ Init_notmuch (void)
      */
     notmuch_rb_eTagTooLongError = rb_define_class_under (mod, "TagTooLongError", notmuch_rb_eBaseError);
     /*
-     * Document-class: Notmuch::UnbalancedFreezeThawError
-     *
-     * Raised when the notmuch_message_thaw function has been called more times
-     * than notmuch_message_freeze.
-     */
-    notmuch_rb_eUnbalancedFreezeThawError = rb_define_class_under (mod, "UnbalancedFreezeThawError",
-								   notmuch_rb_eBaseError);
-    /*
      * Document-class: Notmuch::UnbalancedAtomicError
      *
      * Raised when notmuch_database_end_atomic has been called more times than
@@ -338,8 +329,7 @@ Init_notmuch (void)
     rb_define_method (notmuch_rb_cMessage, "remove_all_tags", notmuch_rb_message_remove_all_tags, 0); /* in message.c */
     rb_define_method (notmuch_rb_cMessage, "maildir_flags_to_tags", notmuch_rb_message_maildir_flags_to_tags, 0); /* in message.c */
     rb_define_method (notmuch_rb_cMessage, "tags_to_maildir_flags", notmuch_rb_message_tags_to_maildir_flags, 0); /* in message.c */
-    rb_define_method (notmuch_rb_cMessage, "freeze", notmuch_rb_message_freeze, 0); /* in message.c */
-    rb_define_method (notmuch_rb_cMessage, "thaw", notmuch_rb_message_thaw, 0); /* in message.c */
+    rb_define_method (notmuch_rb_cMessage, "sync", notmuch_rb_message_sync, 0); /* in message.c */
 
     /*
      * Document-class: Notmuch::Tags
diff --git a/bindings/ruby/message.c b/bindings/ruby/message.c
index eed4b31..719ca16 100644
--- a/bindings/ruby/message.c
+++ b/bindings/ruby/message.c
@@ -328,38 +328,19 @@ notmuch_rb_message_tags_to_maildir_flags (VALUE self)
 }
 
 /*
- * call-seq: MESSAGE.freeze => true
+ * call-seq: MESSAGE.sync => true
  *
- * Freeze the 'message'
+ * Sync the 'message' to the database
  */
 VALUE
-notmuch_rb_message_freeze (VALUE self)
+notmuch_rb_message_sync (VALUE self)
 {
     notmuch_status_t ret;
     notmuch_message_t *message;
 
     Data_Get_Notmuch_Message (self, message);
 
-    ret = notmuch_message_freeze (message);
-    notmuch_rb_status_raise (ret);
-
-    return Qtrue;
-}
-
-/*
- * call-seq: MESSAGE.thaw => true
- *
- * Thaw a 'message'
- */
-VALUE
-notmuch_rb_message_thaw (VALUE self)
-{
-    notmuch_status_t ret;
-    notmuch_message_t *message;
-
-    Data_Get_Notmuch_Message (self, message);
-
-    ret = notmuch_message_thaw (message);
+    ret = notmuch_message_sync (message);
     notmuch_rb_status_raise (ret);
 
     return Qtrue;
diff --git a/bindings/ruby/status.c b/bindings/ruby/status.c
index b11fb9f..8d1390a 100644
--- a/bindings/ruby/status.c
+++ b/bindings/ruby/status.c
@@ -41,8 +41,6 @@ notmuch_rb_status_raise (notmuch_status_t status)
 	rb_raise (notmuch_rb_eNullPointerError, "null pointer");
     case NOTMUCH_STATUS_TAG_TOO_LONG:
 	rb_raise (notmuch_rb_eTagTooLongError, "tag too long");
-    case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
-	rb_raise (notmuch_rb_eUnbalancedFreezeThawError, "unbalanced freeze/thaw");
     case NOTMUCH_STATUS_UNBALANCED_ATOMIC:
 	rb_raise (notmuch_rb_eUnbalancedAtomicError, "unbalanced atomic");
     default:
diff --git a/contrib/notmuch-deliver/src/main.c b/contrib/notmuch-deliver/src/main.c
index 032b9d6..afe84ff 100644
--- a/contrib/notmuch-deliver/src/main.c
+++ b/contrib/notmuch-deliver/src/main.c
@@ -300,6 +300,11 @@ add_tags(notmuch_message_t *message, char **tags)
 				tags[i], notmuch_status_to_string(ret));
 	}
 
+	ret = notmuch_message_sync(message);
+	if (ret != NOTMUCH_STATUS_SUCCESS)
+		g_warning("Failed to sync changes to database: %s",
+			notmuch_status_to_string(ret));
+
 	return i;
 }
 
@@ -319,6 +324,11 @@ rm_tags(notmuch_message_t *message, char **tags)
 				tags[i], notmuch_status_to_string(ret));
 	}
 
+	ret = notmuch_message_sync(message);
+	if (ret != NOTMUCH_STATUS_SUCCESS)
+		g_warning("Failed to sync changes to database: %s",
+			notmuch_status_to_string(ret));
+
 	return i;
 }
 
diff --git a/lib/database.cc b/lib/database.cc
index 91d4329..98ea789 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -264,8 +264,6 @@ notmuch_status_to_string (notmuch_status_t status)
 	return "Erroneous NULL pointer";
     case NOTMUCH_STATUS_TAG_TOO_LONG:
 	return "Tag value is too long (exceeds NOTMUCH_TAG_MAX)";
-    case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
-	return "Unbalanced number of calls to notmuch_message_freeze/thaw";
     case NOTMUCH_STATUS_UNBALANCED_ATOMIC:
 	return "Unbalanced number of calls to notmuch_database_begin_atomic/end_atomic";
     default:
@@ -917,7 +915,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
 	    filename = _notmuch_message_talloc_copy_data (message);
 	    if (filename && *filename != '\0') {
 		_notmuch_message_add_filename (message, filename);
-		_notmuch_message_sync (message);
+		notmuch_message_sync (message);
 	    }
 	    talloc_free (filename);
 
@@ -993,7 +991,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
 	    filename = _notmuch_message_talloc_copy_data (message);
 	    if (filename && *filename != '\0') {
 		_notmuch_message_clear_data (message);
-		_notmuch_message_sync (message);
+		notmuch_message_sync (message);
 	    }
 	    talloc_free (filename);
 
@@ -1483,7 +1481,7 @@ _merge_threads (notmuch_database_t *notmuch,
 
 	_notmuch_message_remove_term (message, "thread", loser_thread_id);
 	_notmuch_message_add_term (message, "thread", winner_thread_id);
-	_notmuch_message_sync (message);
+	notmuch_message_sync (message);
 
 	notmuch_message_destroy (message);
 	message = NULL;
@@ -1601,7 +1599,7 @@ _notmuch_database_link_message_to_children (notmuch_database_t *notmuch,
 	} else if (strcmp (*thread_id, child_thread_id)) {
 	    _notmuch_message_remove_term (child_message, "reference",
 					  message_id);
-	    _notmuch_message_sync (child_message);
+	    notmuch_message_sync (child_message);
 	    ret = _merge_threads (notmuch, *thread_id, child_thread_id);
 	    if (ret)
 		goto DONE;
@@ -1828,7 +1826,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch,
 	    ret = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
 	}
 
-	_notmuch_message_sync (message);
+	notmuch_message_sync (message);
     } catch (const Xapian::Error &error) {
 	fprintf (stderr, "A Xapian exception occurred adding message: %s.\n",
 		 error.get_msg().c_str());
@@ -1873,7 +1871,7 @@ notmuch_database_remove_message (notmuch_database_t *notmuch,
 	    if (status == NOTMUCH_STATUS_SUCCESS)
 		_notmuch_message_delete (message);
 	    else if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
-		_notmuch_message_sync (message);
+		notmuch_message_sync (message);
 
 	    notmuch_message_destroy (message);
     }
diff --git a/lib/message.cc b/lib/message.cc
index 320901f..b7e4bbe 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -28,7 +28,6 @@
 struct visible _notmuch_message {
     notmuch_database_t *notmuch;
     Xapian::docid doc_id;
-    int frozen;
     char *message_id;
     char *thread_id;
     char *in_reply_to;
@@ -97,7 +96,6 @@ _notmuch_message_create_for_document (const void *talloc_owner,
     message->notmuch = notmuch;
     message->doc_id = doc_id;
 
-    message->frozen = 0;
     message->flags = 0;
 
     /* Each of these will be lazily created as needed. */
@@ -199,7 +197,7 @@ _notmuch_message_create (const void *talloc_owner,
  *     returned message contains a newly created document (not yet
  *     added to the database) and a document ID that is known not to
  *     exist in the database. The caller can modify the message, and a
- *     call to _notmuch_message_sync will add * the document to the
+ *     call to notmuch_message_sync will add the document to the
  *     database.
  *
  * If an error occurs, this function will return NULL and *status
@@ -476,7 +474,7 @@ notmuch_message_get_replies (notmuch_message_t *message)
 /* Add an additional 'filename' for 'message'.
  *
  * This change will not be reflected in the database until the next
- * call to _notmuch_message_sync. */
+ * call to notmuch_message_sync. */
 notmuch_status_t
 _notmuch_message_add_filename (notmuch_message_t *message,
 			       const char *filename)
@@ -515,7 +513,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,
 /* Remove a particular 'filename' from 'message'.
  *
  * This change will not be reflected in the database until the next
- * call to _notmuch_message_sync.
+ * call to notmuch_message_sync.
  *
  * If this message still has other filenames, returns
  * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID.
@@ -831,17 +829,22 @@ _notmuch_message_set_header_values (notmuch_message_t *message,
     message->doc.add_value (NOTMUCH_VALUE_SUBJECT, subject);
 }
 
-/* Synchronize changes made to message->doc out into the database. */
-void
-_notmuch_message_sync (notmuch_message_t *message)
+/* Synchronize changes made to message->doc into the given database. */
+notmuch_status_t
+_notmuch_message_sync_to_database (notmuch_message_t *message,
+				   notmuch_database_t *notmuch)
 {
+    notmuch_status_t status;
     Xapian::WritableDatabase *db;
 
-    if (message->notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY)
-	return;
+    status = _notmuch_database_ensure_writable(notmuch);
+    if (status)
+	return status;
 
-    db = static_cast <Xapian::WritableDatabase *> (message->notmuch->xapian_db);
+    db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
     db->replace_document (message->doc_id, message->doc);
+
+    return NOTMUCH_STATUS_SUCCESS;
 }
 
 /* Delete a message document from the database. */
@@ -879,7 +882,7 @@ _notmuch_message_close (notmuch_message_t *message)
  * names to prefix values.
  *
  * This change will not be reflected in the database until the next
- * call to _notmuch_message_sync. */
+ * call to notmuch_message_sync. */
 notmuch_private_status_t
 _notmuch_message_add_term (notmuch_message_t *message,
 			   const char *prefix_name,
@@ -940,7 +943,7 @@ _notmuch_message_gen_terms (notmuch_message_t *message,
  * names to prefix values.
  *
  * This change will not be reflected in the database until the next
- * call to _notmuch_message_sync. */
+ * call to notmuch_message_sync. */
 notmuch_private_status_t
 _notmuch_message_remove_term (notmuch_message_t *message,
 			      const char *prefix_name,
@@ -977,11 +980,6 @@ notmuch_status_t
 notmuch_message_add_tag (notmuch_message_t *message, const char *tag)
 {
     notmuch_private_status_t private_status;
-    notmuch_status_t status;
-
-    status = _notmuch_database_ensure_writable (message->notmuch);
-    if (status)
-	return status;
 
     if (tag == NULL)
 	return NOTMUCH_STATUS_NULL_POINTER;
@@ -995,9 +993,6 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag)
 			private_status);
     }
 
-    if (! message->frozen)
-	_notmuch_message_sync (message);
-
     return NOTMUCH_STATUS_SUCCESS;
 }
 
@@ -1005,11 +1000,6 @@ notmuch_status_t
 notmuch_message_remove_tag (notmuch_message_t *message, const char *tag)
 {
     notmuch_private_status_t private_status;
-    notmuch_status_t status;
-
-    status = _notmuch_database_ensure_writable (message->notmuch);
-    if (status)
-	return status;
 
     if (tag == NULL)
 	return NOTMUCH_STATUS_NULL_POINTER;
@@ -1023,9 +1013,6 @@ notmuch_message_remove_tag (notmuch_message_t *message, const char *tag)
 			private_status);
     }
 
-    if (! message->frozen)
-	_notmuch_message_sync (message);
-
     return NOTMUCH_STATUS_SUCCESS;
 }
 
@@ -1113,10 +1100,6 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
     if (! seen_maildir_info)
 	return NOTMUCH_STATUS_SUCCESS;
 
-    status = notmuch_message_freeze (message);
-    if (status)
-	return status;
-
     for (i = 0; i < ARRAY_SIZE(flag2tag); i++) {
 	if ((strchr (combined_flags, flag2tag[i].flag) != NULL)
 	    ^ 
@@ -1129,7 +1112,7 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message)
 	if (status)
 	    return status;
     }
-    status = notmuch_message_thaw (message);
+    status = notmuch_message_sync (message);
 
     talloc_free (combined_flags);
 
@@ -1348,7 +1331,7 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)
 		continue;
 	    }
 
-	    _notmuch_message_sync (message);
+	    notmuch_message_sync (message);
 	}
 
 	talloc_free (filename_new);
@@ -1364,14 +1347,9 @@ notmuch_status_t
 notmuch_message_remove_all_tags (notmuch_message_t *message)
 {
     notmuch_private_status_t private_status;
-    notmuch_status_t status;
     notmuch_tags_t *tags;
     const char *tag;
 
-    status = _notmuch_database_ensure_writable (message->notmuch);
-    if (status)
-	return status;
-
     for (tags = notmuch_message_get_tags (message);
 	 notmuch_tags_valid (tags);
 	 notmuch_tags_move_to_next (tags))
@@ -1385,44 +1363,14 @@ notmuch_message_remove_all_tags (notmuch_message_t *message)
 	}
     }
 
-    if (! message->frozen)
-	_notmuch_message_sync (message);
-
     talloc_free (tags);
     return NOTMUCH_STATUS_SUCCESS;
 }
 
 notmuch_status_t
-notmuch_message_freeze (notmuch_message_t *message)
+notmuch_message_sync (notmuch_message_t *message)
 {
-    notmuch_status_t status;
-
-    status = _notmuch_database_ensure_writable (message->notmuch);
-    if (status)
-	return status;
-
-    message->frozen++;
-
-    return NOTMUCH_STATUS_SUCCESS;
-}
-
-notmuch_status_t
-notmuch_message_thaw (notmuch_message_t *message)
-{
-    notmuch_status_t status;
-
-    status = _notmuch_database_ensure_writable (message->notmuch);
-    if (status)
-	return status;
-
-    if (message->frozen > 0) {
-	message->frozen--;
-	if (message->frozen == 0)
-	    _notmuch_message_sync (message);
-	return NOTMUCH_STATUS_SUCCESS;
-    } else {
-	return NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW;
-    }
+    return _notmuch_message_sync_to_database (message, message->notmuch);
 }
 
 void
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index 7a409f5..9a43129 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -122,7 +122,6 @@ typedef enum _notmuch_private_status {
     NOTMUCH_PRIVATE_STATUS_FILE_NOT_EMAIL = NOTMUCH_STATUS_FILE_NOT_EMAIL,
     NOTMUCH_PRIVATE_STATUS_NULL_POINTER = NOTMUCH_STATUS_NULL_POINTER,
     NOTMUCH_PRIVATE_STATUS_TAG_TOO_LONG = NOTMUCH_STATUS_TAG_TOO_LONG,
-    NOTMUCH_PRIVATE_STATUS_UNBALANCED_FREEZE_THAW = NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW,
 
     /* Then add our own private values. */
     NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG = NOTMUCH_STATUS_LAST_STATUS,
@@ -295,8 +294,10 @@ _notmuch_message_set_header_values (notmuch_message_t *message,
 				    const char *date,
 				    const char *from,
 				    const char *subject);
-void
-_notmuch_message_sync (notmuch_message_t *message);
+
+notmuch_status_t
+_notmuch_message_sync_to_database (notmuch_message_t *message,
+				   notmuch_database_t *notmuch);
 
 notmuch_status_t
 _notmuch_message_delete (notmuch_message_t *message);
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 3633bed..f6962ee 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -78,9 +78,6 @@ typedef int notmuch_bool_t;
  * NOTMUCH_STATUS_TAG_TOO_LONG: A tag value is too long (exceeds
  *	NOTMUCH_TAG_MAX)
  *
- * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: The notmuch_message_thaw
- *	function has been called more times than notmuch_message_freeze.
- *
  * NOTMUCH_STATUS_UNBALANCED_ATOMIC: notmuch_database_end_atomic has
  *	been called more times than notmuch_database_begin_atomic.
  *
@@ -99,7 +96,6 @@ typedef enum _notmuch_status {
     NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID,
     NOTMUCH_STATUS_NULL_POINTER,
     NOTMUCH_STATUS_TAG_TOO_LONG,
-    NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW,
     NOTMUCH_STATUS_UNBALANCED_ATOMIC,
 
     NOTMUCH_STATUS_LAST_STATUS
@@ -1043,6 +1039,9 @@ notmuch_message_get_tags (notmuch_message_t *message);
 
 /* Add a tag to the given message.
  *
+ * The changes to the message will not be committed into the database
+ * until notmuch_message_sync is called.
+ *
  * Return value:
  *
  * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
@@ -1051,15 +1050,15 @@ notmuch_message_get_tags (notmuch_message_t *message);
  *
  * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
  *	(exceeds NOTMUCH_TAG_MAX)
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *	mode so message cannot be modified.
  */
 notmuch_status_t
 notmuch_message_add_tag (notmuch_message_t *message, const char *tag);
 
 /* Remove a tag from the given message.
  *
+ * The changes to the message will not be committed into the database
+ * until notmuch_message_sync is called.
+ *
  * Return value:
  *
  * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
@@ -1068,17 +1067,14 @@ notmuch_message_add_tag (notmuch_message_t *message, const char *tag);
  *
  * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
  *	(exceeds NOTMUCH_TAG_MAX)
- *
- * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
- *	mode so message cannot be modified.
  */
 notmuch_status_t
 notmuch_message_remove_tag (notmuch_message_t *message, const char *tag);
 
 /* Remove all tags from the given message.
  *
- * See notmuch_message_freeze for an example showing how to safely
- * replace tag values.
+ * See notmuch_message_sync for an example showing how to safely replace
+ * tag values.
  *
  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
  *	mode so message cannot be modified.
@@ -1148,79 +1144,44 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message);
  *
  * A client can ensure that maildir filename flags remain synchronized
  * with notmuch database tags by calling this function after changing
- * tags, (after calls to notmuch_message_add_tag,
- * notmuch_message_remove_tag, or notmuch_message_freeze/
- * notmuch_message_thaw). See also notmuch_message_maildir_flags_to_tags
- * for synchronizing maildir flag changes back to tags.
+ * tags, (after calls to notmuch_message_sync).
+ *
+ * See also notmuch_message_maildir_flags_to_tags for synchronizing
+ * maildir flag changes back to tags.
  */
 notmuch_status_t
 notmuch_message_tags_to_maildir_flags (notmuch_message_t *message);
 
-/* Freeze the current state of 'message' within the database.
+/* Synchronize the current state of 'message' into the database.
  *
- * This means that changes to the message state, (via
+ * This will commit any changes made to the message state, (via
  * notmuch_message_add_tag, notmuch_message_remove_tag, and
- * notmuch_message_remove_all_tags), will not be committed to the
- * database until the message is thawed with notmuch_message_thaw.
- *
- * Multiple calls to freeze/thaw are valid and these calls will
- * "stack". That is there must be as many calls to thaw as to freeze
- * before a message is actually thawed.
+ * notmuch_message_remove_all_tags), to the database.
  *
- * The ability to do freeze/thaw allows for safe transactions to
- * change tag values. For example, explicitly setting a message to
- * have a given set of tags might look like this:
- *
- *    notmuch_message_freeze (message);
+ * If this method succeeds, the message in the database is guaranteed to
+ * have the full set of changes made to the message committed to the
+ * database. For example, explicitly setting a message to have a given
+ * set of tags might look like this:
  *
  *    notmuch_message_remove_all_tags (message);
  *
  *    for (i = 0; i < NUM_TAGS; i++)
  *        notmuch_message_add_tag (message, tags[i]);
  *
- *    notmuch_message_thaw (message);
- *
- * With freeze/thaw used like this, the message in the database is
- * guaranteed to have either the full set of original tag values, or
- * the full set of new tag values, but nothing in between.
+ *    notmuch_message_sync (message);
  *
- * Imagine the example above without freeze/thaw and the operation
- * somehow getting interrupted. This could result in the message being
- * left with no tags if the interruption happened after
- * notmuch_message_remove_all_tags but before notmuch_message_add_tag.
+ * This method only works if the database associated with 'message' was
+ * opened in read-write mode.
  *
  * Return value:
  *
- * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
+ * NOTMUCH_STATUS_SUCCESS: Message successfully synchronized.
  *
  * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
  *	mode so message cannot be modified.
  */
 notmuch_status_t
-notmuch_message_freeze (notmuch_message_t *message);
-
-/* Thaw the current 'message', synchronizing any changes that may have
- * occurred while 'message' was frozen into the notmuch database.
- *
- * See notmuch_message_freeze for an example of how to use this
- * function to safely provide tag changes.
- *
- * Multiple calls to freeze/thaw are valid and these calls with
- * "stack". That is there must be as many calls to thaw as to freeze
- * before a message is actually thawed.
- *
- * Return value:
- *
- * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
- *	its frozen count has successfully been reduced by 1).
- *
- * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw
- *	an unfrozen message. That is, there have been an unbalanced
- *	number of calls to notmuch_message_freeze and
- *	notmuch_message_thaw.
- */
-notmuch_status_t
-notmuch_message_thaw (notmuch_message_t *message);
+notmuch_message_sync (notmuch_message_t *message);
 
 /* Destroy a notmuch_message_t object.
  *
diff --git a/notmuch-new.c b/notmuch-new.c
index feb9c32..644a809 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -509,12 +509,11 @@ add_files (notmuch_database_t *notmuch,
 	/* success */
 	case NOTMUCH_STATUS_SUCCESS:
 	    state->added_messages++;
-	    notmuch_message_freeze (message);
 	    for (tag=state->new_tags; *tag != NULL; tag++)
 	        notmuch_message_add_tag (message, *tag);
 	    if (state->synchronize_flags == TRUE)
 		notmuch_message_maildir_flags_to_tags (message);
-	    notmuch_message_thaw (message);
+	    notmuch_message_sync (message);
 	    break;
 	/* Non-fatal issues (go on to next file) */
 	case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
@@ -537,7 +536,6 @@ add_files (notmuch_database_t *notmuch,
 	case NOTMUCH_STATUS_FILE_ERROR:
 	case NOTMUCH_STATUS_NULL_POINTER:
 	case NOTMUCH_STATUS_TAG_TOO_LONG:
-	case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
 	case NOTMUCH_STATUS_UNBALANCED_ATOMIC:
 	case NOTMUCH_STATUS_LAST_STATUS:
 	    INTERNAL_ERROR ("add_message returned unexpected value: %d",  status);
diff --git a/notmuch-tag.c b/notmuch-tag.c
index 88d559b..23714c9 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -145,8 +145,6 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
 	 notmuch_messages_move_to_next (messages)) {
 	message = notmuch_messages_get (messages);
 
-	notmuch_message_freeze (message);
-
 	for (i = 0; tag_ops[i].tag; i++) {
 	    if (tag_ops[i].remove)
 		notmuch_message_remove_tag (message, tag_ops[i].tag);
@@ -154,7 +152,7 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
 		notmuch_message_add_tag (message, tag_ops[i].tag);
 	}
 
-	notmuch_message_thaw (message);
+	notmuch_message_sync (message);
 
 	if (synchronize_flags)
 	    notmuch_message_tags_to_maildir_flags (message);
diff --git a/tag-util.c b/tag-util.c
index eab482f..65db504 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -228,12 +228,6 @@ tag_op_list_apply (notmuch_message_t *message,
     if (! (flags & TAG_FLAG_PRE_OPTIMIZED) && ! makes_changes (message, list, flags))
 	return NOTMUCH_STATUS_SUCCESS;
 
-    status = notmuch_message_freeze (message);
-    if (status) {
-	message_error (message, status, "freezing message");
-	return status;
-    }
-
     if (flags & TAG_FLAG_REMOVE_ALL) {
 	status = notmuch_message_remove_all_tags (message);
 	if (status) {
@@ -259,9 +253,9 @@ tag_op_list_apply (notmuch_message_t *message,
 	}
     }
 
-    status = notmuch_message_thaw (message);
+    status = notmuch_message_sync (message);
     if (status) {
-	message_error (message, status, "thawing message");
+	message_error (message, status, "syncing message");
 	return status;
     }
 
-- 
1.8.0



More information about the notmuch mailing list