Show More
@@ -365,6 +365,11 b' class bundlerepository(object):' | |||||
365 | self.manstart = self._cgunpacker.tell() |
|
365 | self.manstart = self._cgunpacker.tell() | |
366 | return c |
|
366 | return c | |
367 |
|
367 | |||
|
368 | def _refreshchangelog(self): | |||
|
369 | # changelog for bundle repo are not filecache, this method is not | |||
|
370 | # applicable. | |||
|
371 | pass | |||
|
372 | ||||
368 | @localrepo.unfilteredpropertycache |
|
373 | @localrepo.unfilteredpropertycache | |
369 | def manifestlog(self): |
|
374 | def manifestlog(self): | |
370 | self._cgunpacker.seek(self.manstart) |
|
375 | self._cgunpacker.seek(self.manstart) |
@@ -1227,8 +1227,62 b' class localrepository(object):' | |||||
1227 | @mixedrepostorecache(('bookmarks', 'plain'), ('bookmarks.current', 'plain'), |
|
1227 | @mixedrepostorecache(('bookmarks', 'plain'), ('bookmarks.current', 'plain'), | |
1228 | ('bookmarks', ''), ('00changelog.i', '')) |
|
1228 | ('bookmarks', ''), ('00changelog.i', '')) | |
1229 | def _bookmarks(self): |
|
1229 | def _bookmarks(self): | |
|
1230 | # Since the multiple files involved in the transaction cannot be | |||
|
1231 | # written atomically (with current repository format), there is a race | |||
|
1232 | # condition here. | |||
|
1233 | # | |||
|
1234 | # 1) changelog content A is read | |||
|
1235 | # 2) outside transaction update changelog to content B | |||
|
1236 | # 3) outside transaction update bookmark file referring to content B | |||
|
1237 | # 4) bookmarks file content is read and filtered against changelog-A | |||
|
1238 | # | |||
|
1239 | # When this happens, bookmarks against nodes missing from A are dropped. | |||
|
1240 | # | |||
|
1241 | # Having this happening during read is not great, but it become worse | |||
|
1242 | # when this happen during write because the bookmarks to the "unknown" | |||
|
1243 | # nodes will be dropped for good. However, writes happen within locks. | |||
|
1244 | # This locking makes it possible to have a race free consistent read. | |||
|
1245 | # For this purpose data read from disc before locking are | |||
|
1246 | # "invalidated" right after the locks are taken. This invalidations are | |||
|
1247 | # "light", the `filecache` mechanism keep the data in memory and will | |||
|
1248 | # reuse them if the underlying files did not changed. Not parsing the | |||
|
1249 | # same data multiple times helps performances. | |||
|
1250 | # | |||
|
1251 | # Unfortunately in the case describe above, the files tracked by the | |||
|
1252 | # bookmarks file cache might not have changed, but the in-memory | |||
|
1253 | # content is still "wrong" because we used an older changelog content | |||
|
1254 | # to process the on-disk data. So after locking, the changelog would be | |||
|
1255 | # refreshed but `_bookmarks` would be preserved. | |||
|
1256 | # Adding `00changelog.i` to the list of tracked file is not | |||
|
1257 | # enough, because at the time we build the content for `_bookmarks` in | |||
|
1258 | # (4), the changelog file has already diverged from the content used | |||
|
1259 | # for loading `changelog` in (1) | |||
|
1260 | # | |||
|
1261 | # To prevent the issue, we force the changelog to be explicitly | |||
|
1262 | # reloaded while computing `_bookmarks`. The data race can still happen | |||
|
1263 | # without the lock (with a narrower window), but it would no longer go | |||
|
1264 | # undetected during the lock time refresh. | |||
|
1265 | # | |||
|
1266 | # The new schedule is as follow | |||
|
1267 | # | |||
|
1268 | # 1) filecache logic detect that `_bookmarks` needs to be computed | |||
|
1269 | # 2) cachestat for `bookmarks` and `changelog` are captured (for book) | |||
|
1270 | # 3) We force `changelog` filecache to be tested | |||
|
1271 | # 4) cachestat for `changelog` are captured (for changelog) | |||
|
1272 | # 5) `_bookmarks` is computed and cached | |||
|
1273 | # | |||
|
1274 | # The step in (3) ensure we have a changelog at least as recent as the | |||
|
1275 | # cache stat computed in (1). As a result at locking time: | |||
|
1276 | # * if the changelog did not changed since (1) -> we can reuse the data | |||
|
1277 | # * otherwise -> the bookmarks get refreshed. | |||
|
1278 | self._refreshchangelog() | |||
1230 | return bookmarks.bmstore(self) |
|
1279 | return bookmarks.bmstore(self) | |
1231 |
|
1280 | |||
|
1281 | def _refreshchangelog(self): | |||
|
1282 | """make sure the in memory changelog match the on-disk one""" | |||
|
1283 | if ('changelog' in vars(self) and self.currenttransaction() is None): | |||
|
1284 | del self.changelog | |||
|
1285 | ||||
1232 | @property |
|
1286 | @property | |
1233 | def _activebookmark(self): |
|
1287 | def _activebookmark(self): | |
1234 | return self._bookmarks.active |
|
1288 | return self._bookmarks.active |
@@ -1748,7 +1748,8 b' class TTest(Test):' | |||||
1748 |
|
1748 | |||
1749 | el = m.group(1) + b"\n" |
|
1749 | el = m.group(1) + b"\n" | |
1750 | if not self._iftest(conditions): |
|
1750 | if not self._iftest(conditions): | |
1751 | retry = "retry" # Not required by listed features |
|
1751 | # listed feature missing, should not match | |
|
1752 | return "retry", False | |||
1752 |
|
1753 | |||
1753 | if el.endswith(b" (esc)\n"): |
|
1754 | if el.endswith(b" (esc)\n"): | |
1754 | if PYTHON3: |
|
1755 | if PYTHON3: |
@@ -119,9 +119,13 b' We build a server side extension for thi' | |||||
119 | > import atexit |
|
119 | > import atexit | |
120 | > import os |
|
120 | > import os | |
121 | > import time |
|
121 | > import time | |
122 | > from mercurial import bookmarks, error, extensions |
|
122 | > import atexit | |
123 | > def wrapinit(orig, self, repo): |
|
123 | > from mercurial import error, extensions, bookmarks | |
|
124 | > | |||
|
125 | > def wait(repo): | |||
124 | > if not os.path.exists('push-A-started'): |
|
126 | > if not os.path.exists('push-A-started'): | |
|
127 | > assert repo._currentlock(repo._lockref) is None | |||
|
128 | > assert repo._currentlock(repo._wlockref) is None | |||
125 | > repo.ui.status(b'setting raced push up\n') |
|
129 | > repo.ui.status(b'setting raced push up\n') | |
126 | > with open('push-A-started', 'w'): |
|
130 | > with open('push-A-started', 'w'): | |
127 | > pass |
|
131 | > pass | |
@@ -131,11 +135,15 b' We build a server side extension for thi' | |||||
131 | > if clock <= 0: |
|
135 | > if clock <= 0: | |
132 | > raise error.Abort("race scenario timed out") |
|
136 | > raise error.Abort("race scenario timed out") | |
133 | > time.sleep(0.1) |
|
137 | > time.sleep(0.1) | |
134 | > return orig(self, repo) |
|
|||
135 | > |
|
138 | > | |
|
139 | > def reposetup(ui, repo): | |||
|
140 | > class racedrepo(repo.__class__): | |||
|
141 | > @property | |||
|
142 | > def _bookmarks(self): | |||
|
143 | > wait(self) | |||
|
144 | > return super(racedrepo, self)._bookmarks | |||
136 | > repo.__class__ = racedrepo |
|
145 | > repo.__class__ = racedrepo | |
137 | > def uisetup(ui): |
|
146 | > | |
138 | > extensions.wrapfunction(bookmarks.bmstore, '__init__', wrapinit) |
|
|||
139 | > def e(): |
|
147 | > def e(): | |
140 | > with open('push-A-done', 'w'): |
|
148 | > with open('push-A-done', 'w'): | |
141 | > pass |
|
149 | > pass | |
@@ -193,6 +201,7 b' Check raced push output.' | |||||
193 | $ cat push-output.txt |
|
201 | $ cat push-output.txt | |
194 | pushing to ssh://user@dummy/bookrace-server |
|
202 | pushing to ssh://user@dummy/bookrace-server | |
195 | searching for changes |
|
203 | searching for changes | |
|
204 | remote has heads on branch 'default' that are not known locally: f26c3b5167d1 | |||
196 | remote: setting raced push up |
|
205 | remote: setting raced push up | |
197 | remote: adding changesets |
|
206 | remote: adding changesets | |
198 | remote: adding manifests |
|
207 | remote: adding manifests |
@@ -501,6 +501,7 b' Try merging the other direction too' | |||||
501 | $ hg debugpathcopies 0 4 |
|
501 | $ hg debugpathcopies 0 4 | |
502 | x -> z (filelog !) |
|
502 | x -> z (filelog !) | |
503 | y -> z (compatibility !) |
|
503 | y -> z (compatibility !) | |
|
504 | y -> z (changeset !) | |||
504 | $ hg debugpathcopies 1 5 |
|
505 | $ hg debugpathcopies 1 5 | |
505 | y -> z (no-filelog !) |
|
506 | y -> z (no-filelog !) | |
506 | $ hg debugpathcopies 2 5 |
|
507 | $ hg debugpathcopies 2 5 |
@@ -84,8 +84,8 b' Test basic extension support' | |||||
84 | uipopulate called (1 times) |
|
84 | uipopulate called (1 times) | |
85 | uipopulate called (1 times) (chg !) |
|
85 | uipopulate called (1 times) (chg !) | |
86 | uipopulate called (1 times) (chg !) |
|
86 | uipopulate called (1 times) (chg !) | |
87 |
uipopulate called (1 times) |
|
87 | uipopulate called (1 times) | |
88 |
reposetup called for a |
|
88 | reposetup called for a | |
89 | ui == repo.ui |
|
89 | ui == repo.ui | |
90 | Foo |
|
90 | Foo | |
91 | $ hg foo --debug |
|
91 | $ hg foo --debug | |
@@ -96,8 +96,8 b' Test basic extension support' | |||||
96 | uipopulate called (1 times) |
|
96 | uipopulate called (1 times) | |
97 | uipopulate called (1 times) (chg !) |
|
97 | uipopulate called (1 times) (chg !) | |
98 | uipopulate called (1 times) (chg !) |
|
98 | uipopulate called (1 times) (chg !) | |
99 |
uipopulate called (1 times) |
|
99 | uipopulate called (1 times) | |
100 |
reposetup called for a |
|
100 | reposetup called for a | |
101 | ui == repo.ui |
|
101 | ui == repo.ui | |
102 | Foo |
|
102 | Foo | |
103 |
|
103 | |||
@@ -107,7 +107,7 b' Test basic extension support' | |||||
107 | uisetup called [status] (no-chg !) |
|
107 | uisetup called [status] (no-chg !) | |
108 | uipopulate called (1 times) |
|
108 | uipopulate called (1 times) | |
109 | uipopulate called (1 times) (chg !) |
|
109 | uipopulate called (1 times) (chg !) | |
110 |
uipopulate called (1 times) |
|
110 | uipopulate called (1 times) | |
111 | reposetup called for a |
|
111 | reposetup called for a | |
112 | ui == repo.ui |
|
112 | ui == repo.ui | |
113 | uipopulate called (1 times) |
|
113 | uipopulate called (1 times) |
@@ -61,8 +61,10 b' Cloning a specific file when stream clon' | |||||
61 | Making sure we have the correct set of requirements |
|
61 | Making sure we have the correct set of requirements | |
62 |
|
62 | |||
63 | $ cat .hg/requires |
|
63 | $ cat .hg/requires | |
64 |
dotencode (tree |
|
64 | dotencode (tree !) | |
65 |
|
|
65 | dotencode (flat-fncache !) | |
|
66 | fncache (tree !) | |||
|
67 | fncache (flat-fncache !) | |||
66 | generaldelta |
|
68 | generaldelta | |
67 | narrowhg-experimental |
|
69 | narrowhg-experimental | |
68 | revlogv1 |
|
70 | revlogv1 | |
@@ -75,8 +77,9 b' Making sure store has the required files' | |||||
75 | $ ls .hg/store/ |
|
77 | $ ls .hg/store/ | |
76 | 00changelog.i |
|
78 | 00changelog.i | |
77 | 00manifest.i |
|
79 | 00manifest.i | |
78 | data (tree flat-fncache !) |
|
80 | data | |
79 |
fncache (tree |
|
81 | fncache (tree !) | |
|
82 | fncache (flat-fncache !) | |||
80 | meta (tree !) |
|
83 | meta (tree !) | |
81 | narrowspec |
|
84 | narrowspec | |
82 | undo |
|
85 | undo |
@@ -124,7 +124,7 b' added upstream revisions.' | |||||
124 | adding manifests |
|
124 | adding manifests | |
125 | adding widest/ revisions (tree !) |
|
125 | adding widest/ revisions (tree !) | |
126 | adding file changes |
|
126 | adding file changes | |
127 |
adding widest/f revisions |
|
127 | adding widest/f revisions | |
128 | added 0 changesets with 1 changes to 1 files |
|
128 | added 0 changesets with 1 changes to 1 files | |
129 | bundle2-input-part: total payload size * (glob) |
|
129 | bundle2-input-part: total payload size * (glob) | |
130 | bundle2-input-bundle: 0 parts total |
|
130 | bundle2-input-bundle: 0 parts total |
@@ -1936,3 +1936,70 b' Test automatic pattern replacement' | |||||
1936 | running 1 tests using 1 parallel processes |
|
1936 | running 1 tests using 1 parallel processes | |
1937 | . |
|
1937 | . | |
1938 | # Ran 1 tests, 0 skipped, 0 failed. |
|
1938 | # Ran 1 tests, 0 skipped, 0 failed. | |
|
1939 | ||||
|
1940 | Test conditional output matching | |||
|
1941 | ================================ | |||
|
1942 | ||||
|
1943 | $ cat << EOF >> test-conditional-matching.t | |||
|
1944 | > #testcases foo bar | |||
|
1945 | > $ echo richtig | |||
|
1946 | > richtig (true !) | |||
|
1947 | > $ echo falsch | |||
|
1948 | > falsch (false !) | |||
|
1949 | > #if foo | |||
|
1950 | > $ echo arthur | |||
|
1951 | > arthur (bar !) | |||
|
1952 | > #endif | |||
|
1953 | > $ echo celeste | |||
|
1954 | > celeste (foo !) | |||
|
1955 | > $ echo zephir | |||
|
1956 | > zephir (bar !) | |||
|
1957 | > EOF | |||
|
1958 | ||||
|
1959 | $ rt test-conditional-matching.t | |||
|
1960 | running 2 tests using 1 parallel processes | |||
|
1961 | ||||
|
1962 | --- $TESTTMP/anothertests/cases/test-conditional-matching.t | |||
|
1963 | +++ $TESTTMP/anothertests/cases/test-conditional-matching.t#bar.err | |||
|
1964 | @@ -3,11 +3,13 @@ | |||
|
1965 | richtig (true !) | |||
|
1966 | $ echo falsch | |||
|
1967 | falsch (false !) | |||
|
1968 | + falsch | |||
|
1969 | #if foo | |||
|
1970 | $ echo arthur | |||
|
1971 | arthur \(bar !\) (re) | |||
|
1972 | #endif | |||
|
1973 | $ echo celeste | |||
|
1974 | celeste \(foo !\) (re) | |||
|
1975 | + celeste | |||
|
1976 | $ echo zephir | |||
|
1977 | zephir \(bar !\) (re) | |||
|
1978 | ||||
|
1979 | ERROR: test-conditional-matching.t#bar output changed | |||
|
1980 | ! | |||
|
1981 | --- $TESTTMP/anothertests/cases/test-conditional-matching.t | |||
|
1982 | +++ $TESTTMP/anothertests/cases/test-conditional-matching.t#foo.err | |||
|
1983 | @@ -3,11 +3,14 @@ | |||
|
1984 | richtig (true !) | |||
|
1985 | $ echo falsch | |||
|
1986 | falsch (false !) | |||
|
1987 | + falsch | |||
|
1988 | #if foo | |||
|
1989 | $ echo arthur | |||
|
1990 | arthur \(bar !\) (re) | |||
|
1991 | + arthur | |||
|
1992 | #endif | |||
|
1993 | $ echo celeste | |||
|
1994 | celeste \(foo !\) (re) | |||
|
1995 | $ echo zephir | |||
|
1996 | zephir \(bar !\) (re) | |||
|
1997 | + zephir | |||
|
1998 | ||||
|
1999 | ERROR: test-conditional-matching.t#foo output changed | |||
|
2000 | ! | |||
|
2001 | Failed test-conditional-matching.t#bar: output changed | |||
|
2002 | Failed test-conditional-matching.t#foo: output changed | |||
|
2003 | # Ran 2 tests, 0 skipped, 2 failed. | |||
|
2004 | python hash seed: * (glob) | |||
|
2005 | [1] |
General Comments 0
You need to be logged in to leave comments.
Login now