feat: add dynamic owner binding

This commit is contained in:
Alex
2026-05-24 21:53:23 +03:00
parent a36abd7fdd
commit 186139f796
7 changed files with 718 additions and 13 deletions
+40 -6
View File
@@ -3,13 +3,24 @@
from __future__ import annotations
import fcntl
import os
import time
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
# Попытка импорта fcntl (для Unix-систем) или msvcrt (для Windows)
try:
import fcntl
_HAS_FCNTL = True
except ImportError:
_HAS_FCNTL = False
try:
import msvcrt
_HAS_MSVCRT = True
except ImportError:
_HAS_MSVCRT = False
class LockUnavailable(RuntimeError):
"""Raised when another process already owns the sync lock."""
@@ -19,10 +30,23 @@ class LockUnavailable(RuntimeError):
def exclusive_file_lock(path: Path) -> Iterator[None]:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("a+", encoding="utf-8") as lock_file:
try:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError as exc:
raise LockUnavailable(f"Lock is already held: {path}") from exc
fd = lock_file.fileno()
if _HAS_FCNTL:
try:
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError as exc:
raise LockUnavailable(f"Lock is already held: {path}") from exc
elif _HAS_MSVCRT:
try:
lock_file.seek(0)
# Блокируем первые 64 байта файла без ожидания (non-blocking)
msvcrt.locking(fd, msvcrt.LK_NBLCK, 64)
except (OSError, IOError) as exc:
raise LockUnavailable(f"Lock is already held: {path}") from exc
else:
# Резервный вариант, если блокировки недоступны на платформе
pass
try:
lock_file.seek(0)
@@ -31,4 +55,14 @@ def exclusive_file_lock(path: Path) -> Iterator[None]:
lock_file.flush()
yield
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
if _HAS_FCNTL:
try:
fcntl.flock(fd, fcntl.LOCK_UN)
except Exception:
pass
elif _HAS_MSVCRT:
try:
lock_file.seek(0)
msvcrt.locking(fd, msvcrt.LK_UNLCK, 64)
except Exception:
pass