pyo3/tests/test_gc.rs

278 lines
6.8 KiB
Rust
Raw Normal View History

2018-09-21 21:32:48 +00:00
use pyo3::class::PyGCProtocol;
use pyo3::class::PyTraverseError;
use pyo3::class::PyVisit;
use pyo3::prelude::*;
2019-12-14 14:16:39 +00:00
use pyo3::types::{PyAny, PyTuple};
2020-02-03 13:25:16 +00:00
use pyo3::{ffi, py_run, AsPyPointer, PyCell};
2019-02-01 13:01:18 +00:00
use std::cell::RefCell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
mod common;
#[pyclass(freelist = 2)]
2018-11-12 12:59:19 +00:00
struct ClassWithFreelist {}
#[test]
fn class_with_freelist() {
let ptr;
{
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new(py, ClassWithFreelist {}).unwrap();
let _inst2 = Py::new(py, ClassWithFreelist {}).unwrap();
ptr = inst.as_ptr();
drop(inst);
}
{
let gil = Python::acquire_gil();
let py = gil.python();
let inst3 = Py::new(py, ClassWithFreelist {}).unwrap();
assert_eq!(ptr, inst3.as_ptr());
let inst4 = Py::new(py, ClassWithFreelist {}).unwrap();
assert_ne!(ptr, inst4.as_ptr())
}
}
struct TestDropCall {
drop_called: Arc<AtomicBool>,
}
2018-10-03 19:04:24 +00:00
impl Drop for TestDropCall {
fn drop(&mut self) {
self.drop_called.store(true, Ordering::Relaxed);
}
}
#[allow(dead_code)]
#[pyclass]
struct DataIsDropped {
member1: TestDropCall,
member2: TestDropCall,
}
#[test]
fn data_is_dropped() {
let drop_called1 = Arc::new(AtomicBool::new(false));
let drop_called2 = Arc::new(AtomicBool::new(false));
{
let gil = Python::acquire_gil();
let py = gil.python();
let data_is_dropped = DataIsDropped {
member1: TestDropCall {
drop_called: Arc::clone(&drop_called1),
},
member2: TestDropCall {
drop_called: Arc::clone(&drop_called2),
},
};
let inst = Py::new(py, data_is_dropped).unwrap();
assert!(!drop_called1.load(Ordering::Relaxed));
assert!(!drop_called2.load(Ordering::Relaxed));
drop(inst);
}
assert!(drop_called1.load(Ordering::Relaxed));
assert!(drop_called2.load(Ordering::Relaxed));
}
#[pyclass]
2018-11-12 12:59:19 +00:00
struct ClassWithDrop {}
2018-10-03 19:04:24 +00:00
impl Drop for ClassWithDrop {
fn drop(&mut self) {
unsafe {
let py = Python::assume_gil_acquired();
let _empty1: Py<PyTuple> = FromPy::from_py(PyTuple::empty(py), py);
let _empty2: PyObject = PyTuple::empty(py).into_py(py);
2019-03-04 04:50:43 +00:00
let _empty3: &PyAny = py.from_owned_ptr(ffi::PyTuple_New(0));
}
}
}
2019-02-23 17:01:22 +00:00
// Test behavior of pythonrun::register_pointers + type_object::dealloc
#[test]
fn create_pointers_in_drop() {
let _gil = Python::acquire_gil();
let ptr;
let cnt;
{
let gil = Python::acquire_gil();
let py = gil.python();
let empty: PyObject = PyTuple::empty(py).into_py(py);
ptr = empty.as_ptr();
// substract 2, because `PyTuple::empty(py).into_py(py)` increases the refcnt by 2
2019-05-25 13:12:33 +00:00
cnt = empty.get_refcnt() - 2;
let inst = Py::new(py, ClassWithDrop {}).unwrap();
drop(inst);
}
// empty1 and empty2 are still alive (stored in pointers list)
{
let _gil = Python::acquire_gil();
assert_eq!(cnt + 2, unsafe { ffi::Py_REFCNT(ptr) });
}
// empty1 and empty2 should be released
{
let _gil = Python::acquire_gil();
assert_eq!(cnt, unsafe { ffi::Py_REFCNT(ptr) });
}
}
#[allow(dead_code)]
#[pyclass]
struct GCIntegration {
self_ref: RefCell<PyObject>,
dropped: TestDropCall,
}
#[pyproto]
impl PyGCProtocol for GCIntegration {
fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> {
visit.call(&*self.self_ref.borrow())
}
fn __clear__(&mut self) {
2018-11-12 12:59:19 +00:00
let gil = GILGuard::acquire();
*self.self_ref.borrow_mut() = gil.python().None();
}
}
#[test]
fn gc_integration() {
let drop_called = Arc::new(AtomicBool::new(false));
{
let gil = Python::acquire_gil();
let py = gil.python();
2020-02-03 13:25:16 +00:00
let inst = PyCell::new_mut(
py,
GCIntegration {
self_ref: RefCell::new(py.None()),
dropped: TestDropCall {
drop_called: Arc::clone(&drop_called),
},
},
)
.unwrap();
*inst.self_ref.borrow_mut() = inst.to_object(py);
}
let gil = Python::acquire_gil();
let py = gil.python();
py.run("import gc; gc.collect()", None, None).unwrap();
assert!(drop_called.load(Ordering::Relaxed));
}
#[pyclass(gc)]
2018-11-12 12:59:19 +00:00
struct GCIntegration2 {}
2018-10-03 19:04:24 +00:00
#[pyproto]
impl PyGCProtocol for GCIntegration2 {
fn __traverse__(&self, _visit: PyVisit) -> Result<(), PyTraverseError> {
Ok(())
}
2019-07-14 18:45:05 +00:00
fn __clear__(&mut self) {}
}
#[test]
fn gc_integration2() {
let gil = Python::acquire_gil();
let py = gil.python();
2020-02-03 13:25:16 +00:00
let inst = PyCell::new_ref(py, GCIntegration2 {}).unwrap();
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
}
#[pyclass(weakref)]
2018-11-12 12:59:19 +00:00
struct WeakRefSupport {}
2018-10-03 19:04:24 +00:00
#[test]
fn weakref_support() {
let gil = Python::acquire_gil();
let py = gil.python();
2020-02-03 13:25:16 +00:00
let inst = PyCell::new_ref(py, WeakRefSupport {}).unwrap();
py_run!(
py,
inst,
"import weakref; assert weakref.ref(inst)() is inst"
);
}
#[pyclass]
struct BaseClassWithDrop {
data: Option<Arc<AtomicBool>>,
}
#[pymethods]
impl BaseClassWithDrop {
#[new]
2019-12-14 14:16:39 +00:00
fn new() -> BaseClassWithDrop {
BaseClassWithDrop { data: None }
}
}
impl Drop for BaseClassWithDrop {
fn drop(&mut self) {
if let Some(ref mut data) = self.data {
data.store(true, Ordering::Relaxed);
}
}
}
2018-10-03 19:04:24 +00:00
#[pyclass(extends = BaseClassWithDrop)]
struct SubClassWithDrop {
data: Option<Arc<AtomicBool>>,
}
#[pymethods]
impl SubClassWithDrop {
#[new]
2020-01-05 07:01:05 +00:00
fn new() -> (Self, BaseClassWithDrop) {
(
SubClassWithDrop { data: None },
BaseClassWithDrop { data: None },
)
}
}
impl Drop for SubClassWithDrop {
fn drop(&mut self) {
if let Some(ref mut data) = self.data {
data.store(true, Ordering::Relaxed);
}
}
}
#[test]
fn inheritance_with_new_methods_with_drop() {
let drop_called1 = Arc::new(AtomicBool::new(false));
let drop_called2 = Arc::new(AtomicBool::new(false));
{
let gil = Python::acquire_gil();
let py = gil.python();
let _typebase = py.get_type::<BaseClassWithDrop>();
let typeobj = py.get_type::<SubClassWithDrop>();
2019-02-23 17:01:22 +00:00
let inst = typeobj.call((), None).unwrap();
let obj = SubClassWithDrop::try_from_mut(inst).unwrap();
obj.data = Some(Arc::clone(&drop_called1));
2018-11-12 12:59:19 +00:00
let base: &mut <SubClassWithDrop as pyo3::PyTypeInfo>::BaseType =
unsafe { py.mut_from_borrowed_ptr(inst.as_ptr()) };
base.data = Some(Arc::clone(&drop_called2));
}
assert!(drop_called1.load(Ordering::Relaxed));
assert!(drop_called2.load(Ordering::Relaxed));
}