view mercurial/lock.py @ 2323:c58a403aa830

setup.py: install packagescan before any mercurial modules is imported Further the installation of packagescan over demandload is moved to the packagescan module. I added as well few more comments in the packagescan module to avoid the wrong use of package scan in the future. Reason: mercurial.packagescan acts as fake mercurial.demandload during a py2exe run. Unfortunatly the import of mercurial.version in setup.py is done before mercurial.packagescan is installed. This results in few imports without mercurial.packagescan in charge and therefore not all dependend modules are detected when running mercurial.packagescan.getmodules later e.g. winerror is missed.
author Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
date Fri, 19 May 2006 08:54:28 -0700
parents ff5c9a92f556
children 0875cda033fd
line wrap: on
line source

# lock.py - simple locking scheme for mercurial
#
# Copyright 2005 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

from demandload import *
demandload(globals(), 'errno os socket time util')

class LockException(IOError):
    def __init__(self, errno, strerror, filename, desc):
        IOError.__init__(self, errno, strerror, filename)
        self.desc = desc

class LockHeld(LockException):
    def __init__(self, errno, filename, desc, locker):
        LockException.__init__(self, errno, 'Lock held', filename, desc)
        self.locker = locker

class LockUnavailable(LockException):
    pass

class lock(object):
    # 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

    def __init__(self, file, timeout=-1, releasefn=None, desc=None):
        self.f = file
        self.held = 0
        self.timeout = timeout
        self.releasefn = releasefn
        self.id = None
        self.host = None
        self.pid = None
        self.desc = desc
        self.lock()

    def __del__(self):
        self.release()

    def lock(self):
        timeout = self.timeout
        while 1:
            try:
                self.trylock()
                return 1
            except LockHeld, inst:
                if timeout != 0:
                    time.sleep(1)
                    if timeout > 0:
                        timeout -= 1
                    continue
                raise LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
                               inst.locker)

    def trylock(self):
        if self.id is None:
            self.host = socket.gethostname()
            self.pid = os.getpid()
            self.id = '%s:%s' % (self.host, self.pid)
        while not self.held:
            try:
                util.makelock(self.id, self.f)
                self.held = 1
            except (OSError, IOError), why:
                if why.errno == errno.EEXIST:
                    locker = self.testlock()
                    if locker:
                        raise LockHeld(errno.EAGAIN, self.f, self.desc,
                                       locker)
                else:
                    raise 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.
        locker = util.readlock(self.f)
        c = locker.find(':')
        if c == -1:
            return locker
        host = locker[:c]
        if host != self.host:
            return locker
        try:
            pid = int(locker[c+1:])
        except:
            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.f + '.break')
            l.trylock()
            os.unlink(self.f)
            l.release()
        except (LockHeld, LockUnavailable):
            return locker

    def release(self):
        if self.held:
            self.held = 0
            if self.releasefn:
                self.releasefn()
            try:
                os.unlink(self.f)
            except: pass