1//! Interaction with Python's global interpreter lock
2
3#[cfg(pyo3_disable_reference_pool)]
4use crate::impl_::panic::PanicTrap;
5use crate::{ffi, Python};
6#[cfg(not(pyo3_disable_reference_pool))]
7use once_cell::sync::Lazy;
8use std::cell::Cell;
9use std::{mem, ptr::NonNull, sync};
10
11static START: sync::Once = sync::Once::new();
12
13std::thread_local! {
14 /// This is an internal counter in pyo3 monitoring whether this thread has the GIL.
15 ///
16 /// It will be incremented whenever a GILGuard or GILPool is created, and decremented whenever
17 /// they are dropped.
18 ///
19 /// As a result, if this thread has the GIL, GIL_COUNT is greater than zero.
20 ///
21 /// Additionally, we sometimes need to prevent safe access to the GIL,
22 /// e.g. when implementing `__traverse__`, which is represented by a negative value.
23 static GIL_COUNT: Cell<isize> = const { Cell::new(0) };
24}
25
26const GIL_LOCKED_DURING_TRAVERSE: isize = -1;
27
28/// Checks whether the GIL is acquired.
29///
30/// Note: This uses pyo3's internal count rather than PyGILState_Check for two reasons:
31/// 1) for performance
32/// 2) PyGILState_Check always returns 1 if the sub-interpreter APIs have ever been called,
33/// which could lead to incorrect conclusions that the GIL is held.
34#[inline(always)]
35fn gil_is_acquired() -> bool {
36 GIL_COUNT.try_with(|c| c.get() > 0).unwrap_or(default:false)
37}
38
39/// Prepares the use of Python in a free-threaded context.
40///
41/// If the Python interpreter is not already initialized, this function will initialize it with
42/// signal handling disabled (Python will not raise the `KeyboardInterrupt` exception). Python
43/// signal handling depends on the notion of a 'main thread', which must be the thread that
44/// initializes the Python interpreter.
45///
46/// If the Python interpreter is already initialized, this function has no effect.
47///
48/// This function is unavailable under PyPy because PyPy cannot be embedded in Rust (or any other
49/// software). Support for this is tracked on the
50/// [PyPy issue tracker](https://github.com/pypy/pypy/issues/3836).
51///
52/// # Examples
53/// ```rust
54/// use pyo3::prelude::*;
55///
56/// # fn main() -> PyResult<()> {
57/// pyo3::prepare_freethreaded_python();
58/// Python::with_gil(|py| py.run(pyo3::ffi::c_str!("print('Hello World')"), None, None))
59/// # }
60/// ```
61#[cfg(not(any(PyPy, GraalPy)))]
62pub fn prepare_freethreaded_python() {
63 // Protect against race conditions when Python is not yet initialized and multiple threads
64 // concurrently call 'prepare_freethreaded_python()'. Note that we do not protect against
65 // concurrent initialization of the Python runtime by other users of the Python C API.
66 START.call_once_force(|_| unsafe {
67 // Use call_once_force because if initialization panics, it's okay to try again.
68 if ffi::Py_IsInitialized() == 0 {
69 ffi::Py_InitializeEx(arg1:0);
70
71 // Release the GIL.
72 ffi::PyEval_SaveThread();
73 }
74 });
75}
76
77/// Executes the provided closure with an embedded Python interpreter.
78///
79/// This function initializes the Python interpreter, executes the provided closure, and then
80/// finalizes the Python interpreter.
81///
82/// After execution all Python resources are cleaned up, and no further Python APIs can be called.
83/// Because many Python modules implemented in C do not support multiple Python interpreters in a
84/// single process, it is not safe to call this function more than once. (Many such modules will not
85/// initialize correctly on the second run.)
86///
87/// # Panics
88/// - If the Python interpreter is already initialized before calling this function.
89///
90/// # Safety
91/// - This function should only ever be called once per process (usually as part of the `main`
92/// function). It is also not thread-safe.
93/// - No Python APIs can be used after this function has finished executing.
94/// - The return value of the closure must not contain any Python value, _including_ `PyResult`.
95///
96/// # Examples
97///
98/// ```rust
99/// unsafe {
100/// pyo3::with_embedded_python_interpreter(|py| {
101/// if let Err(e) = py.run(pyo3::ffi::c_str!("print('Hello World')"), None, None) {
102/// // We must make sure to not return a `PyErr`!
103/// e.print(py);
104/// }
105/// });
106/// }
107/// ```
108#[cfg(not(any(PyPy, GraalPy)))]
109pub unsafe fn with_embedded_python_interpreter<F, R>(f: F) -> R
110where
111 F: for<'p> dynFnOnce(Python<'p>) -> R,
112{
113 assert_eq!(
114 unsafe { ffi::Py_IsInitialized() },
115 0,
116 "called `with_embedded_python_interpreter` but a Python interpreter is already running."
117 );
118
119 unsafe { ffi::Py_InitializeEx(0) };
120
121 let result = {
122 let guard = unsafe { GILGuard::assume() };
123 let py = guard.python();
124 // Import the threading module - this ensures that it will associate this thread as the "main"
125 // thread, which is important to avoid an `AssertionError` at finalization.
126 py.import("threading").unwrap();
127
128 // Execute the closure.
129 f(py)
130 };
131
132 // Finalize the Python interpreter.
133 unsafe { ffi::Py_Finalize() };
134
135 result
136}
137
138/// RAII type that represents the Global Interpreter Lock acquisition.
139pub(crate) enum GILGuard {
140 /// Indicates the GIL was already held with this GILGuard was acquired.
141 Assumed,
142 /// Indicates that we actually acquired the GIL when this GILGuard was acquired
143 Ensured { gstate: ffi::PyGILState_STATE },
144}
145
146impl GILGuard {
147 /// PyO3 internal API for acquiring the GIL. The public API is Python::with_gil.
148 ///
149 /// If the GIL was already acquired via PyO3, this returns
150 /// `GILGuard::Assumed`. Otherwise, the GIL will be acquired and
151 /// `GILGuard::Ensured` will be returned.
152 pub(crate) fn acquire() -> Self {
153 if gil_is_acquired() {
154 // SAFETY: We just checked that the GIL is already acquired.
155 return unsafe { Self::assume() };
156 }
157
158 // Maybe auto-initialize the GIL:
159 // - If auto-initialize feature set and supported, try to initialize the interpreter.
160 // - If the auto-initialize feature is set but unsupported, emit hard errors only when the
161 // extension-module feature is not activated - extension modules don't care about
162 // auto-initialize so this avoids breaking existing builds.
163 // - Otherwise, just check the GIL is initialized.
164 cfg_if::cfg_if! {
165 if #[cfg(all(feature = "auto-initialize", not(any(PyPy, GraalPy))))] {
166 prepare_freethreaded_python();
167 } else {
168 // This is a "hack" to make running `cargo test` for PyO3 convenient (i.e. no need
169 // to specify `--features auto-initialize` manually. Tests within the crate itself
170 // all depend on the auto-initialize feature for conciseness but Cargo does not
171 // provide a mechanism to specify required features for tests.
172 #[cfg(not(any(PyPy, GraalPy)))]
173 if option_env!("CARGO_PRIMARY_PACKAGE").is_some() {
174 prepare_freethreaded_python();
175 }
176
177 START.call_once_force(|_| unsafe {
178 // Use call_once_force because if there is a panic because the interpreter is
179 // not initialized, it's fine for the user to initialize the interpreter and
180 // retry.
181 assert_ne!(
182 ffi::Py_IsInitialized(),
183 0,
184 "The Python interpreter is not initialized and the `auto-initialize` \
185 feature is not enabled.\n\n\
186 Consider calling `pyo3::prepare_freethreaded_python()` before attempting \
187 to use Python APIs."
188 );
189 });
190 }
191 }
192
193 // SAFETY: We have ensured the Python interpreter is initialized.
194 unsafe { Self::acquire_unchecked() }
195 }
196
197 /// Acquires the `GILGuard` without performing any state checking.
198 ///
199 /// This can be called in "unsafe" contexts where the normal interpreter state
200 /// checking performed by `GILGuard::acquire` may fail. This includes calling
201 /// as part of multi-phase interpreter initialization.
202 pub(crate) unsafe fn acquire_unchecked() -> Self {
203 if gil_is_acquired() {
204 return unsafe { Self::assume() };
205 }
206
207 let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL
208 increment_gil_count();
209
210 #[cfg(not(pyo3_disable_reference_pool))]
211 if let Some(pool) = Lazy::get(&POOL) {
212 pool.update_counts(unsafe { Python::assume_gil_acquired() });
213 }
214 GILGuard::Ensured { gstate }
215 }
216
217 /// Acquires the `GILGuard` while assuming that the GIL is already held.
218 pub(crate) unsafe fn assume() -> Self {
219 increment_gil_count();
220 let guard = GILGuard::Assumed;
221 #[cfg(not(pyo3_disable_reference_pool))]
222 if let Some(pool) = Lazy::get(&POOL) {
223 pool.update_counts(guard.python());
224 }
225 guard
226 }
227
228 /// Gets the Python token associated with this [`GILGuard`].
229 #[inline]
230 pub fn python(&self) -> Python<'_> {
231 unsafe { Python::assume_gil_acquired() }
232 }
233}
234
235/// The Drop implementation for `GILGuard` will release the GIL.
236impl Drop for GILGuard {
237 fn drop(&mut self) {
238 match self {
239 GILGuard::Assumed => {}
240 GILGuard::Ensured { gstate: &mut PyGILState_STATE } => unsafe {
241 // Drop the objects in the pool before attempting to release the thread state
242 ffi::PyGILState_Release(*gstate);
243 },
244 }
245 decrement_gil_count();
246 }
247}
248
249// Vector of PyObject
250type PyObjVec = Vec<NonNull<ffi::PyObject>>;
251
252#[cfg(not(pyo3_disable_reference_pool))]
253/// Thread-safe storage for objects which were dec_ref while the GIL was not held.
254struct ReferencePool {
255 pending_decrefs: sync::Mutex<PyObjVec>,
256}
257
258#[cfg(not(pyo3_disable_reference_pool))]
259impl ReferencePool {
260 const fn new() -> Self {
261 Self {
262 pending_decrefs: sync::Mutex::new(Vec::new()),
263 }
264 }
265
266 fn register_decref(&self, obj: NonNull<ffi::PyObject>) {
267 self.pending_decrefs.lock().unwrap().push(obj);
268 }
269
270 fn update_counts(&self, _py: Python<'_>) {
271 let mut pending_decrefs = self.pending_decrefs.lock().unwrap();
272 if pending_decrefs.is_empty() {
273 return;
274 }
275
276 let decrefs = mem::take(&mut *pending_decrefs);
277 drop(pending_decrefs);
278
279 for ptr in decrefs {
280 unsafe { ffi::Py_DECREF(ptr.as_ptr()) };
281 }
282 }
283}
284
285#[cfg(not(pyo3_disable_reference_pool))]
286unsafe impl Send for ReferencePool {}
287
288#[cfg(not(pyo3_disable_reference_pool))]
289unsafe impl Sync for ReferencePool {}
290
291#[cfg(not(pyo3_disable_reference_pool))]
292static POOL: Lazy<ReferencePool> = Lazy::new(ReferencePool::new);
293
294/// A guard which can be used to temporarily release the GIL and restore on `Drop`.
295pub(crate) struct SuspendGIL {
296 count: isize,
297 tstate: *mut ffi::PyThreadState,
298}
299
300impl SuspendGIL {
301 pub(crate) unsafe fn new() -> Self {
302 let count: isize = GIL_COUNT.with(|c: &Cell| c.replace(val:0));
303 let tstate: *mut PyThreadState = unsafe { ffi::PyEval_SaveThread() };
304
305 Self { count, tstate }
306 }
307}
308
309impl Drop for SuspendGIL {
310 fn drop(&mut self) {
311 GIL_COUNT.with(|c: &Cell| c.set(self.count));
312 unsafe {
313 ffi::PyEval_RestoreThread(self.tstate);
314
315 // Update counts of PyObjects / Py that were cloned or dropped while the GIL was released.
316 #[cfg(not(pyo3_disable_reference_pool))]
317 if let Some(pool: &ReferencePool) = Lazy::get(&POOL) {
318 pool.update_counts(_py:Python::assume_gil_acquired());
319 }
320 }
321 }
322}
323
324/// Used to lock safe access to the GIL
325pub(crate) struct LockGIL {
326 count: isize,
327}
328
329impl LockGIL {
330 /// Lock access to the GIL while an implementation of `__traverse__` is running
331 pub fn during_traverse() -> Self {
332 Self::new(GIL_LOCKED_DURING_TRAVERSE)
333 }
334
335 fn new(reason: isize) -> Self {
336 let count: isize = GIL_COUNT.with(|c: &Cell| c.replace(val:reason));
337
338 Self { count }
339 }
340
341 #[cold]
342 fn bail(current: isize) {
343 match current {
344 GIL_LOCKED_DURING_TRAVERSE => panic!(
345 "Access to the GIL is prohibited while a __traverse__ implmentation is running."
346 ),
347 _ => panic!("Access to the GIL is currently prohibited."),
348 }
349 }
350}
351
352impl Drop for LockGIL {
353 fn drop(&mut self) {
354 GIL_COUNT.with(|c: &Cell| c.set(self.count));
355 }
356}
357
358/// Increments the reference count of a Python object if the GIL is held. If
359/// the GIL is not held, this function will panic.
360///
361/// # Safety
362/// The object must be an owned Python reference.
363#[cfg(feature = "py-clone")]
364#[track_caller]
365pub unsafe fn register_incref(obj: NonNull<ffi::PyObject>) {
366 if gil_is_acquired() {
367 unsafe { ffi::Py_INCREF(obj.as_ptr()) }
368 } else {
369 panic!("Cannot clone pointer into Python heap without the GIL being held.");
370 }
371}
372
373/// Registers a Python object pointer inside the release pool, to have its reference count decreased
374/// the next time the GIL is acquired in pyo3.
375///
376/// If the GIL is held, the reference count will be decreased immediately instead of being queued
377/// for later.
378///
379/// # Safety
380/// The object must be an owned Python reference.
381#[track_caller]
382pub unsafe fn register_decref(obj: NonNull<ffi::PyObject>) {
383 if gil_is_acquired() {
384 unsafe { ffi::Py_DECREF(op:obj.as_ptr()) }
385 } else {
386 #[cfg(not(pyo3_disable_reference_pool))]
387 POOL.register_decref(obj);
388 #[cfg(all(
389 pyo3_disable_reference_pool,
390 not(pyo3_leak_on_drop_without_reference_pool)
391 ))]
392 {
393 let _trap = PanicTrap::new("Aborting the process to avoid panic-from-drop.");
394 panic!("Cannot drop pointer into Python heap without the GIL being held.");
395 }
396 }
397}
398
399/// Increments pyo3's internal GIL count - to be called whenever GILPool or GILGuard is created.
400#[inline(always)]
401fn increment_gil_count() {
402 // Ignores the error in case this function called from `atexit`.
403 let _ = GIL_COUNT.try_with(|c: &Cell| {
404 let current: isize = c.get();
405 if current < 0 {
406 LockGIL::bail(current);
407 }
408 c.set(val:current + 1);
409 });
410}
411
412/// Decrements pyo3's internal GIL count - to be called whenever GILPool or GILGuard is dropped.
413#[inline(always)]
414fn decrement_gil_count() {
415 // Ignores the error in case this function called from `atexit`.
416 let _ = GIL_COUNT.try_with(|c: &Cell| {
417 let current: isize = c.get();
418 debug_assert!(
419 current > 0,
420 "Negative GIL count detected. Please report this error to the PyO3 repo as a bug."
421 );
422 c.set(val:current - 1);
423 });
424}
425
426#[cfg(test)]
427mod tests {
428 use super::GIL_COUNT;
429 #[cfg(not(pyo3_disable_reference_pool))]
430 use super::{gil_is_acquired, POOL};
431 use crate::{ffi, PyObject, Python};
432 use crate::{gil::GILGuard, types::any::PyAnyMethods};
433 use std::ptr::NonNull;
434
435 fn get_object(py: Python<'_>) -> PyObject {
436 py.eval(ffi::c_str!("object()"), None, None)
437 .unwrap()
438 .unbind()
439 }
440
441 #[cfg(not(pyo3_disable_reference_pool))]
442 fn pool_dec_refs_does_not_contain(obj: &PyObject) -> bool {
443 !POOL
444 .pending_decrefs
445 .lock()
446 .unwrap()
447 .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
448 }
449
450 // with no GIL, threads can empty the POOL at any time, so this
451 // function does not test anything meaningful
452 #[cfg(not(any(pyo3_disable_reference_pool, Py_GIL_DISABLED)))]
453 fn pool_dec_refs_contains(obj: &PyObject) -> bool {
454 POOL.pending_decrefs
455 .lock()
456 .unwrap()
457 .contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
458 }
459
460 #[test]
461 fn test_pyobject_drop_with_gil_decreases_refcnt() {
462 Python::with_gil(|py| {
463 let obj = get_object(py);
464
465 // Create a reference to drop with the GIL.
466 let reference = obj.clone_ref(py);
467
468 assert_eq!(obj.get_refcnt(py), 2);
469 #[cfg(not(pyo3_disable_reference_pool))]
470 assert!(pool_dec_refs_does_not_contain(&obj));
471
472 // With the GIL held, reference count will be decreased immediately.
473 drop(reference);
474
475 assert_eq!(obj.get_refcnt(py), 1);
476 #[cfg(not(any(pyo3_disable_reference_pool)))]
477 assert!(pool_dec_refs_does_not_contain(&obj));
478 });
479 }
480
481 #[test]
482 #[cfg(all(not(pyo3_disable_reference_pool), not(target_arch = "wasm32")))] // We are building wasm Python with pthreads disabled
483 fn test_pyobject_drop_without_gil_doesnt_decrease_refcnt() {
484 let obj = Python::with_gil(|py| {
485 let obj = get_object(py);
486 // Create a reference to drop without the GIL.
487 let reference = obj.clone_ref(py);
488
489 assert_eq!(obj.get_refcnt(py), 2);
490 assert!(pool_dec_refs_does_not_contain(&obj));
491
492 // Drop reference in a separate thread which doesn't have the GIL.
493 std::thread::spawn(move || drop(reference)).join().unwrap();
494
495 // The reference count should not have changed (the GIL has always
496 // been held by this thread), it is remembered to release later.
497 assert_eq!(obj.get_refcnt(py), 2);
498 #[cfg(not(Py_GIL_DISABLED))]
499 assert!(pool_dec_refs_contains(&obj));
500 obj
501 });
502
503 // Next time the GIL is acquired, the reference is released
504 #[allow(unused)]
505 Python::with_gil(|py| {
506 // with no GIL, another thread could still be processing
507 // DECREFs after releasing the lock on the POOL, so the
508 // refcnt could still be 2 when this assert happens
509 #[cfg(not(Py_GIL_DISABLED))]
510 assert_eq!(obj.get_refcnt(py), 1);
511 assert!(pool_dec_refs_does_not_contain(&obj));
512 });
513 }
514
515 #[test]
516 #[allow(deprecated)]
517 fn test_gil_counts() {
518 // Check with_gil and GILGuard both increase counts correctly
519 let get_gil_count = || GIL_COUNT.with(|c| c.get());
520
521 assert_eq!(get_gil_count(), 0);
522 Python::with_gil(|_| {
523 assert_eq!(get_gil_count(), 1);
524
525 let pool = unsafe { GILGuard::assume() };
526 assert_eq!(get_gil_count(), 2);
527
528 let pool2 = unsafe { GILGuard::assume() };
529 assert_eq!(get_gil_count(), 3);
530
531 drop(pool);
532 assert_eq!(get_gil_count(), 2);
533
534 Python::with_gil(|_| {
535 // nested with_gil updates gil count
536 assert_eq!(get_gil_count(), 3);
537 });
538 assert_eq!(get_gil_count(), 2);
539
540 drop(pool2);
541 assert_eq!(get_gil_count(), 1);
542 });
543 assert_eq!(get_gil_count(), 0);
544 }
545
546 #[test]
547 fn test_allow_threads() {
548 assert!(!gil_is_acquired());
549
550 Python::with_gil(|py| {
551 assert!(gil_is_acquired());
552
553 py.allow_threads(move || {
554 assert!(!gil_is_acquired());
555
556 Python::with_gil(|_| assert!(gil_is_acquired()));
557
558 assert!(!gil_is_acquired());
559 });
560
561 assert!(gil_is_acquired());
562 });
563
564 assert!(!gil_is_acquired());
565 }
566
567 #[cfg(feature = "py-clone")]
568 #[test]
569 #[should_panic]
570 fn test_allow_threads_updates_refcounts() {
571 Python::with_gil(|py| {
572 // Make a simple object with 1 reference
573 let obj = get_object(py);
574 assert!(obj.get_refcnt(py) == 1);
575 // Clone the object without the GIL which should panic
576 py.allow_threads(|| obj.clone());
577 });
578 }
579
580 #[test]
581 fn dropping_gil_does_not_invalidate_references() {
582 // Acquiring GIL for the second time should be safe - see #864
583 Python::with_gil(|py| {
584 let obj = Python::with_gil(|_| py.eval(ffi::c_str!("object()"), None, None).unwrap());
585
586 // After gil2 drops, obj should still have a reference count of one
587 assert_eq!(obj.get_refcnt(), 1);
588 })
589 }
590
591 #[cfg(feature = "py-clone")]
592 #[test]
593 fn test_clone_with_gil() {
594 Python::with_gil(|py| {
595 let obj = get_object(py);
596 let count = obj.get_refcnt(py);
597
598 // Cloning with the GIL should increase reference count immediately
599 #[allow(clippy::redundant_clone)]
600 let c = obj.clone();
601 assert_eq!(count + 1, c.get_refcnt(py));
602 })
603 }
604
605 #[test]
606 #[cfg(not(pyo3_disable_reference_pool))]
607 fn test_update_counts_does_not_deadlock() {
608 // update_counts can run arbitrary Python code during Py_DECREF.
609 // if the locking is implemented incorrectly, it will deadlock.
610
611 use crate::ffi;
612 use crate::gil::GILGuard;
613
614 Python::with_gil(|py| {
615 let obj = get_object(py);
616
617 unsafe extern "C" fn capsule_drop(capsule: *mut ffi::PyObject) {
618 // This line will implicitly call update_counts
619 // -> and so cause deadlock if update_counts is not handling recursion correctly.
620 let pool = unsafe { GILGuard::assume() };
621
622 // Rebuild obj so that it can be dropped
623 unsafe {
624 PyObject::from_owned_ptr(
625 pool.python(),
626 ffi::PyCapsule_GetPointer(capsule, std::ptr::null()) as _,
627 )
628 };
629 }
630
631 let ptr = obj.into_ptr();
632
633 let capsule =
634 unsafe { ffi::PyCapsule_New(ptr as _, std::ptr::null(), Some(capsule_drop)) };
635
636 POOL.register_decref(NonNull::new(capsule).unwrap());
637
638 // Updating the counts will call decref on the capsule, which calls capsule_drop
639 POOL.update_counts(py);
640 })
641 }
642
643 #[test]
644 #[cfg(not(pyo3_disable_reference_pool))]
645 fn test_gil_guard_update_counts() {
646 use crate::gil::GILGuard;
647
648 Python::with_gil(|py| {
649 let obj = get_object(py);
650
651 // For GILGuard::acquire
652
653 POOL.register_decref(NonNull::new(obj.clone_ref(py).into_ptr()).unwrap());
654 #[cfg(not(Py_GIL_DISABLED))]
655 assert!(pool_dec_refs_contains(&obj));
656 let _guard = GILGuard::acquire();
657 assert!(pool_dec_refs_does_not_contain(&obj));
658
659 // For GILGuard::assume
660
661 POOL.register_decref(NonNull::new(obj.clone_ref(py).into_ptr()).unwrap());
662 #[cfg(not(Py_GIL_DISABLED))]
663 assert!(pool_dec_refs_contains(&obj));
664 let _guard2 = unsafe { GILGuard::assume() };
665 assert!(pool_dec_refs_does_not_contain(&obj));
666 })
667 }
668}
669