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 |
|
|
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 th |
|
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 |
|
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