From d7472ec13a65c6c5ff00365b1477677d1fecbb3c Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 13 Jul 2003 02:22:03 +0000 Subject: Fixed critical shutdown race in _Database._commit. Related to SF patch 723231 (which pointed out the problem, but didn't fix it, just shut up the warning msg -- which was pointing out a dead- serious bug!). Bugfix candidate. --- Lib/dumbdbm.py | 24 +++++++++++++++++++----- Misc/NEWS | 8 ++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Lib/dumbdbm.py b/Lib/dumbdbm.py index 8e2ec90..4e426f1 100644 --- a/Lib/dumbdbm.py +++ b/Lib/dumbdbm.py @@ -33,6 +33,17 @@ error = IOError # For anydbm class _Database(UserDict.DictMixin): + # The on-disk directory and data files can remain in mutually + # inconsistent states for an arbitrarily long time (see comments + # at the end of __setitem__). This is only repaired when _commit() + # gets called. One place _commit() gets called is from __del__(), + # and if that occurs at program shutdown time, module globals may + # already have gotten rebound to None. Since it's crucial that + # _commit() finish sucessfully, we can't ignore shutdown races + # here, and _commit() must not reference any globals. + _os = _os # for _commit() + _open = _open # for _commit() + def __init__(self, filebasename, mode): self._mode = mode @@ -78,17 +89,20 @@ class _Database(UserDict.DictMixin): # file (if any) is renamed with a .bak extension first. If a .bak # file currently exists, it's deleted. def _commit(self): + # CAUTION: It's vital that _commit() succeed, and _commit() can + # be called from __del__(). Therefore we must never reference a + # global in this routine. try: - _os.unlink(self._bakfile) - except _os.error: + self._os.unlink(self._bakfile) + except self._os.error: pass try: - _os.rename(self._dirfile, self._bakfile) - except _os.error: + self._os.rename(self._dirfile, self._bakfile) + except self._os.error: pass - f = _open(self._dirfile, 'w', self._mode) + f = self._open(self._dirfile, 'w', self._mode) for key, pos_and_siz_pair in self._index.iteritems(): f.write("%r, %r\n" % (key, pos_and_siz_pair)) f.close() diff --git a/Misc/NEWS b/Misc/NEWS index b793a73..1098ad3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -38,6 +38,14 @@ Extension modules Library ------- +- It's vital that a dumbdbm database be closed properly, else the + on-disk data and directory files can be left in mutually inconsistent + states. dumbdbm.py's _Database.__del__() method attempted to close + the database properly, but a shutdown race in _Database._commit() + could prevent this form working, so that a program trusting __del__() + to get the on-disk files in synch could be badly surprised. The race + has been repaired. + - The classes in threading.py are now new-style classes. That they weren't before was an oversight. -- cgit v0.12