##// END OF EJS Templates
dirstate: add a way to test races happening during status...
Raphaël Gomès , Pierre-Yves David pierre-yves.david@octobus.net -
r51110:ae61851e stable
parent child Browse files
Show More
@@ -0,0 +1,190 b''
1 =====================================================================
2 Check potential race conditions between a status and other operations
3 =====================================================================
4
5 The `hg status` command can run without the wlock, however it might end up
6 having to update the on-disk dirstate files, for example to mark ambiguous
7 files as clean, or to update directory caches information with dirstate-v2.
8
9
10 If another process updates the dirstate in the meantime we might run into
11 trouble. Especially, commands doing semantic changes like `hg add` or
12 `hg commit` should not see their update erased by a concurrent status.
13
14 Unlike commands like `add` or `commit`, `status` only writes the dirstate
15 to update caches, no actual information is lost if we fail to write to disk.
16
17
18 This test file is meant to test various cases where such parallel operations
19 between a status with reasons to update the dirstate and another semantic
20 changes happen.
21
22
23 Setup
24 =====
25
26 $ directories="dir dir/nested dir2"
27 $ first_files="dir/nested/a dir/b dir/c dir/d dir2/e f"
28 $ second_files="g dir/nested/h dir/i dir/j dir2/k dir2/l dir/nested/m"
29 $ extra_files="dir/n dir/o p q"
30
31 $ hg init reference-repo
32 $ cd reference-repo
33 $ mkdir -p dir/nested dir2
34 $ touch -t 200001010000 $first_files $directories
35 $ hg commit -Aqm "recreate a bunch of files to facilitate dirstate-v2 append"
36 $ touch -t 200001010010 $second_files $directories
37 $ hg commit -Aqm "more files to have two commits"
38 $ hg log -G -v
39 @ changeset: 1:c349430a1631
40 | tag: tip
41 | user: test
42 | date: Thu Jan 01 00:00:00 1970 +0000
43 | files: dir/i dir/j dir/nested/h dir/nested/m dir2/k dir2/l g
44 | description:
45 | more files to have two commits
46 |
47 |
48 o changeset: 0:4f23db756b09
49 user: test
50 date: Thu Jan 01 00:00:00 1970 +0000
51 files: dir/b dir/c dir/d dir/nested/a dir2/e f
52 description:
53 recreate a bunch of files to facilitate dirstate-v2 append
54
55
56 $ hg manifest
57 dir/b
58 dir/c
59 dir/d
60 dir/i
61 dir/j
62 dir/nested/a
63 dir/nested/h
64 dir/nested/m
65 dir2/e
66 dir2/k
67 dir2/l
68 f
69 g
70
71 Add some unknown files and refresh the dirstate
72
73 $ touch -t 200001010020 $extra_files
74 $ hg add dir/o
75 $ hg remove dir/nested/m
76
77 $ hg st
78 A dir/o
79 R dir/nested/m
80 ? dir/n
81 ? p
82 ? q
83 $ hg debugstate
84 n 644 0 2000-01-01 00:00:00 dir/b
85 n 644 0 2000-01-01 00:00:00 dir/c
86 n 644 0 2000-01-01 00:00:00 dir/d
87 n 644 0 2000-01-01 00:10:00 dir/i
88 n 644 0 2000-01-01 00:10:00 dir/j
89 n 644 0 2000-01-01 00:00:00 dir/nested/a
90 n 644 0 2000-01-01 00:10:00 dir/nested/h
91 r ?????????????????????????????????? dir/nested/m (glob)
92 a ?????????????????????????????????? dir/o (glob)
93 n 644 0 2000-01-01 00:00:00 dir2/e
94 n 644 0 2000-01-01 00:10:00 dir2/k
95 n 644 0 2000-01-01 00:10:00 dir2/l
96 n 644 0 2000-01-01 00:00:00 f
97 n 644 0 2000-01-01 00:10:00 g
98 $ hg debugstate > ../reference
99 $ cd ..
100
101 Explain / verify the test principles
102 ------------------------------------
103
104 First, we can properly copy the reference
105
106 $ cp -a reference-repo sanity-check
107 $ cd sanity-check
108 $ hg debugstate
109 n 644 0 2000-01-01 00:00:00 dir/b
110 n 644 0 2000-01-01 00:00:00 dir/c
111 n 644 0 2000-01-01 00:00:00 dir/d
112 n 644 0 2000-01-01 00:10:00 dir/i
113 n 644 0 2000-01-01 00:10:00 dir/j
114 n 644 0 2000-01-01 00:00:00 dir/nested/a
115 n 644 0 2000-01-01 00:10:00 dir/nested/h
116 r ?????????????????????????????????? dir/nested/m (glob)
117 a ?????????????????????????????????? dir/o (glob)
118 n 644 0 2000-01-01 00:00:00 dir2/e
119 n 644 0 2000-01-01 00:10:00 dir2/k
120 n 644 0 2000-01-01 00:10:00 dir2/l
121 n 644 0 2000-01-01 00:00:00 f
122 n 644 0 2000-01-01 00:10:00 g
123 $ hg debugstate > ../post-copy
124 $ diff ../reference ../post-copy
125
126 And status thinks the cache is in a proper state
127
128 $ hg st
129 A dir/o
130 R dir/nested/m
131 ? dir/n
132 ? p
133 ? q
134 $ hg debugstate
135 n 644 0 2000-01-01 00:00:00 dir/b
136 n 644 0 2000-01-01 00:00:00 dir/c
137 n 644 0 2000-01-01 00:00:00 dir/d
138 n 644 0 2000-01-01 00:10:00 dir/i
139 n 644 0 2000-01-01 00:10:00 dir/j
140 n 644 0 2000-01-01 00:00:00 dir/nested/a
141 n 644 0 2000-01-01 00:10:00 dir/nested/h
142 r ?????????????????????????????????? dir/nested/m (glob)
143 a ?????????????????????????????????? dir/o (glob)
144 n 644 0 2000-01-01 00:00:00 dir2/e
145 n 644 0 2000-01-01 00:10:00 dir2/k
146 n 644 0 2000-01-01 00:10:00 dir2/l
147 n 644 0 2000-01-01 00:00:00 f
148 n 644 0 2000-01-01 00:10:00 g
149 $ hg debugstate > ../post-status
150 $ diff ../reference ../post-status
151
152 Then we can start a status that:
153 - has some update to do (the touch call)
154 - will wait AFTER running status, but before updating the cache on disk
155
156 $ touch -t 200001010001 dir/c
157 $ hg st >$TESTTMP/status-race-lock.out 2>$TESTTMP/status-race-lock.log \
158 > --config rhg.on-unsupported=abort \
159 > --config devel.sync.status.pre-dirstate-write-file=$TESTTMP/status-race-lock \
160 > &
161 $ $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/status-race-lock.waiting
162
163 We check it runs the status first by modifying a file and updating another timestamp
164
165 $ touch -t 200001010003 dir/i
166 $ echo babar > dir/j
167 $ touch $TESTTMP/status-race-lock
168 $ wait
169
170 The test process should have reported a status before the change we made,
171 and should have missed the timestamp update
172
173 $ cat $TESTTMP/status-race-lock.out
174 A dir/o
175 R dir/nested/m
176 ? dir/n
177 ? p
178 ? q
179 $ cat $TESTTMP/status-race-lock.log
180 $ hg debugstate | grep dir/c
181 n 644 0 2000-01-01 00:01:00 dir/c
182 $ hg debugstate | grep dir/i
183 n 644 0 2000-01-01 00:10:00 dir/i
184 $ hg debugstate | grep dir/j
185 n 644 0 2000-01-01 00:10:00 dir/j
186
187 final cleanup
188
189 $ rm $TESTTMP/status-race-lock $TESTTMP/status-race-lock.waiting
190 $ cd ..
@@ -677,6 +677,22 b' coreconfigitem('
677 b'serverrequirecert',
677 b'serverrequirecert',
678 default=False,
678 default=False,
679 )
679 )
680 # Makes the status algorithm wait for the existence of this file
681 # (or until a timeout of `devel.sync.status.pre-dirstate-write-file-timeout`
682 # seconds) before taking the lock and writing the dirstate.
683 # Status signals that it's ready to wait by creating a file
684 # with the same name + `.waiting`.
685 # Useful when testing race conditions.
686 coreconfigitem(
687 b'devel',
688 b'sync.status.pre-dirstate-write-file',
689 default=None,
690 )
691 coreconfigitem(
692 b'devel',
693 b'sync.status.pre-dirstate-write-file-timeout',
694 default=2,
695 )
680 coreconfigitem(
696 coreconfigitem(
681 b'devel',
697 b'devel',
682 b'strip-obsmarkers',
698 b'strip-obsmarkers',
@@ -36,6 +36,7 b' from . import ('
36 sparse,
36 sparse,
37 subrepo,
37 subrepo,
38 subrepoutil,
38 subrepoutil,
39 testing,
39 util,
40 util,
40 )
41 )
41 from .utils import (
42 from .utils import (
@@ -1854,6 +1855,8 b' class workingctx(committablectx):'
1854
1855
1855 def _poststatusfixup(self, status, fixup):
1856 def _poststatusfixup(self, status, fixup):
1856 """update dirstate for files that are actually clean"""
1857 """update dirstate for files that are actually clean"""
1858 ui = self._repo.ui
1859 testing.wait_on_cfg(self._repo.ui, b'status.pre-dirstate-write-file')
1857 poststatus = self._repo.postdsstatus()
1860 poststatus = self._repo.postdsstatus()
1858 if fixup or poststatus or self._repo.dirstate._dirty:
1861 if fixup or poststatus or self._repo.dirstate._dirty:
1859 try:
1862 try:
@@ -1890,9 +1893,7 b' class workingctx(committablectx):'
1890 # consistency, because .hg/dirstate was
1893 # consistency, because .hg/dirstate was
1891 # already changed simultaneously after last
1894 # already changed simultaneously after last
1892 # caching (see also issue5584 for detail)
1895 # caching (see also issue5584 for detail)
1893 self._repo.ui.debug(
1896 ui.debug(b'skip updating dirstate: identity mismatch\n')
1894 b'skip updating dirstate: identity mismatch\n'
1895 )
1896 except error.LockError:
1897 except error.LockError:
1897 pass
1898 pass
1898 finally:
1899 finally:
@@ -19,6 +19,7 b' use hg::lock::LockError;'
19 use hg::manifest::Manifest;
19 use hg::manifest::Manifest;
20 use hg::matchers::{AlwaysMatcher, IntersectionMatcher};
20 use hg::matchers::{AlwaysMatcher, IntersectionMatcher};
21 use hg::repo::Repo;
21 use hg::repo::Repo;
22 use hg::utils::debug::debug_wait_for_file;
22 use hg::utils::files::get_bytes_from_os_string;
23 use hg::utils::files::get_bytes_from_os_string;
23 use hg::utils::files::get_bytes_from_path;
24 use hg::utils::files::get_bytes_from_path;
24 use hg::utils::files::get_path_from_bytes;
25 use hg::utils::files::get_path_from_bytes;
@@ -409,6 +410,13 b' pub fn run(invocation: &crate::CliInvoca'
409 after_status,
410 after_status,
410 )?;
411 )?;
411
412
413 // Development config option to test write races
414 if let Err(e) =
415 debug_wait_for_file(&config, "status.pre-dirstate-write-file")
416 {
417 ui.write_stderr(e.as_bytes()).ok();
418 }
419
412 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
420 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
413 && !dirstate_write_needed
421 && !dirstate_write_needed
414 {
422 {
General Comments 0
You need to be logged in to leave comments. Login now