[PATCH 1 of 4 STABLE V2] lock: avoid unintentional lock acquisition at failure of readlock

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Mon May 1 06:57:43 EDT 2017


At Mon, 01 May 2017 19:33:23 +0900,
FUJIWARA Katsunori wrote:
> 
> # HG changeset patch
> # User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
> # Date 1493634412 -32400
> #      Mon May 01 19:26:52 2017 +0900
> # Branch stable
> # Node ID 1a8c526828d5c2e86800c1680dec5cb4660f2af7
> # Parent  b59a292d0a536ee21e17d018829c36d8d4415569
> lock: avoid unintentional lock acquisition at failure of readlock
> 
> Acquiring lock by vfs.makelock() and getting lock holder (aka
> "locker") information by vfs.readlock() aren't atomic action.
> Therefore, failure of the former doesn't ensure success of the latter.
> 
> Before this patch, lock is unintentionally acquired, if
> self.parentlock is None (this is default value), and lock._readlock()
> returns None for ENOENT at vfs.readlock(), because these None are
> recognized as equal to each other.
> 
> In this case, lock symlink (or file) isn't created, even though lock
> is treated as acquired in memory.
> 
> To avoid this issue, this patch retries lock acquisition immediately,
> if lock._readlock() returns None "locker".
> 
> This issue will be covered by a test added in subsequent patch,
> because simple emulation of ENOENT at vfs.readlock() easily causes
> another issue.  "raising ENOENT only at the first vfs.readlock()
> invocation" is too complicated for unit test, IMHO.
> 
> diff --git a/mercurial/lock.py b/mercurial/lock.py
> --- a/mercurial/lock.py
> +++ b/mercurial/lock.py
> @@ -131,6 +131,9 @@ class lock(object):
>              except (OSError, IOError) as why:
>                  if why.errno == errno.EEXIST:
>                      locker = self._readlock()
> +                    if locker is None:
> +                        continue
> +
>                      # special case where a parent process holds the lock -- this
>                      # is different from the pid being different because we do
>                      # want the unlock and postrelease functions to be called,
> @@ -140,6 +143,9 @@ class lock(object):
>                          self.held = 1
>                          return
>                      locker = self._testlock(locker)
> +                    # keep checking "locker" for safety, even though
> +                    # current _testlock doesn't return None for
> +                    # non-None locker

Oops, I overlooked that self._testlock() returns None if lock is
removed for dead locker. I'll send revised series.

>                      if locker is not None:
>                          raise error.LockHeld(errno.EAGAIN,
>                                               self.vfs.join(self.f), self.desc,
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

-- 
----------------------------------------------------------------------
[FUJIWARA Katsunori]                             foozy at lares.dti.ne.jp


More information about the Mercurial-devel mailing list