MCPcopy Create free account
hub / github.com/tox-dev/filelock / SoftReadWriteLock

Class SoftReadWriteLock

src/filelock/_soft_rw/_sync.py:115–749  ·  view source on GitHub ↗

Cross-process and cross-host reader/writer lock built on :class:`SoftFileLock` primitives. Use this class instead of :class:`~filelock.ReadWriteLock` when the lock file lives on a network filesystem (NFS, Lustre with ``-o flock``, HPC cluster shared storage). ``ReadWriteLock`` is backe

Source from the content-addressed store, hash-verified

113
114
115class SoftReadWriteLock(metaclass=_SoftRWMeta):
116 """
117 Cross-process and cross-host reader/writer lock built on :class:`SoftFileLock` primitives.
118
119 Use this class instead of :class:`~filelock.ReadWriteLock` when the lock file lives on a network
120 filesystem (NFS, Lustre with ``-o flock``, HPC cluster shared storage). ``ReadWriteLock`` is backed
121 by SQLite and cannot run on NFS because SQLite's ``fcntl`` locking is unreliable there.
122
123 Layout on disk for a lock at ``foo.lock``:
124
125 - ``foo.lock.state`` — a :class:`SoftFileLock` taken only during state transitions (microseconds).
126 - ``foo.lock.write`` — writer marker; its presence means a writer is claiming or holding the lock.
127 - ``foo.lock.readers/<host>.<pid>.<uuid>`` — one file per reader.
128
129 Each marker stores a random token (``secrets.token_hex(16)``), the holder's pid, and the holder's
130 hostname. A daemon heartbeat thread refreshes ``mtime`` on every held marker. A marker whose mtime
131 has not advanced in ``stale_threshold`` seconds may be evicted by any process on any host, giving
132 correct behavior when a compute node crashes with a lock held.
133
134 Writer acquire is two-phase and writer-preferring: phase 1 claims ``.write`` (blocking any new
135 reader), phase 2 waits for existing readers to drain. Writer starvation is impossible.
136
137 Reentrancy, upgrade/downgrade rules, thread pinning, and singleton caching by resolved path match
138 :class:`~filelock.ReadWriteLock`.
139
140 Forking invalidates the inherited instance in the child so the child cannot double-own the lock with its parent;
141 ``release()`` on that instance is a no-op, and the child must construct a new instance if it needs a lock.
142
143 Trust boundary: protects against same-UID non-cooperating processes (one host or cross-host) and
144 same-host different-UID users via ``0o600`` / ``0o700`` permissions. Does not protect against root
145 compromise, NTP tampering on same-UID cross-host nodes, or multi-tenant mounts where hostile
146 co-tenants share the UID.
147
148 :param lock_file: path to the lock file; sidecar state/write/readers live next to it
149 :param timeout: maximum wait time in seconds; ``-1`` means block indefinitely
150 :param blocking: if ``False``, raise :class:`~filelock.Timeout` immediately on contention
151 :param is_singleton: if ``True``, reuse existing instances for the same resolved path
152 :param heartbeat_interval: seconds between heartbeat refreshes; default 30 s
153 :param stale_threshold: seconds of ``mtime`` inactivity before a marker is stale; defaults to
154 ``3 * heartbeat_interval``, matching etcd&#x27;s ``LeaseKeepAlive`` convention
155 :param poll_interval: seconds between acquire retries under contention; default 0.25 s
156
157 .. versionadded:: 3.27.0
158
159 """
160
161 _instances: WeakValueDictionary[Path, SoftReadWriteLock] = WeakValueDictionary()
162 _instances_lock = threading.RLock()
163
164 def __init__( # ruff:ignore[too-many-arguments] # public constructor: one parameter per documented lock option
165 self,
166 lock_file: str | os.PathLike[str],
167 timeout: float = -1,
168 *,
169 blocking: bool = True,
170 is_singleton: bool = True, # ruff:ignore[unused-method-argument] # consumed by _SoftRWMeta.__call__
171 heartbeat_interval: float = 30.0,
172 stale_threshold: float | None = None,

Calls

no outgoing calls