You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: guide/src/faq.md
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,18 +4,18 @@ Sorry that you're having trouble using PyO3. If you can't find the answer to you
4
4
5
5
## I'm experiencing deadlocks using PyO3 with `std::sync::OnceLock`, `std::sync::LazyLock`, `lazy_static`, and `once_cell`!
6
6
7
-
`OnceLock`, `LazyLock`, and their thirdparty predecessors use blocking to ensure only one thread ever initializes them. Because the Python GIL is an additional lock this can lead to deadlocks in the following way:
7
+
`OnceLock`, `LazyLock`, and their thirdparty predecessors use blocking to ensure only one thread ever initializes them. Because the Python interpreter can introduce additional locks (the Python GIL and GC can both require all other threads to pause) this can lead to deadlocks in the following way:
8
8
9
-
1. A thread (thread A) which has acquired the Python GIL starts initialization of a `OnceLock` value.
10
-
2. The initialization code calls some Python API which temporarily releases the GIL e.g. `Python::import`.
11
-
3. Another thread (thread B) acquires the Python GIL and attempts to access the same `OnceLock` value.
9
+
1. A thread (thread A) which is attached to the Python interpreter starts initialization of a `OnceLock` value.
10
+
2. The initialization code calls some Python API which temporarily detaches from the interpreter e.g. `Python::import`.
11
+
3. Another thread (thread B) attaches to the Python interpreter and attempts to access the same `OnceLock` value.
12
12
4. Thread B is blocked, because it waits for `OnceLock`'s initialization to lock to release.
13
-
5.Thread A is blocked, because it waits to re-acquire the GIL which thread B still holds.
13
+
5.On non-free-threaded Python, thread A is now also blocked, because it waits to re-attach to the interpreter (by taking the GIL which thread B still holds).
14
14
6. Deadlock.
15
15
16
-
PyO3 provides a struct [`GILOnceCell`] which implements a single-initialization API based on these types that relies on the GIL for locking. If the GIL is released or there is no GIL, then this type allows the initialization function to race but ensures that the data is only ever initialized once. If you need to ensure that the initialization function is called once and only once, you can make use of the [`OnceExt`] and [`OnceLockExt`] extension traits that enable using the standard library types for this purpose but provide new methods for these types that avoid the risk of deadlocking with the Python GIL. This means they can be used in place of other choices when you are experiencing the deadlock described above. See the documentation for [`GILOnceCell`] and [`OnceExt`] for further details and an example how to use them.
16
+
PyO3 provides a struct [`PyOnceLock`] which implements a single-initialization API based on these types that avoids deadlocks. You can also make use of the [`OnceExt`] and [`OnceLockExt`] extension traits that enable using the standard library types for this purpose by providing new methods for these types that avoid the risk of deadlocking with the Python interpreter. This means they can be used in place of other choices when you are experiencing the deadlock described above. See the documentation for [`PyOnceLock`] and [`OnceExt`] for further details and an example how to use them.
Copy file name to clipboardExpand all lines: guide/src/migration.md
+40-1Lines changed: 40 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,7 +21,46 @@ For this reason we chose to rename these to more modern terminology introduced i
21
21
<summary><small>Click to expand</small></summary>
22
22
23
23
The type alias `PyObject` (aka `Py<PyAny>`) is often confused with the identically named FFI definition `pyo3::ffi::PyObject`. For this reason we are deprecating its usage. To migrate simply replace its usage by the target type `Py<PyAny>`.
24
+
</details>
25
+
26
+
### Replacement of `GILOnceCell` with `PyOnceLock`
27
+
<detailsopen>
28
+
<summary><small>Click to expand</small></summary>
29
+
30
+
Similar to the above renaming of `Python::with_gil` and related APIs, the `GILOnceCell` type was designed for a Python interpreter which was limited by the GIL. Aside from its name, it allowed for the "once" initialization to race because the racing was mediated by the GIL and was extremely unlikely to manifest in practice.
31
+
32
+
With the introduction of free-threaded Python the racy initialization behavior is more likely to be problematic and so a new type `PyOnceLock` has been introduced which performs true single-initialization correctly while attached to the Python interpreter. It exposes the same API as `GILOnceCell`, so should be a drop-in replacement with the notable exception that if the racy initialization of `GILOnceCell` was inadvertently relied on (e.g. due to circular references) then the stronger once-ever guarantee of `PyOnceLock` may lead to deadlocking which requires refactoring.
0 commit comments