aboutsummaryrefslogtreecommitdiffstats
path: root/library/python/filelock/__init__.py
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/python/filelock/__init__.py
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/python/filelock/__init__.py')
-rw-r--r--library/python/filelock/__init__.py122
1 files changed, 122 insertions, 0 deletions
diff --git a/library/python/filelock/__init__.py b/library/python/filelock/__init__.py
new file mode 100644
index 0000000000..f81ff67f37
--- /dev/null
+++ b/library/python/filelock/__init__.py
@@ -0,0 +1,122 @@
+import errno
+import logging
+import os
+import sys
+
+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)
+
+
+class AbstractFileLock(object):
+
+ def __init__(self, path):
+ self.path = path
+
+ def acquire(self, blocking=True):
+ raise NotImplementedError
+
+ def release(self):
+ raise NotImplementedError
+
+ def __enter__(self):
+ self.acquire()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.release()
+
+
+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)
+
+ def acquire(self, blocking=True):
+ import errno
+ try:
+ self._locker(self._lock, blocking)
+ except IOError as e:
+ if e.errno in (errno.EAGAIN, errno.EACCES) and not blocking:
+ return False
+ raise
+ return True
+
+ def release(self):
+ self._unlocker(self._lock)
+
+ def __del__(self):
+ if hasattr(self, "_lock"):
+ self._lock.close()
+
+
+class _WinFileLock(AbstractFileLock):
+ """
+ Based on LockFile / UnlockFile from win32 API
+ https://msdn.microsoft.com/en-us/library/windows/desktop/aa365202(v=vs.85).aspx
+ """
+
+ _LOCKED_BYTES_NUM = 1
+
+ def __init__(self, path):
+ super(_WinFileLock, self).__init__(path)
+ self._lock = None
+ try:
+ with file(path, 'w') as lock_file:
+ lock_file.write(" " * 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)
+ else:
+ return False
+
+ def release(self):
+ if self._lock:
+ library.python.windows.unlock_file(self._lock, 0, self._LOCKED_BYTES_NUM, raises=False)
+ self._lock.close()
+ self._lock = None
+
+
+class FileLock(AbstractFileLock):
+
+ def __init__(self, path):
+ super(FileLock, self).__init__(path)
+
+ if sys.platform.startswith('win'):
+ self._lock = _WinFileLock(path)
+ else:
+ self._lock = _NixFileLock(path)
+
+ def acquire(self, blocking=True):
+ logger.debug('Acquiring filelock (blocking=%s): %s', blocking, self.path)
+ return self._lock.acquire(blocking)
+
+ def release(self):
+ logger.debug('Ensuring filelock released: %s', self.path)
+ return self._lock.release()