diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-11-01 10:10:09 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-11-01 10:20:27 +0300 |
commit | 864de19590b66130e4e89d94921eac85f68726a4 (patch) | |
tree | 81e0df7c643d88edd6bd593c013c9c498295f9b6 /library/python/filelock/__init__.py | |
parent | e130c0be0961db0ebe54f3c23c14ec5b940c32d0 (diff) | |
download | ydb-864de19590b66130e4e89d94921eac85f68726a4.tar.gz |
Intermediate changes
commit_hash:afe2bc6dcfc79e6d34dabab9a0c92d3fa18bc87d
Diffstat (limited to 'library/python/filelock/__init__.py')
-rw-r--r-- | library/python/filelock/__init__.py | 93 |
1 files changed, 73 insertions, 20 deletions
diff --git a/library/python/filelock/__init__.py b/library/python/filelock/__init__.py index fae89713d3..545844b598 100644 --- a/library/python/filelock/__init__.py +++ b/library/python/filelock/__init__.py @@ -1,19 +1,17 @@ +import collections import errno import logging import os +import struct import sys +import time import library.python.windows logger = logging.getLogger(__name__) - -def set_close_on_exec(stream): - if library.python.windows.on_win(): - library.python.windows.set_handle_information(stream, inherit=False) - else: - import fcntl - fcntl.fcntl(stream, fcntl.F_SETFD, fcntl.FD_CLOEXEC) +# python2 compat +os_O_CLOEXEC = getattr(os, 'O_CLOEXEC', 1 << 19) class AbstractFileLock(object): @@ -40,13 +38,15 @@ class _NixFileLock(AbstractFileLock): def __init__(self, path): super(_NixFileLock, self).__init__(path) from fcntl import flock, LOCK_EX, LOCK_UN, LOCK_NB + self._locker = lambda lock, blocking: flock(lock, LOCK_EX if blocking else LOCK_EX | LOCK_NB) self._unlocker = lambda lock: flock(lock, LOCK_UN) - self._lock = open(self.path, 'a') - set_close_on_exec(self._lock) + # nonbuffered random access rw mode + self._lock = os.fdopen(os.open(self.path, os.O_RDWR | os.O_CREAT | os_O_CLOEXEC), 'r+b', 0) def acquire(self, blocking=True): import errno + try: self._locker(self._lock, blocking) except IOError as e: @@ -73,34 +73,32 @@ class _WinFileLock(AbstractFileLock): def __init__(self, path): super(_WinFileLock, self).__init__(path) - self._lock = None + # nonbuffered random access rw mode + self._lock = os.fdopen(os.open(self.path, os.O_RDWR | os.O_CREAT | os.O_BINARY | os.O_NOINHERIT), 'r+b', 0) try: - with open(path, 'w') as lock_file: - lock_file.write(" " * self._LOCKED_BYTES_NUM) + self._lock.write(b' ' * self._LOCKED_BYTES_NUM) except IOError as e: if e.errno != errno.EACCES or not os.path.isfile(path): raise def acquire(self, blocking=True): - self._lock = open(self.path) - set_close_on_exec(self._lock) - - import time locked = False while not locked: locked = library.python.windows.lock_file(self._lock, 0, self._LOCKED_BYTES_NUM, raises=False) if locked: return True if blocking: - time.sleep(.5) + time.sleep(0.5) else: return False def release(self): if self._lock: library.python.windows.unlock_file(self._lock, 0, self._LOCKED_BYTES_NUM, raises=False) + + def __del__(self): + if getattr(self, '_lock', False): self._lock.close() - self._lock = None class FileLock(AbstractFileLock): @@ -114,9 +112,64 @@ class FileLock(AbstractFileLock): self._lock = _NixFileLock(path) def acquire(self, blocking=True): - logger.debug('Acquiring filelock (blocking=%s): %s', blocking, self.path) + logger.debug('Acquiring %s (blocking=%s): %s', type(self).__name__, blocking, self.path) return self._lock.acquire(blocking) def release(self): - logger.debug('Ensuring filelock released: %s', self.path) + logger.debug('Ensuring %s released: %s', type(self).__name__, self.path) return self._lock.release() + + +_LockInfo = collections.namedtuple('LockInfo', ['pid', 'time']) + + +class _PidLockMixin(object): + _LockedBytes = 0 + _InfoFormat = 'QQ' + _InfoFmtSize = struct.calcsize(_InfoFormat) + + def _register_lock(self): + self._lock.seek(self._LockedBytes, os.SEEK_SET) + self._lock.write(struct.pack(self._InfoFormat, os.getpid(), int(time.time()))) + + @property + def info(self): + self._lock.seek(self._LockedBytes, os.SEEK_SET) + try: + data = struct.unpack(self._InfoFormat, self._lock.read(self._InfoFmtSize)) + except struct.error: + data = 0, 0 + return _LockInfo(*data) + + +class _NixPidFileLock(_NixFileLock, _PidLockMixin): + def acquire(self, blocking=True): + if super(_NixPidFileLock, self).acquire(blocking): + self._register_lock() + return True + return False + + +class _WinPidFileLock(_WinFileLock, _PidLockMixin): + _LockedBytes = _WinFileLock._LOCKED_BYTES_NUM + + def acquire(self, blocking=True): + if super(_WinPidFileLock, self).acquire(blocking): + self._register_lock() + return True + return False + + +class PidFileLock(FileLock): + + def __init__(self, path): + AbstractFileLock.__init__(self, path) + + if sys.platform.startswith('win'): + self._lock = _WinPidFileLock(path) + else: + self._lock = _NixPidFileLock(path) + + @property + def info(self): + return self._lock.info |