Files
archive-clients/src/archive_clients/locking.py
T

50 lines
1.3 KiB
Python

"""Exclusive process ownership for the client state database."""
from __future__ import annotations
import fcntl
import os
from pathlib import Path
from typing import IO
class DatabaseLockedError(RuntimeError):
pass
class DatabaseLease:
def __init__(self, database: Path):
self.path = Path(f"{database}.lock")
self._file: IO[str] | None = None
def acquire(self) -> None:
if self._file is not None:
return
self.path.parent.mkdir(parents=True, exist_ok=True)
lock_file = self.path.open("a+", encoding="utf-8")
os.chmod(self.path, 0o600)
try:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError as exc:
lock_file.close()
raise DatabaseLockedError(
"client database is owned by a running process"
) from exc
self._file = lock_file
def release(self) -> None:
if self._file is None:
return
try:
fcntl.flock(self._file.fileno(), fcntl.LOCK_UN)
finally:
self._file.close()
self._file = None
def __enter__(self) -> "DatabaseLease":
self.acquire()
return self
def __exit__(self, *_: object) -> None:
self.release()