mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 01:07:15 +03:00
5380dea2a7
Python 2.6 introduced the "except type as instance" syntax, replacing the "except type, instance" syntax that came before. Python 3 dropped support for the latter syntax. Since we no longer support Python 2.4 or 2.5, we have no need to continue supporting the "except type, instance". This patch mass rewrites the exception syntax to be Python 2.6+ and Python 3 compatible. This patch was produced by running `2to3 -f except -w -n .`.
157 lines
5.0 KiB
Python
157 lines
5.0 KiB
Python
# lock.py - simple advisory locking scheme for mercurial
|
|
#
|
|
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
import util, error
|
|
import errno, os, socket, time
|
|
import warnings
|
|
|
|
class lock(object):
|
|
'''An advisory lock held by one process to control access to a set
|
|
of files. Non-cooperating processes or incorrectly written scripts
|
|
can ignore Mercurial's locking scheme and stomp all over the
|
|
repository, so don't do that.
|
|
|
|
Typically used via localrepository.lock() to lock the repository
|
|
store (.hg/store/) or localrepository.wlock() to lock everything
|
|
else under .hg/.'''
|
|
|
|
# lock is symlink on platforms that support it, file on others.
|
|
|
|
# symlink is used because create of directory entry and contents
|
|
# are atomic even over nfs.
|
|
|
|
# old-style lock: symlink to pid
|
|
# new-style lock: symlink to hostname:pid
|
|
|
|
_host = None
|
|
|
|
def __init__(self, vfs, file, timeout=-1, releasefn=None, desc=None):
|
|
self.vfs = vfs
|
|
self.f = file
|
|
self.held = 0
|
|
self.timeout = timeout
|
|
self.releasefn = releasefn
|
|
self.desc = desc
|
|
self.postrelease = []
|
|
self.pid = os.getpid()
|
|
self.delay = self.lock()
|
|
|
|
def __del__(self):
|
|
if self.held:
|
|
warnings.warn("use lock.release instead of del lock",
|
|
category=DeprecationWarning,
|
|
stacklevel=2)
|
|
|
|
# ensure the lock will be removed
|
|
# even if recursive locking did occur
|
|
self.held = 1
|
|
|
|
self.release()
|
|
|
|
def lock(self):
|
|
timeout = self.timeout
|
|
while True:
|
|
try:
|
|
self.trylock()
|
|
return self.timeout - timeout
|
|
except error.LockHeld as inst:
|
|
if timeout != 0:
|
|
time.sleep(1)
|
|
if timeout > 0:
|
|
timeout -= 1
|
|
continue
|
|
raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
|
|
inst.locker)
|
|
|
|
def trylock(self):
|
|
if self.held:
|
|
self.held += 1
|
|
return
|
|
if lock._host is None:
|
|
lock._host = socket.gethostname()
|
|
lockname = '%s:%s' % (lock._host, self.pid)
|
|
while not self.held:
|
|
try:
|
|
self.vfs.makelock(lockname, self.f)
|
|
self.held = 1
|
|
except (OSError, IOError) as why:
|
|
if why.errno == errno.EEXIST:
|
|
locker = self.testlock()
|
|
if locker is not None:
|
|
raise error.LockHeld(errno.EAGAIN,
|
|
self.vfs.join(self.f), self.desc,
|
|
locker)
|
|
else:
|
|
raise error.LockUnavailable(why.errno, why.strerror,
|
|
why.filename, self.desc)
|
|
|
|
def testlock(self):
|
|
"""return id of locker if lock is valid, else None.
|
|
|
|
If old-style lock, we cannot tell what machine locker is on.
|
|
with new-style lock, if locker is on this machine, we can
|
|
see if locker is alive. If locker is on this machine but
|
|
not alive, we can safely break lock.
|
|
|
|
The lock file is only deleted when None is returned.
|
|
|
|
"""
|
|
try:
|
|
locker = self.vfs.readlock(self.f)
|
|
except (OSError, IOError) as why:
|
|
if why.errno == errno.ENOENT:
|
|
return None
|
|
raise
|
|
try:
|
|
host, pid = locker.split(":", 1)
|
|
except ValueError:
|
|
return locker
|
|
if host != lock._host:
|
|
return locker
|
|
try:
|
|
pid = int(pid)
|
|
except ValueError:
|
|
return locker
|
|
if util.testpid(pid):
|
|
return locker
|
|
# if locker dead, break lock. must do this with another lock
|
|
# held, or can race and break valid lock.
|
|
try:
|
|
l = lock(self.vfs, self.f + '.break', timeout=0)
|
|
self.vfs.unlink(self.f)
|
|
l.release()
|
|
except error.LockError:
|
|
return locker
|
|
|
|
def release(self):
|
|
"""release the lock and execute callback function if any
|
|
|
|
If the lock has been acquired multiple times, the actual release is
|
|
delayed to the last release call."""
|
|
if self.held > 1:
|
|
self.held -= 1
|
|
elif self.held == 1:
|
|
self.held = 0
|
|
if os.getpid() != self.pid:
|
|
# we forked, and are not the parent
|
|
return
|
|
try:
|
|
if self.releasefn:
|
|
self.releasefn()
|
|
finally:
|
|
try:
|
|
self.vfs.unlink(self.f)
|
|
except OSError:
|
|
pass
|
|
for callback in self.postrelease:
|
|
callback()
|
|
|
|
def release(*locks):
|
|
for lock in locks:
|
|
if lock is not None:
|
|
lock.release()
|