docs: more warnings about locking and the GIL (#5689)

* docs: more warnings about locking and the GIL

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix require → reacquire typo

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rwgkio@gmail.com>
This commit is contained in:
Henry Schreiner
2025-05-24 19:12:49 -04:00
committed by GitHub
parent d7769de533
commit 9d06626521
2 changed files with 16 additions and 3 deletions

View File

@@ -299,9 +299,17 @@ However, the module is no longer free-threading safe, for the same reason as
before, because the calculation is not synchronized. We can synchronize it
using a Python critical section. This will do nothing if not in free-threaded
Python. You can have it lock one or two Python objects. You cannot nest it.
(Note: In Python 3.13t, Python re-locks if you enter a critical section again,
which happens in various places. This was optimized away in 3.14+. Use a
``std::mutex`` instead if this is a problem).
.. warning::
When using a ``py::scoped_critical_section``, make sure it is not nested and
that no other synchronization primitives (such as a ``std::mutex``) are
held, which could lead to deadlocks. In 3.13, taking the same lock causes it
to release then reacquire, which means you can't use it to, for example, read
and write to a dictionary, because the dictionary uses a critical section
internally in CPython. Use a ``std::mutex`` instead if you need this on
Python 3.13. In 3.14, taking a lock on a locked object no longer releases
and relocks as an optimization, which also fixes this case.
.. code-block:: cpp
:emphasize-lines: 1,4,8

View File

@@ -365,8 +365,13 @@ TEST_CASE("Threads") {
py::gil_scoped_acquire gil{};
#ifdef Py_GIL_DISABLED
# if PY_VERSION_HEX < 0x030E0000
// This will not run with the GIL, so it won't deadlock. That's
// because of how we run our tests. Be more careful of
// deadlocks if the "free-threaded" GIL could be enabled (at
// runtime).
std::lock_guard<std::mutex> lock(mutex);
# else
// CPython's thread-safe API in no-GIL mode.
py::scoped_critical_section lock(locals);
# endif
#endif