aboutsummaryrefslogtreecommitdiffstats
path: root/library/python/filelock/__init__.py
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-11-01 10:10:09 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-11-01 10:20:27 +0300
commit864de19590b66130e4e89d94921eac85f68726a4 (patch)
tree81e0df7c643d88edd6bd593c013c9c498295f9b6 /library/python/filelock/__init__.py
parente130c0be0961db0ebe54f3c23c14ec5b940c32d0 (diff)
downloadydb-864de19590b66130e4e89d94921eac85f68726a4.tar.gz
Intermediate changes
commit_hash:afe2bc6dcfc79e6d34dabab9a0c92d3fa18bc87d
Diffstat (limited to 'library/python/filelock/__init__.py')
-rw-r--r--library/python/filelock/__init__.py93
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