##// END OF EJS Templates
merge: improve working-copy mtime race handling...
Raphaël Gomès -
r52952:b332ae61 default
parent child Browse files
Show More
@@ -1769,14 +1769,28 class dirstate(intdirstate.idirstate):
1769 ladd(fn)
1769 ladd(fn)
1770 else:
1770 else:
1771 madd(fn)
1771 madd(fn)
1772 elif not t.mtime_likely_equal_to(timestamp.mtime_of(st)):
1772 else:
1773 # There might be a change in the future if for example the
1773 reliable = None
1774 # internal clock is off, but this is a case where the issues
1774 if mtime_boundary is not None:
1775 # the user would face would be a lot worse and there is
1775 reliable = timestamp.reliable_mtime_of(
1776 # nothing we can really do.
1776 st, mtime_boundary
1777 ladd(fn)
1777 )
1778 elif listclean:
1778 elif t.mtime_likely_equal_to(timestamp.mtime_of(st)):
1779 cadd(fn)
1779 # We can't compute the current fs time, so we're in
1780 # a readonly fs or a LFS context.
1781 cadd(fn)
1782 continue
1783
1784 if reliable is None or not t.mtime_likely_equal_to(
1785 reliable
1786 ):
1787 # There might be a change in the future if for example
1788 # the internal clock is off, but this is a case where
1789 # the issues the user would face would be a lot worse
1790 # and there is nothing we can really do.
1791 ladd(fn)
1792 elif listclean:
1793 cadd(fn)
1780 status = scmutil.status(
1794 status = scmutil.status(
1781 modified, added, removed, deleted, unknown, ignored, clean
1795 modified, added, removed, deleted, unknown, ignored, clean
1782 )
1796 )
@@ -137,3 +137,44 def make_mtime_reliable(
137 return None
137 return None
138 else:
138 else:
139 return file_timestamp
139 return file_timestamp
140
141
142 FS_TICK_WAIT_TIMEOUT = 0.1 # 100 milliseconds
143
144
145 def wait_until_fs_tick(vfs) -> Optional[Tuple[timestamp, bool]]:
146 """Wait until the next update from the filesystem time by writing in a loop
147 a new temporary file inside the working directory and checking if its time
148 differs from the first one observed.
149
150 Returns `None` if we are unable to get the filesystem time,
151 `(timestamp, True)` if we've timed out waiting for the filesystem clock
152 to tick, and `(timestamp, False)` if we've waited successfully.
153
154 On Linux, your average tick is going to be a "jiffy", or 1/HZ.
155 HZ is your kernel's tick rate (if it has one configured) and the value
156 is the one returned by `grep 'CONFIG_HZ=' /boot/config-$(uname -r)`,
157 again assuming a normal setup.
158
159 In my case (Alphare) at the time of writing, I get `CONFIG_HZ=250`,
160 which equates to 4ms.
161 This might change with a series that could make it to Linux 6.12:
162 https://lore.kernel.org/all/20241002-mgtime-v10-8-d1c4717f5284@kernel.org
163 """
164 start = time.monotonic()
165
166 try:
167 old_fs_time = get_fs_now(vfs)
168 new_fs_time = get_fs_now(vfs)
169
170 while (
171 new_fs_time[0] == old_fs_time[0]
172 and new_fs_time[1] == old_fs_time[1]
173 ):
174 if time.monotonic() - start > FS_TICK_WAIT_TIMEOUT:
175 return (old_fs_time, True)
176 new_fs_time = get_fs_now(vfs)
177 except OSError:
178 return None
179 else:
180 return (new_fs_time, False)
@@ -2214,17 +2214,38 def filter_ambiguous_files(repo, file_da
2214 part, but we want to at least prevent later file changes to alter the
2214 part, but we want to at least prevent later file changes to alter the
2215 contents of the file right after the update operation so quickly that the
2215 contents of the file right after the update operation so quickly that the
2216 same mtime is recorded for the operation.
2216 same mtime is recorded for the operation.
2217 To prevent such ambiguities from happenning, we will only keep the
2217
2218 "file data" for files with mtimes that are strictly in the past,
2218 To prevent such ambiguities from happenning, we will do (up to) two things:
2219 i.e. whose mtime is strictly lower than the current time.
2219 - wait until the filesystem clock has ticked
2220 - only keep the "file data" for files with mtimes that are strictly in
2221 the past, i.e. whose mtime is strictly lower than the current time.
2222
2223 We only wait for the system clock to tick if using dirstate-v2, since v1
2224 only has second-level granularity and waiting for a whole second is
2225 too much of a penalty in the general case.
2226
2227 Although we're assuming that people running dirstate-v2 on Linux
2228 don't have a second-granularity FS (with the exclusion of NFS), users
2229 can be surprising, and at some point in the future, dirstate-v2 will become
2230 the default. To that end, we limit the wait time to 100ms and fall back
2231 to the filtering method in case of a timeout.
2232
2233 +------------+------+--------------+
2234 | version | wait | filter level |
2235 +------------+------+--------------+
2236 | V1 | No | Second |
2237 | V2 | Yes | Nanosecond |
2238 | V2-slow-fs | No | Second |
2239 +------------+------+--------------+
2220
2240
2221 This protects us from race conditions from operations that could run right
2241 This protects us from race conditions from operations that could run right
2222 after this one, especially other Mercurial operations that could be waiting
2242 after this one, especially other Mercurial operations that could be waiting
2223 for the wlock to touch files contents and the dirstate.
2243 for the wlock to touch files contents and the dirstate.
2224
2244
2225 In an ideal world, we could only get reliable information in `getfiledata`
2245 In an ideal world, we could only get reliable information in `getfiledata`
2226 (from `getbatch`), however the current approach has been a successful
2246 (from `getbatch`), however this filtering approach has been a successful
2227 compromise for many years.
2247 compromise for many years. A patch series of the linux kernel might change
2248 this in 6.12³.
2228
2249
2229 At the time this comment is written, not using any "cache" file data at all
2250 At the time this comment is written, not using any "cache" file data at all
2230 here would not be viable, as it would result is a very large amount of work
2251 here would not be viable, as it would result is a very large amount of work
@@ -2239,22 +2260,55 def filter_ambiguous_files(repo, file_da
2239 "different write with same mtime" issue virtually vanish. However,
2260 "different write with same mtime" issue virtually vanish. However,
2240 dirstate v1 cannot store such precision and a bunch of python-runtime,
2261 dirstate v1 cannot store such precision and a bunch of python-runtime,
2241 operating-system and filesystem parts do not provide us with such
2262 operating-system and filesystem parts do not provide us with such
2242 precision, so we have to operate as if it wasn't available."""
2263 precision, so we have to operate as if it wasn't available.
2264
2265 [3] https://lore.kernel.org/all/20241002-mgtime-v10-8-d1c4717f5284@kernel.org
2266 """
2243 ambiguous_mtime: FileData = {}
2267 ambiguous_mtime: FileData = {}
2244 now = timestamp.get_fs_now(repo.vfs)
2268 dirstate_v2 = repo.dirstate._use_dirstate_v2
2269 fs_now_result = None
2270 fast_enough_fs = True
2271 if dirstate_v2:
2272 fstype = util.getfstype(repo.vfs.base)
2273 # Exclude NFS right off the bat
2274 fast_enough_fs = fstype != b'nfs'
2275 if fstype is not None and fast_enough_fs:
2276 fs_now_result = timestamp.wait_until_fs_tick(repo.vfs)
2277
2278 if fs_now_result is None:
2279 try:
2280 now = timestamp.get_fs_now(repo.vfs)
2281 fs_now_result = (now, False)
2282 except OSError:
2283 pass
2284
2245 if fs_now_result is None:
2285 if fs_now_result is None:
2246 # we can't write to the FS, so we won't actually update
2286 # we can't write to the FS, so we won't actually update
2247 # the dirstate content anyway, no need to put cache
2287 # the dirstate content anyway, no need to put cache
2248 # information.
2288 # information.
2249 return None
2289 return None
2250 else:
2290 else:
2251 now_sec = now[0]
2252 now, timed_out = fs_now_result
2291 now, timed_out = fs_now_result
2253 if timed_out:
2292 if timed_out:
2254 fast_enough_fs = False
2293 fast_enough_fs = False
2255 for f, m in file_data.items():
2294 for f, m in file_data.items():
2256 if m is not None and m[2][0] >= now_sec:
2295 if m is not None:
2257 ambiguous_mtime[f] = (m[0], m[1], None)
2296 reliable = timestamp.make_mtime_reliable(m[2], now)
2297 if reliable is None or (
2298 reliable[2] and (not dirstate_v2 or not fast_enough_fs)
2299 ):
2300 # Either it's not reliable, or it's second ambiguous
2301 # and we're in dirstate-v1 or in a slow fs, so discard
2302 # the mtime.
2303 ambiguous_mtime[f] = (m[0], m[1], None)
2304 elif reliable[2]:
2305 # We need to remember that this time is "second ambiguous"
2306 # otherwise the next status might miss a subsecond change
2307 # if its "stat" doesn't provide nanoseconds.
2308 #
2309 # TODO make osutil.c understand nanoseconds when possible
2310 # (see timestamp.py for the same note)
2311 ambiguous_mtime[f] = (m[0], m[1], reliable)
2258 for f, m in ambiguous_mtime.items():
2312 for f, m in ambiguous_mtime.items():
2259 file_data[f] = m
2313 file_data[f] = m
2260 return file_data
2314 return file_data
General Comments 0
You need to be logged in to leave comments. Login now