New locking idea (was Re: [PATCH 0 of 2] new log option --tolerant)

Matt Mackall mpm at
Mon Dec 13 00:01:59 CST 2010

On Sun, 2010-12-12 at 04:19 +0100, Jason Harris wrote:
> Ok I have come across the following issue in MacHg. (The issue is general to GUI
> clients but I will use MacHg in the description below. Substitute your favorite
> other GUI client which does things in a multithreaded way.)
> I need Mercurial to treat the revision ranges I pass it tolerantly. The
> situation arises in MacHg because of its ubiquitous use of multi-threaded-ness
> and some history altering operations, like strip, histedit, collapse, rebase
> with --collapse, etc. These history editing operations can decrease the number
> of changesets after an operation. Now just after the operation the "hg tip"
> command might be still loading and yet MacHg might need to also fire off some
> "hg log" commands to update the history view of the repository. Ie we might be
> issuing in essence a command like hg log --rev "0:23" --template "<stuff>" but
> maybe after the operation the repository only has 19 changesets in it. We don't
> want this 'hg log' command we just issued to fail with an abort: unknown
> revision '23'!

I'm not thrilled with this approach, but you've got me thinking about
alternatives to fix this long-standing issue.

To have complete locking for strip and friends, we need to satisfy the

 - any number of simultaneous readers
 - appenders exclude other appenders, but not readers
 - destroyers exclude readers and appenders

Mercurial currently handles the first two, but not the last (limited to
extensions and the rollback command). To add appenders to the mix, we
need to be able to do the following:

 - keep track of an arbitrary number of readers efficiently
 - atomically exclude new readers
 - keep destroyers from waiting forever

My thought is we can accomplish this with a new reader/ lock directory
in .hg/store. When the directory exists, readers just need to create a
uniquely-named lock file in the directory to indicate they're in the
process of reading and delete it when they're done. This obviously isn't
as fast as doing nothing, but should still be pretty fast.

When a destroyer wants to take over, it first takes the write lock to
exclude appenders and other destroyers. It then attempts to delete the
reader directory. If there are no read locks in the directory, rmdir
will succeed. If rmdir fails, it loops until it succeeds. To unlock, it
recreates the reader directory, and releases the write lock.

However, a destroyer may wait forever if new readers keep appearing. To
prevent this, it renames the reader directory to reader-blocked. New
readers will wait for the reader directory to reappear, while old
readers will try deleting their read locks from both locations.

This relies on atomicity of rmdir vs file creation, which is a pretty
reasonable requirement as failing it would mean a filesystem could
'leak' files.

In addition to fixing Jason's problem, this will also let us safely do
things like running strip on a repo that's being served by hgweb.

Mathematics is the supreme nostalgia of our time.

More information about the Mercurial-devel mailing list