[PATCH 2 of 4 STABLE V3] lock: avoid unintentional lock acquisition at failure of readlock

Yuya Nishihara yuya at tcha.org
Mon May 1 09:23:18 EDT 2017


On Mon, 01 May 2017 20:02:50 +0900, FUJIWARA Katsunori wrote:
> # HG changeset patch
> # User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
> # Date 1493636353 -32400
> #      Mon May 01 19:59:13 2017 +0900
> # Branch stable
> # Node ID cc349553a2126c8e28bc24402bab3bad7981c8c0
> # Parent  e1938d6051da1eff0c0d78fc7aae0d426e99aad2
> 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 ENOEXIST
> causes failure of vfs.readlock() while 5 times retrying, because
> lock._trylock() returns to caller silently after retrying, and
> lock.lock() assumes that lock._trylock() returns successfully only if
> lock is acquired.
> 
> 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 makes lock._trylock() raise
> LockHeld(EAGAIN) at the end of it, if lock isn't acquired while
> retrying.
> 
> An empty "locker" meaning "busy for frequent lock/unlock by many
> processes" might appear in an abortion message, if lock acquisition
> fails. Therefore, this patch also does:
> 
>   - use '%r' to increase visibility of "locker", even if it is empty
>   - show hint message to explain what empty "locker" means
> 
> diff --git a/mercurial/lock.py b/mercurial/lock.py
> --- a/mercurial/lock.py
> +++ b/mercurial/lock.py
> @@ -151,6 +151,12 @@ class lock(object):
>                      raise error.LockUnavailable(why.errno, why.strerror,
>                                                  why.filename, self.desc)
>  
> +        if not self.held:
> +            # use empty locker to mean "busy for frequent lock/unlock
> +            # by many processes"
> +            raise error.LockHeld(errno.EAGAIN,
> +                                 self.vfs.join(self.f), self.desc, "")

For reference, this bug would probably exist since 3b6e5914edd8 "lock: loop
a finite number of times in trylock (issue4787)." Before, trylock() would
never return until lock was taken.


More information about the Mercurial-devel mailing list