/proc/locks is host-wide and the kernel serves it over several read() calls, resuming by record index. When other processes add or drop locks between those reads the table shifts and entries are skipped, so a lock that is still held can read as gone. - tests: the live-DB preview and header-probe lock tests read their own process's locks from /proc/locks and flaked on busy CI hosts (1 in 12 on a quiet box; 9 in 12 with six processes churning locks). A shared tests/posix_lock_probe.own_posix_locks() reads /proc/self/fdinfo instead, which lists this process's locks per open file in one read. - hermes_state_lockowners: state_db_write_lock_holders() names the writer in the "state.db write lock unavailable" warning. One pass could skip the holder and log "no write-class lock held at the deadline". It now unions three passes; a skip rarely repeats.
32 lines
1.3 KiB
Python
32 lines
1.3 KiB
Python
"""This process's POSIX advisory locks on a file, read from ``/proc/self/fdinfo`` (Linux).
|
|
|
|
Not ``/proc/locks``: that table is host-wide and the kernel serves it over several ``read()``
|
|
calls, so lock churn in other processes (a parallel test run) shifts it mid-read and silently
|
|
skips entries — a lock this process still holds reads as cancelled. ``fdinfo`` lists this
|
|
process's locks per open file and is read in one call.
|
|
"""
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
def own_posix_locks(*paths) -> list[str]:
|
|
"""Sorted ``"POSIX ADVISORY <type> <pid> <dev>:<ino> <start> <end>"`` lines this process holds on *paths*."""
|
|
targets = set()
|
|
for path in paths:
|
|
try:
|
|
st = os.stat(path)
|
|
except OSError:
|
|
continue
|
|
targets.add((st.st_dev, st.st_ino))
|
|
found = set()
|
|
for fd in os.listdir("/proc/self/fd"):
|
|
try:
|
|
st = os.stat(f"/proc/self/fd/{fd}")
|
|
info = Path(f"/proc/self/fdinfo/{fd}").read_text(encoding="utf-8")
|
|
except OSError:
|
|
continue
|
|
if (st.st_dev, st.st_ino) in targets:
|
|
found.update(line.split(": ", 1)[1] for line in info.splitlines()
|
|
if line.startswith("lock:") and "POSIX ADVISORY" in line)
|
|
return sorted(found)
|