[PATCH] repair: fix transaction with no lock case in strip

Laurent Charignon lcharignon at fb.com
Mon Dec 28 16:28:32 CST 2015


Alternatively we could add a comment to ask extensions to have a lock when calling strip (this is what we do with obsolete.createmarkers).
Let me know what you think.

Thanks,

Laurent

> On Dec 28, 2015, at 2:20 PM, Laurent Charignon <lcharignon at fb.com> wrote:
> 
> # HG changeset patch
> # User Laurent Charignon <lcharignon at fb.com>
> # Date 1451341019 28800
> #      Mon Dec 28 14:16:59 2015 -0800
> # Node ID bef9c27c77012ad82dc954f97205c6042793d558
> # Parent  23541bdd1610c08af247f9c8719045cf247ce541
> repair: fix transaction with no lock case in strip
> 
> Before this patch, if repair.strip was called without holding a lock,
> the strip transaction would be made without lock. There was no occurrences of
> the problem in core (because we are always calling repair.strip while holding
> a lock), so I added a test with a test extension that shows the issue.
> 
> diff --git a/mercurial/repair.py b/mercurial/repair.py
> --- a/mercurial/repair.py
> +++ b/mercurial/repair.py
> @@ -158,11 +158,12 @@ def strip(ui, repo, nodelist, backup=Tru
>         del curtr  # avoid carrying reference to transaction for nothing
>         msg = _('programming error: cannot strip from inside a transaction')
>         raise error.Abort(msg, hint=_('contact your extension maintainer'))
> -
> -    tr = repo.transaction("strip")
> -    offset = len(tr.entries)
> -
> +    tr = lock = wlock = None
>     try:
> +        wlock = repo.wlock()
> +        lock = repo.lock()
> +        tr = repo.transaction("strip")
> +        offset = len(tr.entries)
>         tr.startgroup()
>         cl.strip(striprev, tr)
>         mfst.strip(striprev, tr)
> @@ -205,15 +206,13 @@ def strip(ui, repo, nodelist, backup=Tru
> 
>         for m in updatebm:
>             bm[m] = repo[newbmtarget].node()
> -        lock = tr = None
> +        tr = None
>         try:
> -            lock = repo.lock()
>             tr = repo.transaction('repair')
>             bm.recordchange(tr)
>             tr.close()
>         finally:
>             tr.release()
> -            lock.release()
> 
>         # remove undo files
>         for undovfs, undofile in repo.undofiles():
> @@ -236,6 +235,11 @@ def strip(ui, repo, nodelist, backup=Tru
>         if saveheads or savebases:
>             # Remove partial backup only if there were no exceptions
>             vfs.unlink(chgrpfile)
> +    finally:
> +        if lock is not None:
> +            lock.release()
> +        if wlock is not None:
> +            wlock.release()
> 
>     repo.destroyed()
> 
> diff --git a/tests/test-strip.t b/tests/test-strip.t
> --- a/tests/test-strip.t
> +++ b/tests/test-strip.t
> @@ -858,4 +858,18 @@ Error during post-close callback of the 
>   abort: boom
>   [255]
> 
> +Wrapping strip does not require to hold a lock
> 
> +  $ cat > ../lockstrip.py << EOF
> +  > from mercurial import cmdutil, repair
> +  > 
> +  > cmdtable = {}
> +  > command = cmdutil.command(cmdtable)
> +  > 
> +  > @command('testlockstrip')
> +  > def testlock(ui, repo):
> +  >     repair.strip(repo.ui, repo, [repo['.'].node()])
> +  > EOF
> +  $ hg testlockstrip --config extensions.lockstrip=$TESTTMP/lockstrip.py
> +  saved backup bundle to * (glob)
> +
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel



More information about the Mercurial-devel mailing list