fsmonitor: fsmonitor should send wlock notifications to watchman
authorEamonn Kent <ekent@fb.com>
Wed, 06 Dec 2017 18:37:49 -0800
changeset 35313 c67fb3bfe3a1
parent 35312 73abb81ccb28
child 35314 1fe3c8296cfe
fsmonitor: fsmonitor should send wlock notifications to watchman The fsmonitor extension should send state-enter and state-leave notifications to watchman when the wlock is acquired/release, respectively. This will allow watchman and watchman subscribers to customize behavior based on whether source control operations are occurring. Test Plan: Tested checkout, update and working copy changes with extension enabled. Differential Revision: https://phab.mercurial-scm.org/D1612
hgext/fsmonitor/__init__.py
--- a/hgext/fsmonitor/__init__.py	Wed Dec 06 18:37:49 2017 -0800
+++ b/hgext/fsmonitor/__init__.py	Wed Dec 06 18:37:49 2017 -0800
@@ -161,6 +161,9 @@
 configitem('fsmonitor', 'blacklistusers',
     default=list,
 )
+configitem('experimental', 'fsmonitor.transaction_notify',
+    default=False,
+)
 
 # This extension is incompatible with the following blacklisted extensions
 # and will disable itself when encountering one of these:
@@ -656,14 +659,18 @@
         self.enter()
 
     def enter(self):
-        # We explicitly need to take a lock here, before we proceed to update
-        # watchman about the update operation, so that we don't race with
-        # some other actor.  merge.update is going to take the wlock almost
-        # immediately anyway, so this is effectively extending the lock
-        # around a couple of short sanity checks.
+        # Make sure we have a wlock prior to sending notifications to watchman.
+        # We don't want to race with other actors. In the update case,
+        # merge.update is going to take the wlock almost immediately. We are
+        # effectively extending the lock around several short sanity checks.
         if self.oldnode is None:
             self.oldnode = self.repo['.'].node()
-        self._lock = self.repo.wlock()
+
+        if self.repo.currentwlock() is None:
+            if util.safehasattr(self.repo, 'wlocknostateupdate'):
+                self._lock = self.repo.wlocknostateupdate()
+            else:
+                self._lock = self.repo.wlock()
         self.need_leave = self._state(
             'state-enter',
             hex(self.oldnode))
@@ -784,4 +791,34 @@
                 orig = super(fsmonitorrepo, self).status
                 return overridestatus(orig, self, *args, **kwargs)
 
+            def wlocknostateupdate(self, *args, **kwargs):
+                return super(fsmonitorrepo, self).wlock(*args, **kwargs)
+
+            def wlock(self, *args, **kwargs):
+                l = super(fsmonitorrepo, self).wlock(*args, **kwargs)
+                if not ui.configbool(
+                    "experimental", "fsmonitor.transaction_notify"):
+                    return l
+                if l.held != 1:
+                    return l
+                origrelease = l.releasefn
+
+                def staterelease():
+                    if origrelease:
+                        origrelease()
+                    if l.stateupdate:
+                        l.stateupdate.exit()
+                        l.stateupdate = None
+
+                try:
+                    l.stateupdate = None
+                    l.stateupdate = state_update(self, name="hg.transaction")
+                    l.stateupdate.enter()
+                    l.releasefn = staterelease
+                except Exception as e:
+                    # Swallow any errors; fire and forget
+                    self.ui.log(
+                        'watchman', 'Exception in state update %s\n', e)
+                return l
+
         repo.__class__ = fsmonitorrepo