~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_dirstate_helpers_pyx.pyx

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2008, 2010 Canonical Ltd
 
1
# Copyright (C) 2007-2010 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
97
97
    object PyTuple_GetItem_void_object "PyTuple_GET_ITEM" (void* tpl, int index)
98
98
    object PyTuple_GET_ITEM(object tpl, Py_ssize_t index)
99
99
 
 
100
    unsigned long PyInt_AsUnsignedLongMask(object number) except? -1
100
101
 
101
102
    char *PyString_AsString(object p)
102
103
    char *PyString_AsString_obj "PyString_AsString" (PyObject *string)
118
119
    # ??? memrchr is a GNU extension :(
119
120
    # void *memrchr(void *s, int c, size_t len)
120
121
 
 
122
# cimport all of the definitions we will need to access
 
123
from _static_tuple_c cimport import_static_tuple_c, StaticTuple, \
 
124
    StaticTuple_New, StaticTuple_SET_ITEM
 
125
 
 
126
import_static_tuple_c()
121
127
 
122
128
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
123
129
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
610
616
        :param new_block: This is to let the caller know that it needs to
611
617
            create a new directory block to store the next entry.
612
618
        """
613
 
        cdef object path_name_file_id_key
 
619
        cdef StaticTuple path_name_file_id_key
 
620
        cdef StaticTuple tmp
614
621
        cdef char *entry_size_cstr
615
622
        cdef unsigned long int entry_size
616
623
        cdef char* executable_cstr
650
657
        # Build up the key that will be used.
651
658
        # By using <object>(void *) Pyrex will automatically handle the
652
659
        # Py_INCREF that we need.
653
 
        path_name_file_id_key = (<object>p_current_dirname[0],
654
 
                                 self.get_next_str(),
655
 
                                 self.get_next_str(),
656
 
                                )
 
660
        cur_dirname = <object>p_current_dirname[0]
 
661
        # Use StaticTuple_New to pre-allocate, rather than creating a regular
 
662
        # tuple and passing it to the StaticTuple constructor.
 
663
        # path_name_file_id_key = StaticTuple(<object>p_current_dirname[0],
 
664
        #                          self.get_next_str(),
 
665
        #                          self.get_next_str(),
 
666
        #                         )
 
667
        tmp = StaticTuple_New(3)
 
668
        Py_INCREF(cur_dirname); StaticTuple_SET_ITEM(tmp, 0, cur_dirname)
 
669
        cur_basename = self.get_next_str()
 
670
        cur_file_id = self.get_next_str()
 
671
        Py_INCREF(cur_basename); StaticTuple_SET_ITEM(tmp, 1, cur_basename)
 
672
        Py_INCREF(cur_file_id); StaticTuple_SET_ITEM(tmp, 2, cur_file_id)
 
673
        path_name_file_id_key = tmp
657
674
 
658
675
        # Parse all of the per-tree information. current has the information in
659
676
        # the same location as parent trees. The only difference is that 'info'
677
694
            executable_cstr = self.get_next(&cur_size)
678
695
            is_executable = (executable_cstr[0] == c'y')
679
696
            info = self.get_next_str()
680
 
            PyList_Append(trees, (
 
697
            # TODO: If we want to use StaticTuple_New here we need to be pretty
 
698
            #       careful. We are relying on a bit of Pyrex
 
699
            #       automatic-conversion from 'int' to PyInt, and that doesn't
 
700
            #       play well with the StaticTuple_SET_ITEM macro.
 
701
            #       Timing doesn't (yet) show a worthwile improvement in speed
 
702
            #       versus complexity and maintainability.
 
703
            # tmp = StaticTuple_New(5)
 
704
            # Py_INCREF(minikind); StaticTuple_SET_ITEM(tmp, 0, minikind)
 
705
            # Py_INCREF(fingerprint); StaticTuple_SET_ITEM(tmp, 1, fingerprint)
 
706
            # Py_INCREF(entry_size); StaticTuple_SET_ITEM(tmp, 2, entry_size)
 
707
            # Py_INCREF(is_executable); StaticTuple_SET_ITEM(tmp, 3, is_executable)
 
708
            # Py_INCREF(info); StaticTuple_SET_ITEM(tmp, 4, info)
 
709
            # PyList_Append(trees, tmp)
 
710
            PyList_Append(trees, StaticTuple(
681
711
                minikind,     # minikind
682
712
                fingerprint,  # fingerprint
683
713
                entry_size,   # size
782
812
_encode = binascii.b2a_base64
783
813
 
784
814
 
785
 
from struct import pack
786
815
cdef _pack_stat(stat_value):
787
816
    """return a string representing the stat value's key fields.
788
817
 
792
821
    cdef char result[6*4] # 6 long ints
793
822
    cdef int *aliased
794
823
    aliased = <int *>result
795
 
    aliased[0] = htonl(stat_value.st_size)
796
 
    aliased[1] = htonl(int(stat_value.st_mtime))
797
 
    aliased[2] = htonl(int(stat_value.st_ctime))
798
 
    aliased[3] = htonl(stat_value.st_dev)
799
 
    aliased[4] = htonl(stat_value.st_ino & 0xFFFFFFFF)
800
 
    aliased[5] = htonl(stat_value.st_mode)
 
824
    aliased[0] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_size))
 
825
    # mtime and ctime will often be floats but get converted to PyInt within
 
826
    aliased[1] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_mtime))
 
827
    aliased[2] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_ctime))
 
828
    aliased[3] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_dev))
 
829
    aliased[4] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_ino))
 
830
    aliased[5] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_mode))
801
831
    packed = PyString_FromStringAndSize(result, 6*4)
802
832
    return _encode(packed)[:-1]
803
833
 
804
834
 
 
835
def pack_stat(stat_value):
 
836
    """Convert stat value into a packed representation quickly with pyrex"""
 
837
    return _pack_stat(stat_value)
 
838
 
 
839
 
805
840
def update_entry(self, entry, abspath, stat_value):
806
841
    """Update the entry based on what is actually on disk.
807
842
 
837
872
    # _st mode of the compiled stat objects.
838
873
    cdef int minikind, saved_minikind
839
874
    cdef void * details
 
875
    cdef int worth_saving
840
876
    minikind = minikind_from_mode(stat_value.st_mode)
841
877
    if 0 == minikind:
842
878
        return None
871
907
    # If we have gotten this far, that means that we need to actually
872
908
    # process this entry.
873
909
    link_or_sha1 = None
 
910
    worth_saving = 1
874
911
    if minikind == c'f':
875
912
        executable = self._is_executable(stat_value.st_mode,
876
913
                                         saved_executable)
887
924
            entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
888
925
                           executable, packed_stat)
889
926
        else:
890
 
            entry[1][0] = ('f', '', stat_value.st_size,
891
 
                           executable, DirState.NULLSTAT)
 
927
            # This file is not worth caching the sha1. Either it is too new, or
 
928
            # it is newly added. Regardless, the only things we are changing
 
929
            # are derived from the stat, and so are not worth caching. So we do
 
930
            # *not* set the IN_MEMORY_MODIFIED flag. (But we'll save the
 
931
            # updated values if there is *other* data worth saving.)
 
932
            entry[1][0] = ('f', '', stat_value.st_size, executable,
 
933
                           DirState.NULLSTAT)
 
934
            worth_saving = 0
892
935
    elif minikind == c'd':
893
 
        link_or_sha1 = None
894
936
        entry[1][0] = ('d', '', 0, False, packed_stat)
895
937
        if saved_minikind != c'd':
896
938
            # This changed from something into a directory. Make sure we
900
942
                self._get_block_entry_index(entry[0][0], entry[0][1], 0)
901
943
            self._ensure_block(block_index, entry_index,
902
944
                               pathjoin(entry[0][0], entry[0][1]))
 
945
        else:
 
946
            # Any changes are derived trivially from the stat object, not worth
 
947
            # re-writing a dirstate for just this
 
948
            worth_saving = 0
903
949
    elif minikind == c'l':
 
950
        if saved_minikind == c'l':
 
951
            # If the object hasn't changed kind, it isn't worth saving the
 
952
            # dirstate just for a symlink. The default is 'fast symlinks' which
 
953
            # save the target in the inode entry, rather than separately. So to
 
954
            # stat, we've already read everything off disk.
 
955
            worth_saving = 0
904
956
        link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
905
957
        if self._cutoff_time is None:
906
958
            self._sha_cutoff_time()
911
963
        else:
912
964
            entry[1][0] = ('l', '', stat_value.st_size,
913
965
                           False, DirState.NULLSTAT)
914
 
    self._dirblock_state = DirState.IN_MEMORY_MODIFIED
 
966
    if worth_saving:
 
967
        # Note, even though _mark_modified will only set
 
968
        # IN_MEMORY_HASH_MODIFIED, it still isn't worth 
 
969
        self._mark_modified([entry])
915
970
    return link_or_sha1
916
971
 
917
972
 
1219
1274
            else:
1220
1275
                try:
1221
1276
                    source_parent_id = self.old_dirname_to_file_id[old_dirname]
1222
 
                except KeyError:
 
1277
                except KeyError, _:
1223
1278
                    source_parent_entry = self.state._get_entry(self.source_index,
1224
1279
                                                           path_utf8=old_dirname)
1225
1280
                    source_parent_id = source_parent_entry[0][2]
1236
1291
            else:
1237
1292
                try:
1238
1293
                    target_parent_id = self.new_dirname_to_file_id[new_dirname]
1239
 
                except KeyError:
 
1294
                except KeyError, _:
1240
1295
                    # TODO: We don't always need to do the lookup, because the
1241
1296
                    #       parent entry will be the same as the source entry.
1242
1297
                    target_parent_entry = self.state._get_entry(self.target_index,
1478
1533
            # interface doesn't require it.
1479
1534
            try:
1480
1535
                self.current_root = self.search_specific_files.pop()
1481
 
            except KeyError:
 
1536
            except KeyError, _:
1482
1537
                raise StopIteration()
1483
1538
            self.searched_specific_files.add(self.current_root)
1484
1539
            # process the entries for this containing directory: the rest will be
1567
1622
                        #            and e.winerror == ERROR_DIRECTORY
1568
1623
                        try:
1569
1624
                            e_winerror = e.winerror
1570
 
                        except AttributeError:
 
1625
                        except AttributeError, _:
1571
1626
                            e_winerror = None
1572
1627
                        win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
1573
1628
                        if (e.errno in win_errors or e_winerror in win_errors):
1656
1711
                    try:
1657
1712
                        self.current_dir_info = self.dir_iterator.next()
1658
1713
                        self.current_dir_list = self.current_dir_info[1]
1659
 
                    except StopIteration:
 
1714
                    except StopIteration, _:
1660
1715
                        self.current_dir_info = None
1661
1716
                else: #(dircmp > 0)
1662
1717
                    # We have a dirblock entry for this location, but there
1744
1799
                advance_entry = -1
1745
1800
                advance_path = -1
1746
1801
                result = None
 
1802
                changed = None
1747
1803
                path_handled = 0
1748
1804
                if current_entry is None:
1749
1805
                    # unversioned -  the check for path_handled when the path
1803
1859
                                and stat.S_IEXEC & current_path_info[3].st_mode)
1804
1860
                            try:
1805
1861
                                relpath_unicode = self.utf8_decode(current_path_info[0])[0]
1806
 
                            except UnicodeDecodeError:
 
1862
                            except UnicodeDecodeError, _:
1807
1863
                                raise errors.BadFilenameEncoding(
1808
1864
                                    current_path_info[0], osutils._fs_enc)
1809
1865
                            if changed is not None:
1851
1907
                try:
1852
1908
                    self.current_dir_info = self.dir_iterator.next()
1853
1909
                    self.current_dir_list = self.current_dir_info[1]
1854
 
                except StopIteration:
 
1910
                except StopIteration, _:
1855
1911
                    self.current_dir_info = None
1856
1912
 
1857
1913
    cdef object _next_consistent_entries(self):