⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here

pyo3/
gil.rs

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(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(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> FnOnce(Python<'p>) -> R,
112{
113    assert_eq!(
114        ffi::Py_IsInitialized(),
115        0,
116        "called `with_embedded_python_interpreter` but a Python interpreter is already running."
117    );
118
119    ffi::Py_InitializeEx(0);
120
121    let result = {
122        let guard = 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    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 Self::assume();
205        }
206
207        let gstate = 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(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 } => 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 = GIL_COUNT.with(|c| c.replace(0));
303        let tstate = 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| 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) = Lazy::get(&POOL) {
318                pool.update_counts(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 = GIL_COUNT.with(|c| c.replace(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| 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        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        ffi::Py_DECREF(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| {
404        let current = c.get();
405        if current < 0 {
406            LockGIL::bail(current);
407        }
408        c.set(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| {
417        let current = 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(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 = GILGuard::assume();
621
622                // Rebuild obj so that it can be dropped
623                PyObject::from_owned_ptr(
624                    pool.python(),
625                    ffi::PyCapsule_GetPointer(capsule, std::ptr::null()) as _,
626                );
627            }
628
629            let ptr = obj.into_ptr();
630
631            let capsule =
632                unsafe { ffi::PyCapsule_New(ptr as _, std::ptr::null(), Some(capsule_drop)) };
633
634            POOL.register_decref(NonNull::new(capsule).unwrap());
635
636            // Updating the counts will call decref on the capsule, which calls capsule_drop
637            POOL.update_counts(py);
638        })
639    }
640
641    #[test]
642    #[cfg(not(pyo3_disable_reference_pool))]
643    fn test_gil_guard_update_counts() {
644        use crate::gil::GILGuard;
645
646        Python::with_gil(|py| {
647            let obj = get_object(py);
648
649            // For GILGuard::acquire
650
651            POOL.register_decref(NonNull::new(obj.clone_ref(py).into_ptr()).unwrap());
652            #[cfg(not(Py_GIL_DISABLED))]
653            assert!(pool_dec_refs_contains(&obj));
654            let _guard = GILGuard::acquire();
655            assert!(pool_dec_refs_does_not_contain(&obj));
656
657            // For GILGuard::assume
658
659            POOL.register_decref(NonNull::new(obj.clone_ref(py).into_ptr()).unwrap());
660            #[cfg(not(Py_GIL_DISABLED))]
661            assert!(pool_dec_refs_contains(&obj));
662            let _guard2 = unsafe { GILGuard::assume() };
663            assert!(pool_dec_refs_does_not_contain(&obj));
664        })
665    }
666}