Skip to content

Commit

Permalink
pythongh-70795: Rework RLock documentation (python#103853)
Browse files Browse the repository at this point in the history
Attempted to simultaneously reduce verbosity, while more descriptively
describing behavior.

Fix links (RLock acquire/release previously linking to Lock
acquire/release, seems like bad copy pasta).

Add a seealso for with-locks.

Switch section to use bullet points.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM>
  • Loading branch information
3 people authored and estyxx committed Jul 17, 2024
1 parent f556097 commit 1fca3b4
Showing 1 changed file with 51 additions and 24 deletions.
75 changes: 51 additions & 24 deletions Doc/library/threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -598,14 +598,25 @@ and "recursion level" in addition to the locked/unlocked state used by primitive
locks. In the locked state, some thread owns the lock; in the unlocked state,
no thread owns it.

To lock the lock, a thread calls its :meth:`~RLock.acquire` method; this
returns once the thread owns the lock. To unlock the lock, a thread calls
its :meth:`~Lock.release` method. :meth:`~Lock.acquire`/:meth:`~Lock.release`
call pairs may be nested; only the final :meth:`~Lock.release` (the
:meth:`~Lock.release` of the outermost pair) resets the lock to unlocked and
allows another thread blocked in :meth:`~Lock.acquire` to proceed.
Threads call a lock's :meth:`~RLock.acquire` method to lock it,
and its :meth:`~Lock.release` method to unlock it.

Reentrant locks also support the :ref:`context management protocol <with-locks>`.
.. note::

Reentrant locks support the :ref:`context management protocol <with-locks>`,
so it is recommended to use :keyword:`with` instead of manually calling
:meth:`~RLock.acquire` and :meth:`~RLock.release`
to handle acquiring and releasing the lock for a block of code.

RLock's :meth:`~RLock.acquire`/:meth:`~RLock.release` call pairs may be nested,
unlike Lock's :meth:`~Lock.acquire`/:meth:`~Lock.release`. Only the final
:meth:`~RLock.release` (the :meth:`~Lock.release` of the outermost pair) resets
the lock to an unlocked state and allows another thread blocked in
:meth:`~RLock.acquire` to proceed.

:meth:`~RLock.acquire`/:meth:`~RLock.release` must be used in pairs: each acquire
must have a release in the thread that has acquired the lock. Failing to
call release as many times the lock has been acquired can lead to deadlock.


.. class:: RLock()
Expand All @@ -624,25 +635,41 @@ Reentrant locks also support the :ref:`context management protocol <with-locks>`

Acquire a lock, blocking or non-blocking.

When invoked without arguments: if this thread already owns the lock, increment
the recursion level by one, and return immediately. Otherwise, if another
thread owns the lock, block until the lock is unlocked. Once the lock is
unlocked (not owned by any thread), then grab ownership, set the recursion level
to one, and return. If more than one thread is blocked waiting until the lock
is unlocked, only one at a time will be able to grab ownership of the lock.
There is no return value in this case.
.. seealso::

When invoked with the *blocking* argument set to ``True``, do the same thing as when
called without arguments, and return ``True``.
:ref:`Using RLock as a context manager <with-locks>`
Recommended over manual :meth:`!acquire` and :meth:`release` calls
whenever practical.

When invoked with the *blocking* argument set to ``False``, do not block. If a call
without an argument would block, return ``False`` immediately; otherwise, do the
same thing as when called without arguments, and return ``True``.

When invoked with the floating-point *timeout* argument set to a positive
value, block for at most the number of seconds specified by *timeout*
and as long as the lock cannot be acquired. Return ``True`` if the lock has
been acquired, ``False`` if the timeout has elapsed.
When invoked with the *blocking* argument set to ``True`` (the default):

* If no thread owns the lock, acquire the lock and return immediately.

* If another thread owns the lock, block until we are able to acquire
lock, or *timeout*, if set to a positive float value.

* If the same thread owns the lock, acquire the lock again, and
return immediately. This is the difference between :class:`Lock` and
:class:`!RLock`; :class:`Lock` handles this case the same as the previous,
blocking until the lock can be acquired.

When invoked with the *blocking* argument set to ``False``:

* If no thread owns the lock, acquire the lock and return immediately.

* If another thread owns the lock, return immediately.

* If the same thread owns the lock, acquire the lock again and return
immediately.

In all cases, if the thread was able to acquire the lock, return ``True``.
If the thread was unable to acquire the lock (i.e. if not blocking or
the timeout was reached) return ``False``.

If called multiple times, failing to call :meth:`~RLock.release` as many times
may lead to deadlock. Consider using :class:`!RLock` as a context manager rather than
calling acquire/release directly.

.. versionchanged:: 3.2
The *timeout* parameter is new.
Expand All @@ -658,7 +685,7 @@ Reentrant locks also support the :ref:`context management protocol <with-locks>`

Only call this method when the calling thread owns the lock. A
:exc:`RuntimeError` is raised if this method is called when the lock is
unlocked.
not acquired.

There is no return value.

Expand Down

0 comments on commit 1fca3b4

Please sign in to comment.