Mercurial > hg
comparison hgext/fsmonitor/state.py @ 32816:1b25c648d5b7
fsmonitor: don't write out state if identity has changed (issue5581)
Inspired by the dirstate fix in dc7efa2826e4, this should fix any race
conditions with the fsmonitor state changing from underneath.
Since we now grab the wlock for any non-invalidate writes, the only situation
this appears to happen in is with a concurrent invalidation. Test that.
author | Siddharth Agarwal <sid0@fb.com> |
---|---|
date | Mon, 12 Jun 2017 15:34:31 -0700 |
parents | 15c998528c36 |
children | 718f7acd6d5e |
comparison
equal
deleted
inserted
replaced
32815:15e85dded933 | 32816:1b25c648d5b7 |
---|---|
11 import os | 11 import os |
12 import socket | 12 import socket |
13 import struct | 13 import struct |
14 | 14 |
15 from mercurial.i18n import _ | 15 from mercurial.i18n import _ |
16 from mercurial import pathutil | 16 from mercurial import ( |
17 pathutil, | |
18 util, | |
19 ) | |
17 | 20 |
18 _version = 4 | 21 _version = 4 |
19 _versionformat = ">I" | 22 _versionformat = ">I" |
20 | 23 |
21 class state(object): | 24 class state(object): |
22 def __init__(self, repo): | 25 def __init__(self, repo): |
23 self._vfs = repo.vfs | 26 self._vfs = repo.vfs |
24 self._ui = repo.ui | 27 self._ui = repo.ui |
25 self._rootdir = pathutil.normasprefix(repo.root) | 28 self._rootdir = pathutil.normasprefix(repo.root) |
26 self._lastclock = None | 29 self._lastclock = None |
30 self._identity = util.filestat(None) | |
27 | 31 |
28 self.mode = self._ui.config('fsmonitor', 'mode', default='on') | 32 self.mode = self._ui.config('fsmonitor', 'mode', default='on') |
29 self.walk_on_invalidate = self._ui.configbool( | 33 self.walk_on_invalidate = self._ui.configbool( |
30 'fsmonitor', 'walk_on_invalidate', False) | 34 'fsmonitor', 'walk_on_invalidate', False) |
31 self.timeout = float(self._ui.config( | 35 self.timeout = float(self._ui.config( |
33 | 37 |
34 def get(self): | 38 def get(self): |
35 try: | 39 try: |
36 file = self._vfs('fsmonitor.state', 'rb') | 40 file = self._vfs('fsmonitor.state', 'rb') |
37 except IOError as inst: | 41 except IOError as inst: |
42 self._identity = util.filestat(None) | |
38 if inst.errno != errno.ENOENT: | 43 if inst.errno != errno.ENOENT: |
39 raise | 44 raise |
40 return None, None, None | 45 return None, None, None |
46 | |
47 self._identity = util.filestat.fromfp(file) | |
41 | 48 |
42 versionbytes = file.read(4) | 49 versionbytes = file.read(4) |
43 if len(versionbytes) < 4: | 50 if len(versionbytes) < 4: |
44 self._ui.log( | 51 self._ui.log( |
45 'fsmonitor', 'fsmonitor: state file only has %d bytes, ' | 52 'fsmonitor', 'fsmonitor: state file only has %d bytes, ' |
88 def set(self, clock, ignorehash, notefiles): | 95 def set(self, clock, ignorehash, notefiles): |
89 if clock is None: | 96 if clock is None: |
90 self.invalidate() | 97 self.invalidate() |
91 return | 98 return |
92 | 99 |
100 # Read the identity from the file on disk rather than from the open file | |
101 # pointer below, because the latter is actually a brand new file. | |
102 identity = util.filestat.frompath(self._vfs.join('fsmonitor.state')) | |
103 if identity != self._identity: | |
104 self._ui.debug('skip updating fsmonitor.state: identity mismatch\n') | |
105 return | |
106 | |
93 try: | 107 try: |
94 file = self._vfs('fsmonitor.state', 'wb', atomictemp=True) | 108 file = self._vfs('fsmonitor.state', 'wb', atomictemp=True, |
109 checkambig=True) | |
95 except (IOError, OSError): | 110 except (IOError, OSError): |
96 self._ui.warn(_("warning: unable to write out fsmonitor state\n")) | 111 self._ui.warn(_("warning: unable to write out fsmonitor state\n")) |
97 return | 112 return |
98 | 113 |
99 with file: | 114 with file: |
109 try: | 124 try: |
110 os.unlink(os.path.join(self._rootdir, '.hg', 'fsmonitor.state')) | 125 os.unlink(os.path.join(self._rootdir, '.hg', 'fsmonitor.state')) |
111 except OSError as inst: | 126 except OSError as inst: |
112 if inst.errno != errno.ENOENT: | 127 if inst.errno != errno.ENOENT: |
113 raise | 128 raise |
129 self._identity = util.filestat(None) | |
114 | 130 |
115 def setlastclock(self, clock): | 131 def setlastclock(self, clock): |
116 self._lastclock = clock | 132 self._lastclock = clock |
117 | 133 |
118 def getlastclock(self): | 134 def getlastclock(self): |