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

Reply via email to