~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

  • Committer: John Arbash Meinel
  • Date: 2007-03-01 21:56:19 UTC
  • mto: (2255.7.84 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: john@arbash-meinel.com-20070301215619-wpt6kz8yem3ypu1b
Update to dirstate locking.
Move all of WT4.lock_* functions locally, so that they can
properly interact and cleanup around when we lock/unlock the
dirstate file.
Change all Lock objects to be non-blocking. So that if someone
grabs a lock on the DirState we find out immediately, rather
than blocking.
Change WT4.unlock() so that if the dirstate is dirty, it will
save the contents even if it only has a read lock.
It does this by trying to take a write lock, if it fails
we just ignore it. If it succeeds, then we can flush to disk.
This is more important now that DirState tracks file changes.
It allows 'bzr status' to update the cached stat and sha values.

Show diffs side-by-side

added added

removed removed

Lines of Context:
194
194
import bisect
195
195
import codecs
196
196
import cStringIO
 
197
import errno
197
198
import os
198
199
import sha
199
200
import struct
285
286
        self._state_file = None
286
287
        self._filename = path
287
288
        self._lock_token = None
 
289
        self._lock_state = None
288
290
        self._id_index = None
289
291
        self._end_of_header = None
290
292
        self._split_path_cache = {}
1028
1030
        # the internal _dirblocks. So the dirblock state must have already been
1029
1031
        # read.
1030
1032
        assert self._dirblock_state != DirState.NOT_IN_MEMORY
1031
 
        assert entry[1][0][0] == 'f', \
1032
 
            'can only get sha1 for a file not: %s %s' % (
1033
 
            DirState._minikind_to_kind[entry[1][0][0]], entry[0])
 
1033
        # TODO: jam 20070301 Because we now allow kind changes (files => dirs)
 
1034
        #       we should actually base this check on the stat value, since
 
1035
        #       that is the absolute measurement of whether we have a file or
 
1036
        #       directory or link. That means that this function might actually
 
1037
        #       change an entry from being a file => dir or dir => file, etc.
 
1038
        if entry[1][0][0] != 'f':
 
1039
            return None
1034
1040
        if stat_value is None:
1035
1041
            stat_value = os.lstat(abspath)
1036
1042
        packed_stat = pack_stat(stat_value)
1563
1569
        num_entries_line = self._state_file.readline()
1564
1570
        assert num_entries_line.startswith('num_entries: '), 'missing num_entries line'
1565
1571
        self._num_entries = int(num_entries_line[len('num_entries: '):-1])
1566
 
    
 
1572
 
1567
1573
    def save(self):
1568
1574
        """Save any pending changes created during this session.
1569
 
        
 
1575
 
1570
1576
        We reuse the existing file, because that prevents race conditions with
1571
 
        file creation, and we expect to be using oslocks on it in the near 
1572
 
        future to prevent concurrent modification and reads - because dirstates
1573
 
        incremental data aggretation is not compatible with reading a modified
1574
 
        file, and replacing a file in use by another process is impossible on 
1575
 
        windows.
 
1577
        file creation, and use oslocks on it to prevent concurrent modification
 
1578
        and reads - because dirstates incremental data aggretation is not
 
1579
        compatible with reading a modified file, and replacing a file in use by
 
1580
        another process is impossible on windows.
1576
1581
 
1577
1582
        A dirstate in read only mode should be smart enough though to validate
1578
1583
        that the file has not changed, and otherwise discard its cache and
1581
1586
        """
1582
1587
        if (self._header_state == DirState.IN_MEMORY_MODIFIED or
1583
1588
            self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
1584
 
            self._state_file.seek(0)
1585
 
            self._state_file.writelines(self.get_lines())
1586
 
            self._state_file.truncate()
1587
 
            self._state_file.flush()
1588
 
            self._header_state = DirState.IN_MEMORY_UNMODIFIED
1589
 
            self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
 
1589
 
 
1590
            if self._lock_state == 'w':
 
1591
                out_file = self._state_file
 
1592
                wlock = None
 
1593
            else:
 
1594
                # Try to grab a write lock so that we can update the file.
 
1595
                try:
 
1596
                    wlock = lock.WriteLock(self._filename)
 
1597
                except (errors.LockError, errors.LockContention), e:
 
1598
                    # We couldn't grab the lock, so just leave things dirty in
 
1599
                    # memory.
 
1600
                    return
 
1601
                except IOError, e:
 
1602
                    # This may be a read-only tree, or someone else may have a
 
1603
                    # ReadLock. so handle the case when we cannot grab a write
 
1604
                    # lock
 
1605
                    if e.errno in (errno.ENOENT, errno.EPERM, errno.EACCES,
 
1606
                                   errno.EAGAIN):
 
1607
                        # Ignore these errors and just don't save anything
 
1608
                        return
 
1609
                    raise
 
1610
                out_file = wlock.f
 
1611
            try:
 
1612
                out_file.seek(0)
 
1613
                out_file.writelines(self.get_lines())
 
1614
                out_file.truncate()
 
1615
                out_file.flush()
 
1616
                self._header_state = DirState.IN_MEMORY_UNMODIFIED
 
1617
                self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
 
1618
            finally:
 
1619
                if wlock is not None:
 
1620
                    wlock.unlock()
1590
1621
 
1591
1622
    def _set_data(self, parent_ids, dirblocks):
1592
1623
        """Set the full dirstate data in memory.
2053
2084
        """Acquire a read lock on the dirstate"""
2054
2085
        if self._lock_token is not None:
2055
2086
            raise errors.LockContention(self._lock_token)
 
2087
        # TODO: jam 20070301 Rather than wiping completely, if the blocks are
 
2088
        #       already in memory, we could read just the header and check for
 
2089
        #       any modification. If not modified, we can just leave things
 
2090
        #       alone
2056
2091
        self._lock_token = lock.ReadLock(self._filename)
 
2092
        self._lock_state = 'r'
2057
2093
        self._state_file = self._lock_token.f
2058
2094
        self._wipe_state()
2059
2095
 
2061
2097
        """Acquire a write lock on the dirstate"""
2062
2098
        if self._lock_token is not None:
2063
2099
            raise errors.LockContention(self._lock_token)
 
2100
        # TODO: jam 20070301 Rather than wiping completely, if the blocks are
 
2101
        #       already in memory, we could read just the header and check for
 
2102
        #       any modification. If not modified, we can just leave things
 
2103
        #       alone
2064
2104
        self._lock_token = lock.WriteLock(self._filename)
 
2105
        self._lock_state = 'w'
2065
2106
        self._state_file = self._lock_token.f
2066
2107
        self._wipe_state()
2067
2108
 
2069
2110
        """Drop any locks held on the dirstate"""
2070
2111
        if self._lock_token is None:
2071
2112
            raise errors.LockNotHeld(self)
 
2113
        # TODO: jam 20070301 Rather than wiping completely, if the blocks are
 
2114
        #       already in memory, we could read just the header and check for
 
2115
        #       any modification. If not modified, we can just leave things
 
2116
        #       alone
2072
2117
        self._state_file = None
 
2118
        self._lock_state = None
2073
2119
        self._lock_token.unlock()
2074
2120
        self._lock_token = None
2075
2121
        self._split_path_cache = {}