mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-22 02:48:08 +01:00
python/notmuch2: do not destroy messages owned by a query
Any messages retrieved from a query - either directly via search_messages() or indirectly via thread objects - are owned by that query. Retrieving the same message (i.e. corresponding to the same message ID / database object) several times will always yield the same C object. The caller is allowed to destroy message objects owned by a query before the query itself - which can save memory for long-lived queries. However, that message must then never be retrieved again from that query. The python-notmuch2 bindings will currently destroy every message object in Message._destroy(), which will lead to an invalid free if the same message is then retrieved again. E.g. the following python program leads to libtalloc abort()ing: import notmuch2 db = notmuch2.Database(mode = notmuch2.Database.MODE.READ_ONLY) t = next(db.threads('*')) msgs = list(zip(t.toplevel(), t.toplevel())) msgs = list(zip(t.toplevel(), t.toplevel())) Fix this issue by creating a subclass of Message, which is used for "standalone" message which have to be freed by the caller. Message class is then used only for messages descended from a query, which do not need to be freed by the caller.
This commit is contained in:
parent
1bca41698a
commit
1317579079
2 changed files with 30 additions and 18 deletions
|
@ -400,7 +400,7 @@ class Database(base.NotmuchObject):
|
||||||
capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID]
|
capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID]
|
||||||
if ret not in ok:
|
if ret not in ok:
|
||||||
raise errors.NotmuchError(ret)
|
raise errors.NotmuchError(ret)
|
||||||
msg = message.Message(self, msg_pp[0], db=self)
|
msg = message.StandaloneMessage(self, msg_pp[0], db=self)
|
||||||
if sync_flags:
|
if sync_flags:
|
||||||
msg.tags.from_maildir_flags()
|
msg.tags.from_maildir_flags()
|
||||||
return self.AddedMessage(
|
return self.AddedMessage(
|
||||||
|
@ -469,7 +469,7 @@ class Database(base.NotmuchObject):
|
||||||
msg_p = msg_pp[0]
|
msg_p = msg_pp[0]
|
||||||
if msg_p == capi.ffi.NULL:
|
if msg_p == capi.ffi.NULL:
|
||||||
raise LookupError
|
raise LookupError
|
||||||
msg = message.Message(self, msg_p, db=self)
|
msg = message.StandaloneMessage(self, msg_p, db=self)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def get(self, filename):
|
def get(self, filename):
|
||||||
|
@ -502,7 +502,7 @@ class Database(base.NotmuchObject):
|
||||||
msg_p = msg_pp[0]
|
msg_p = msg_pp[0]
|
||||||
if msg_p == capi.ffi.NULL:
|
if msg_p == capi.ffi.NULL:
|
||||||
raise LookupError
|
raise LookupError
|
||||||
msg = message.Message(self, msg_p, db=self)
|
msg = message.StandaloneMessage(self, msg_p, db=self)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -14,7 +14,7 @@ __all__ = ['Message']
|
||||||
|
|
||||||
|
|
||||||
class Message(base.NotmuchObject):
|
class Message(base.NotmuchObject):
|
||||||
"""An email message stored in the notmuch database.
|
"""An email message stored in the notmuch database retrieved via a query.
|
||||||
|
|
||||||
This should not be directly created, instead it will be returned
|
This should not be directly created, instead it will be returned
|
||||||
by calling methods on :class:`Database`. A message keeps a
|
by calling methods on :class:`Database`. A message keeps a
|
||||||
|
@ -61,22 +61,10 @@ class Message(base.NotmuchObject):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alive(self):
|
def alive(self):
|
||||||
if not self._parent.alive:
|
return self._parent.alive
|
||||||
return False
|
|
||||||
try:
|
|
||||||
self._msg_p
|
|
||||||
except errors.ObjectDestroyedError:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self._destroy()
|
|
||||||
|
|
||||||
def _destroy(self):
|
def _destroy(self):
|
||||||
if self.alive:
|
pass
|
||||||
capi.lib.notmuch_message_destroy(self._msg_p)
|
|
||||||
self._msg_p = None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def messageid(self):
|
def messageid(self):
|
||||||
|
@ -375,6 +363,30 @@ class Message(base.NotmuchObject):
|
||||||
if isinstance(other, self.__class__):
|
if isinstance(other, self.__class__):
|
||||||
return self.messageid == other.messageid
|
return self.messageid == other.messageid
|
||||||
|
|
||||||
|
class StandaloneMessage(Message):
|
||||||
|
"""An email message stored in the notmuch database.
|
||||||
|
|
||||||
|
This subclass of Message is used for messages that are retrieved from the
|
||||||
|
database directly and are not owned by a query.
|
||||||
|
"""
|
||||||
|
@property
|
||||||
|
def alive(self):
|
||||||
|
if not self._parent.alive:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
self._msg_p
|
||||||
|
except errors.ObjectDestroyedError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self._destroy()
|
||||||
|
|
||||||
|
def _destroy(self):
|
||||||
|
if self.alive:
|
||||||
|
capi.lib.notmuch_message_destroy(self._msg_p)
|
||||||
|
self._msg_p = None
|
||||||
|
|
||||||
class FilenamesIter(base.NotmuchIter):
|
class FilenamesIter(base.NotmuchIter):
|
||||||
"""Iterator for binary filenames objects."""
|
"""Iterator for binary filenames objects."""
|
||||||
|
|
Loading…
Reference in a new issue