~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lru_cache.py

  • Committer: John Arbash Meinel
  • Date: 2009-07-08 14:28:04 UTC
  • mto: This revision was merged to the branch mainline in revision 4521.
  • Revision ID: john@arbash-meinel.com-20090708142804-i9rkpi9dmnu7v3x1
Fix bug #396838, Update LRUCache to maintain invariant even
if a cleanup function raises an exception.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2006, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
48
48
                                     self.next_key, prev_key)
49
49
 
50
50
    def run_cleanup(self):
51
 
        if self.cleanup is not None:
52
 
            self.cleanup(self.key, self.value)
53
 
        self.cleanup = None
54
 
        # Just make sure to break any refcycles, etc
55
 
        self.value = None
 
51
        try:
 
52
            if self.cleanup is not None:
 
53
                self.cleanup(self.key, self.value)
 
54
        finally:
 
55
            self.cleanup = None
 
56
            # Just make sure to break any refcycles, etc
 
57
            self.value = None
56
58
 
57
59
 
58
60
class LRUCache(object):
156
158
            raise ValueError('cannot use _null_key as a key')
157
159
        if key in self._cache:
158
160
            node = self._cache[key]
159
 
            node.run_cleanup()
160
 
            node.value = value
161
 
            node.cleanup = cleanup
 
161
            try:
 
162
                node.run_cleanup()
 
163
            finally:
 
164
                node.value = value
 
165
                node.cleanup = cleanup
 
166
                self._record_access(node)
162
167
        else:
163
168
            node = _LRUNode(key, value, cleanup=cleanup)
164
169
            self._cache[key] = node
165
 
        self._record_access(node)
 
170
            self._record_access(node)
166
171
 
167
172
        if len(self._cache) > self._max_cache:
168
173
            # Trigger the cleanup
241
246
        # If we have removed all entries, remove the head pointer as well
242
247
        if self._least_recently_used is None:
243
248
            self._most_recently_used = None
244
 
        node.run_cleanup()
245
 
        # Now remove this node from the linked list
246
 
        if node.prev is not None:
247
 
            node.prev.next_key = node.next_key
248
 
        if node.next_key is not _null_key:
249
 
            node_next = self._cache[node.next_key]
250
 
            node_next.prev = node.prev
251
 
        # And remove this node's pointers
252
 
        node.prev = None
253
 
        node.next_key = _null_key
 
249
        try:
 
250
            node.run_cleanup()
 
251
        finally:
 
252
            # Now remove this node from the linked list
 
253
            if node.prev is not None:
 
254
                node.prev.next_key = node.next_key
 
255
            if node.next_key is not _null_key:
 
256
                node_next = self._cache[node.next_key]
 
257
                node_next.prev = node.prev
 
258
            # And remove this node's pointers
 
259
            node.prev = None
 
260
            node.next_key = _null_key
254
261
 
255
262
    def _remove_lru(self):
256
263
        """Remove one entry from the lru, and handle consequences.