pyo3/src/gil.rs

484 lines
15 KiB
Rust
Raw Normal View History

2017-06-20 21:10:12 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
2019-02-23 17:01:22 +00:00
//! Interaction with python's global interpreter lock
2020-03-18 04:31:22 +00:00
use crate::{ffi, internal_tricks::Unsendable, PyAny, Python};
2018-11-12 20:36:08 +00:00
use std::ptr::NonNull;
use std::{any, sync};
2015-01-05 16:05:53 +00:00
static START: sync::Once = sync::Once::new();
static START_PYO3: sync::Once = sync::Once::new();
2015-01-05 16:05:53 +00:00
2015-06-27 20:45:35 +00:00
/// Prepares the use of Python in a free-threaded context.
2015-04-18 20:20:19 +00:00
///
2015-06-27 20:45:35 +00:00
/// If the Python interpreter is not already initialized, this function
2015-04-18 20:20:19 +00:00
/// will initialize it with disabled signal handling
2015-06-27 20:45:35 +00:00
/// (Python will not raise the `KeyboardInterrupt` exception).
2015-04-18 20:20:19 +00:00
/// Python signal handling depends on the notion of a 'main thread', which must be
2015-06-27 20:45:35 +00:00
/// the thread that initializes the Python interpreter.
///
/// If both the Python interpreter and Python threading are already initialized,
/// this function has no effect.
///
/// # Panic
/// If the Python interpreter is initialized but Python threading is not,
/// a panic occurs.
/// It is not possible to safely access the Python runtime unless the main
/// thread (the thread which originally initialized Python) also initializes
/// threading.
///
2018-11-12 21:28:45 +00:00
/// When writing an extension module, the `#[pymodule]` macro
2015-06-27 20:45:35 +00:00
/// will ensure that Python threading is initialized.
///
2015-01-04 04:50:28 +00:00
pub fn prepare_freethreaded_python() {
2015-06-27 20:45:35 +00:00
// Protect against race conditions when Python is not yet initialized
2015-01-04 19:11:18 +00:00
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
2015-06-27 20:45:35 +00:00
// Note that we do not protect against concurrent initialization of the Python runtime
// by other users of the Python C API.
2015-01-04 04:50:28 +00:00
START.call_once(|| unsafe {
2015-01-04 19:11:18 +00:00
if ffi::Py_IsInitialized() != 0 {
2015-06-27 20:45:35 +00:00
// If Python is already initialized, we expect Python threading to also be initialized,
// as we can't make the existing Python main thread acquire the GIL.
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
#[cfg(not(Py_3_7))]
2017-07-18 11:28:49 +00:00
assert_ne!(ffi::PyEval_ThreadsInitialized(), 0);
2015-01-04 19:11:18 +00:00
} else {
2018-02-21 18:06:48 +00:00
// If Python isn't initialized yet, we expect that Python threading
// isn't initialized either.
2018-02-21 18:29:14 +00:00
#[cfg(not(Py_3_7))]
2018-02-21 18:06:48 +00:00
assert_eq!(ffi::PyEval_ThreadsInitialized(), 0);
2015-06-27 20:45:35 +00:00
// Initialize Python.
2015-01-04 19:11:18 +00:00
// We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
// Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
2015-06-27 20:45:35 +00:00
// Note that the 'main thread' notion in Python isn't documented properly;
// and running Python without one is not officially supported.
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
// PyPy does not support the embedding API
#[cfg(not(PyPy))]
2015-01-04 19:11:18 +00:00
ffi::Py_InitializeEx(0);
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
// > Changed in version 3.7: This function is now called by Py_Initialize(), so you dont have
// > to call it yourself anymore.
#[cfg(not(Py_3_7))]
2015-01-04 19:11:18 +00:00
ffi::PyEval_InitThreads();
// PyEval_InitThreads() will acquire the GIL,
// but we don't want to hold it at this point
2015-01-04 04:50:28 +00:00
// (it's not acquired in the other code paths)
2015-01-04 19:11:18 +00:00
// So immediately release the GIL:
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
#[cfg(not(PyPy))]
2015-01-04 19:11:18 +00:00
let _thread_state = ffi::PyEval_SaveThread();
2015-06-27 20:45:35 +00:00
// Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
2015-01-04 19:11:18 +00:00
// and will be restored by PyGILState_Ensure.
2015-01-04 04:50:28 +00:00
}
2017-06-21 06:26:28 +00:00
init_once();
});
}
#[doc(hidden)]
pub fn init_once() {
START_PYO3.call_once(|| unsafe {
2017-06-21 06:26:28 +00:00
// initialize release pool
2017-07-18 18:12:35 +00:00
POOL = Box::into_raw(Box::new(ReleasePool::new()));
2015-01-04 04:50:28 +00:00
});
2015-01-05 16:05:53 +00:00
}
2016-03-05 16:41:04 +00:00
/// RAII type that represents the Global Interpreter Lock acquisition.
2015-06-27 20:45:35 +00:00
///
/// # Example
/// ```
2017-05-13 05:43:17 +00:00
/// use pyo3::Python;
2015-06-27 20:45:35 +00:00
///
/// {
/// let gil_guard = Python::acquire_gil();
/// let py = gil_guard.python();
/// } // GIL is released when gil_guard is dropped
/// ```
#[must_use]
pub struct GILGuard {
2017-06-23 20:42:46 +00:00
owned: usize,
borrowed: usize,
gstate: ffi::PyGILState_STATE,
// Stable solution for impl !Send
no_send: Unsendable,
}
2017-06-11 15:46:23 +00:00
/// The Drop implementation for `GILGuard` will release the GIL.
impl Drop for GILGuard {
fn drop(&mut self) {
2017-06-21 06:26:28 +00:00
unsafe {
2017-07-18 18:12:35 +00:00
let pool: &'static mut ReleasePool = &mut *POOL;
2020-03-26 05:50:00 +00:00
pool.drain(self.python(), self.owned, self.borrowed);
2017-06-21 06:26:28 +00:00
ffi::PyGILState_Release(self.gstate);
}
}
}
2017-07-18 18:12:35 +00:00
/// Release pool
struct ReleasePool {
owned: ArrayList<NonNull<ffi::PyObject>>,
borrowed: ArrayList<NonNull<ffi::PyObject>>,
2018-11-14 06:39:07 +00:00
pointers: *mut Vec<NonNull<ffi::PyObject>>,
obj: Vec<Box<dyn any::Any>>,
p: parking_lot::Mutex<*mut Vec<NonNull<ffi::PyObject>>>,
2017-07-09 06:08:57 +00:00
}
2017-07-18 18:12:35 +00:00
impl ReleasePool {
fn new() -> ReleasePool {
ReleasePool {
owned: ArrayList::new(),
borrowed: ArrayList::new(),
pointers: Box::into_raw(Box::new(Vec::with_capacity(256))),
2018-11-20 07:21:36 +00:00
obj: Vec::with_capacity(8),
p: parking_lot::Mutex::new(Box::into_raw(Box::new(Vec::with_capacity(256)))),
2017-07-09 06:08:57 +00:00
}
}
unsafe fn release_pointers(&mut self) {
let mut v = self.p.lock();
2018-11-18 14:14:00 +00:00
let vec = &mut **v;
if vec.is_empty() {
return;
}
// switch vectors
2018-11-18 14:14:00 +00:00
std::mem::swap(&mut self.pointers, &mut *v);
2017-07-09 06:08:57 +00:00
drop(v);
2018-11-18 14:14:00 +00:00
// release PyObjects
2018-11-20 07:21:36 +00:00
for ptr in vec.iter_mut() {
2018-11-14 06:39:07 +00:00
ffi::Py_DECREF(ptr.as_ptr());
2017-07-09 06:08:57 +00:00
}
vec.set_len(0);
}
2020-03-26 05:50:00 +00:00
pub unsafe fn drain(&mut self, _py: Python, owned: usize, borrowed: usize) {
// Release owned objects(call decref)
2018-11-18 03:13:20 +00:00
while owned < self.owned.len() {
let last = self.owned.pop_back().unwrap();
ffi::Py_DECREF(last.as_ptr());
2017-07-09 06:08:57 +00:00
}
// Release borrowed objects(don't call decref)
self.borrowed.truncate(borrowed);
2020-03-26 05:50:00 +00:00
self.release_pointers();
self.obj.clear();
2017-07-09 06:08:57 +00:00
}
}
2017-07-18 18:12:35 +00:00
static mut POOL: *mut ReleasePool = ::std::ptr::null_mut();
2017-06-21 06:26:28 +00:00
2017-07-20 21:21:57 +00:00
#[doc(hidden)]
pub struct GILPool<'p> {
py: Python<'p>,
2017-06-23 20:42:46 +00:00
owned: usize,
borrowed: usize,
no_send: Unsendable,
}
impl<'p> GILPool<'p> {
#[inline]
pub fn new(py: Python) -> GILPool {
2017-08-03 18:37:24 +00:00
let p: &'static mut ReleasePool = unsafe { &mut *POOL };
GILPool {
py,
owned: p.owned.len(),
borrowed: p.borrowed.len(),
no_send: Unsendable::default(),
}
}
}
impl<'p> Drop for GILPool<'p> {
fn drop(&mut self) {
unsafe {
2017-07-18 18:12:35 +00:00
let pool: &'static mut ReleasePool = &mut *POOL;
2020-03-26 05:50:00 +00:00
pool.drain(self.py, self.owned, self.borrowed);
}
}
}
pub unsafe fn register_any<'p, T: 'static>(obj: T) -> &'p T {
let pool: &'static mut ReleasePool = &mut *POOL;
pool.obj.push(Box::new(obj));
pool.obj
.last()
.unwrap()
.as_ref()
.downcast_ref::<T>()
.unwrap()
}
2018-11-12 20:36:08 +00:00
pub unsafe fn register_pointer(obj: NonNull<ffi::PyObject>) {
2018-11-18 14:14:00 +00:00
let pool = &mut *POOL;
(**pool.p.lock()).push(obj);
}
2019-03-04 04:50:43 +00:00
pub unsafe fn register_owned(_py: Python, obj: NonNull<ffi::PyObject>) -> &PyAny {
2018-11-18 14:14:00 +00:00
let pool = &mut *POOL;
2019-03-04 04:50:43 +00:00
&*(pool.owned.push_back(obj) as *const _ as *const PyAny)
2017-06-23 20:42:46 +00:00
}
2019-03-04 04:50:43 +00:00
pub unsafe fn register_borrowed(_py: Python, obj: NonNull<ffi::PyObject>) -> &PyAny {
2018-11-18 14:14:00 +00:00
let pool = &mut *POOL;
2019-03-04 04:50:43 +00:00
&*(pool.borrowed.push_back(obj) as *const _ as *const PyAny)
}
impl GILGuard {
/// Acquires the global interpreter lock, which allows access to the Python runtime.
2015-06-27 20:45:35 +00:00
///
/// If the Python runtime is not already initialized, this function will initialize it.
/// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
pub fn acquire() -> GILGuard {
2017-06-23 20:42:46 +00:00
prepare_freethreaded_python();
2017-06-21 06:26:28 +00:00
unsafe {
let gstate = ffi::PyGILState_Ensure(); // acquire GIL
2017-07-18 18:12:35 +00:00
let pool: &'static mut ReleasePool = &mut *POOL;
GILGuard {
owned: pool.owned.len(),
borrowed: pool.borrowed.len(),
gstate,
no_send: Unsendable::default(),
}
2017-06-21 06:26:28 +00:00
}
}
2015-04-18 20:20:19 +00:00
/// Retrieves the marker type that proves that the GIL was acquired.
2015-06-21 22:35:01 +00:00
#[inline]
2017-07-18 11:28:49 +00:00
pub fn python(&self) -> Python {
unsafe { Python::assume_gil_acquired() }
}
}
use self::array_list::ArrayList;
mod array_list {
use std::collections::LinkedList;
const BLOCK_SIZE: usize = 256;
/// A container type for Release Pool
/// See #271 for why this is crated
pub(super) struct ArrayList<T> {
2019-07-15 04:32:18 +00:00
inner: LinkedList<[Option<T>; BLOCK_SIZE]>,
length: usize,
}
2019-07-15 04:32:18 +00:00
impl<T: Copy> ArrayList<T> {
pub fn new() -> Self {
ArrayList {
inner: LinkedList::new(),
length: 0,
}
}
pub fn push_back(&mut self, item: T) -> &T {
2018-11-18 15:26:02 +00:00
let next_idx = self.next_idx();
if next_idx == 0 {
2019-07-15 04:32:18 +00:00
self.inner.push_back([None; BLOCK_SIZE]);
}
2019-07-15 04:32:18 +00:00
self.inner.back_mut().unwrap()[next_idx] = Some(item);
self.length += 1;
2019-07-15 04:32:18 +00:00
self.inner.back().unwrap()[next_idx].as_ref().unwrap()
}
2018-11-18 15:26:02 +00:00
pub fn pop_back(&mut self) -> Option<T> {
self.length -= 1;
2018-11-18 15:26:02 +00:00
let current_idx = self.next_idx();
2018-12-26 11:18:02 +00:00
if current_idx == 0 {
2018-11-18 15:26:02 +00:00
let last_list = self.inner.pop_back()?;
2019-07-15 04:32:18 +00:00
return last_list[0];
2018-11-18 15:26:02 +00:00
}
2019-07-15 04:32:18 +00:00
self.inner.back().and_then(|arr| arr[current_idx])
}
pub fn len(&self) -> usize {
self.length
}
pub fn truncate(&mut self, new_len: usize) {
if self.length <= new_len {
return;
}
2018-11-20 07:21:36 +00:00
while self.inner.len() > (new_len + BLOCK_SIZE - 1) / BLOCK_SIZE {
self.inner.pop_back();
}
self.length = new_len;
}
2018-11-18 15:26:02 +00:00
fn next_idx(&self) -> usize {
self.length % BLOCK_SIZE
}
}
}
#[cfg(test)]
mod test {
2018-11-15 20:58:22 +00:00
use super::{GILPool, NonNull, ReleasePool, POOL};
2018-10-31 10:43:21 +00:00
use crate::object::PyObject;
2019-02-24 07:17:44 +00:00
use crate::AsPyPointer;
2019-02-23 17:01:22 +00:00
use crate::Python;
use crate::ToPyObject;
use crate::{ffi, gil};
fn get_object() -> PyObject {
// Convenience function for getting a single unique object
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py.eval("object()", None, None).unwrap();
obj.to_object(py)
}
#[test]
fn test_owned() {
2019-02-23 17:01:22 +00:00
gil::init_once();
2019-03-09 23:28:25 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object();
let obj_ptr = obj.as_ptr();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
unsafe {
2017-07-18 18:12:35 +00:00
let p: &'static mut ReleasePool = &mut *POOL;
{
let gil = Python::acquire_gil();
let py = gil.python();
2019-03-09 23:28:25 +00:00
let _ = gil::register_owned(py, obj.into_nonnull());
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
assert_eq!(p.owned.len(), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.owned.len(), 0);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}
#[test]
fn test_owned_nested() {
2019-02-23 17:01:22 +00:00
gil::init_once();
2017-08-08 06:52:24 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
2019-03-09 23:28:25 +00:00
let obj = get_object();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
let obj_ptr = obj.as_ptr();
unsafe {
2017-07-18 18:12:35 +00:00
let p: &'static mut ReleasePool = &mut *POOL;
{
let _pool = GILPool::new(py);
assert_eq!(p.owned.len(), 0);
2019-03-09 23:28:25 +00:00
let _ = gil::register_owned(py, obj.into_nonnull());
assert_eq!(p.owned.len(), 1);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
{
let _pool = GILPool::new(py);
2019-03-09 23:28:25 +00:00
let obj = get_object();
let _ = gil::register_owned(py, obj.into_nonnull());
assert_eq!(p.owned.len(), 2);
}
assert_eq!(p.owned.len(), 1);
}
{
assert_eq!(p.owned.len(), 0);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}
#[test]
fn test_borrowed() {
2019-02-23 17:01:22 +00:00
gil::init_once();
unsafe {
2017-07-18 18:12:35 +00:00
let p: &'static mut ReleasePool = &mut *POOL;
let obj = get_object();
let obj_ptr = obj.as_ptr();
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.borrowed.len(), 0);
2019-02-23 17:01:22 +00:00
gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap());
assert_eq!(p.borrowed.len(), 1);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.borrowed.len(), 0);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}
#[test]
fn test_borrowed_nested() {
2019-02-23 17:01:22 +00:00
gil::init_once();
unsafe {
2017-07-18 18:12:35 +00:00
let p: &'static mut ReleasePool = &mut *POOL;
let obj = get_object();
let obj_ptr = obj.as_ptr();
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.borrowed.len(), 0);
2019-02-23 17:01:22 +00:00
gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap());
assert_eq!(p.borrowed.len(), 1);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
{
let _pool = GILPool::new(py);
assert_eq!(p.borrowed.len(), 1);
2019-02-23 17:01:22 +00:00
gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap());
assert_eq!(p.borrowed.len(), 2);
}
assert_eq!(p.borrowed.len(), 1);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.borrowed.len(), 0);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}
#[test]
fn test_pyobject_drop() {
2019-02-23 17:01:22 +00:00
gil::init_once();
2019-03-09 23:28:25 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
let obj = get_object();
// Ensure that obj does not get freed
let _ref = obj.clone_ref(py);
let obj_ptr = obj.as_ptr();
unsafe {
2017-07-18 18:12:35 +00:00
let p: &'static mut ReleasePool = &mut *POOL;
{
assert_eq!(p.owned.len(), 0);
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
}
drop(obj);
2019-04-24 20:41:59 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 2);
{
let _gil = Python::acquire_gil();
}
2019-03-09 23:28:25 +00:00
assert_eq!(ffi::Py_REFCNT(obj_ptr), 1);
}
}
}