123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- # Copyright 2010 Matt Chaput. All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are met:
- #
- # 1. Redistributions of source code must retain the above copyright notice,
- # this list of conditions and the following disclaimer.
- #
- # 2. Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution.
- #
- # THIS SOFTWARE IS PROVIDED BY MATT CHAPUT ``AS IS'' AND ANY EXPRESS OR
- # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- # EVENT SHALL MATT CHAPUT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #
- # The views and conclusions contained in the software and documentation are
- # those of the authors and should not be interpreted as representing official
- # policies, either expressed or implied, of Matt Chaput.
- """
- This module contains classes implementing exclusive locks for platforms with
- fcntl (UNIX and Mac OS X) and Windows. Whoosh originally used directory
- creation as a locking method, but it had the problem that if the program
- crashed the lock directory was left behind and would keep the index locked
- until it was cleaned up. Using OS-level file locks fixes this.
- """
- import errno
- import os
- import sys
- import time
- def try_for(fn, timeout=5.0, delay=0.1):
- """Calls ``fn`` every ``delay`` seconds until it returns True or
- ``timeout`` seconds elapse. Returns True if the lock was acquired, or False
- if the timeout was reached.
- :param timeout: Length of time (in seconds) to keep retrying to acquire the
- lock. 0 means return immediately. Only used when blocking is False.
- :param delay: How often (in seconds) to retry acquiring the lock during
- the timeout period. Only used when blocking is False and timeout > 0.
- """
- until = time.time() + timeout
- v = fn()
- while not v and time.time() < until:
- time.sleep(delay)
- v = fn()
- return v
- class LockBase(object):
- """Base class for file locks.
- """
- def __init__(self, filename):
- self.fd = None
- self.filename = filename
- self.locked = False
- def __del__(self):
- if hasattr(self, "fd") and self.fd:
- try:
- self.release()
- except:
- pass
- def acquire(self, blocking=False):
- """Acquire the lock. Returns True if the lock was acquired.
- :param blocking: if True, call blocks until the lock is acquired.
- This may not be available on all platforms. On Windows, this is
- actually just a delay of 10 seconds, rechecking every second.
- """
- pass
- def release(self):
- pass
- class FcntlLock(LockBase):
- """File lock based on UNIX-only fcntl module.
- """
- def acquire(self, blocking=False):
- import fcntl # @UnresolvedImport
- flags = os.O_CREAT | os.O_WRONLY
- self.fd = os.open(self.filename, flags)
- mode = fcntl.LOCK_EX
- if not blocking:
- mode |= fcntl.LOCK_NB
- try:
- fcntl.flock(self.fd, mode)
- self.locked = True
- return True
- except IOError:
- e = sys.exc_info()[1]
- if e.errno not in (errno.EAGAIN, errno.EACCES):
- raise
- os.close(self.fd)
- self.fd = None
- return False
- def release(self):
- if self.fd is None:
- raise Exception("Lock was not acquired")
- import fcntl # @UnresolvedImport
- fcntl.flock(self.fd, fcntl.LOCK_UN)
- os.close(self.fd)
- self.fd = None
- class MsvcrtLock(LockBase):
- """File lock based on Windows-only msvcrt module.
- """
- def acquire(self, blocking=False):
- import msvcrt # @UnresolvedImport
- flags = os.O_CREAT | os.O_WRONLY
- mode = msvcrt.LK_NBLCK
- if blocking:
- mode = msvcrt.LK_LOCK
- self.fd = os.open(self.filename, flags)
- try:
- msvcrt.locking(self.fd, mode, 1)
- return True
- except IOError:
- e = sys.exc_info()[1]
- if e.errno not in (errno.EAGAIN, errno.EACCES, errno.EDEADLK):
- raise
- os.close(self.fd)
- self.fd = None
- return False
- def release(self):
- import msvcrt # @UnresolvedImport
- if self.fd is None:
- raise Exception("Lock was not acquired")
- msvcrt.locking(self.fd, msvcrt.LK_UNLCK, 1)
- os.close(self.fd)
- self.fd = None
- if os.name == "nt":
- FileLock = MsvcrtLock
- else:
- FileLock = FcntlLock
|