diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/python/filelock/__init__.py | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/python/filelock/__init__.py')
-rw-r--r-- | library/python/filelock/__init__.py | 122 |
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() |