2018-09-21 21:32:48 +00:00
|
|
|
use pyo3::class::PyGCProtocol;
|
|
|
|
use pyo3::class::PyTraverseError;
|
|
|
|
use pyo3::class::PyVisit;
|
2018-05-02 18:49:40 +00:00
|
|
|
use pyo3::prelude::*;
|
2020-03-18 04:31:22 +00:00
|
|
|
use pyo3::types::PyTuple;
|
2020-02-15 04:42:25 +00:00
|
|
|
use pyo3::{ffi, py_run, AsPyPointer, PyCell, PyTryInto};
|
2019-02-01 13:01:18 +00:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::Arc;
|
2018-05-02 18:49:40 +00:00
|
|
|
|
|
|
|
mod common;
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass(freelist = 2)]
|
2018-11-12 12:59:19 +00:00
|
|
|
struct ClassWithFreelist {}
|
2018-05-02 18:49:40 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn class_with_freelist() {
|
|
|
|
let ptr;
|
|
|
|
{
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
|
|
|
|
2019-02-07 18:07:09 +00:00
|
|
|
let inst = Py::new(py, ClassWithFreelist {}).unwrap();
|
|
|
|
let _inst2 = Py::new(py, ClassWithFreelist {}).unwrap();
|
2018-05-02 18:49:40 +00:00
|
|
|
ptr = inst.as_ptr();
|
|
|
|
drop(inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
|
|
|
|
2019-02-07 18:07:09 +00:00
|
|
|
let inst3 = Py::new(py, ClassWithFreelist {}).unwrap();
|
2018-05-02 18:49:40 +00:00
|
|
|
assert_eq!(ptr, inst3.as_ptr());
|
|
|
|
|
2019-02-07 18:07:09 +00:00
|
|
|
let inst4 = Py::new(py, ClassWithFreelist {}).unwrap();
|
2018-05-02 18:49:40 +00:00
|
|
|
assert_ne!(ptr, inst4.as_ptr())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TestDropCall {
|
2018-06-15 19:21:12 +00:00
|
|
|
drop_called: Arc<AtomicBool>,
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
2018-10-03 19:04:24 +00:00
|
|
|
|
2018-05-02 18:49:40 +00:00
|
|
|
impl Drop for TestDropCall {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.drop_called.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass]
|
2018-05-02 18:49:40 +00:00
|
|
|
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();
|
2019-02-13 20:35:26 +00:00
|
|
|
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();
|
2018-05-02 18:49:40 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass]
|
2018-11-12 12:59:19 +00:00
|
|
|
struct ClassWithDrop {}
|
2018-10-03 19:04:24 +00:00
|
|
|
|
2018-05-02 18:49:40 +00:00
|
|
|
impl Drop for ClassWithDrop {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
let py = Python::assume_gil_acquired();
|
|
|
|
|
2019-04-17 16:48:59 +00:00
|
|
|
let _empty1: Py<PyTuple> = FromPy::from_py(PyTuple::empty(py), py);
|
2019-08-24 17:21:45 +00:00
|
|
|
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));
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-23 17:01:22 +00:00
|
|
|
// Test behavior of pythonrun::register_pointers + type_object::dealloc
|
2018-05-02 18:49:40 +00:00
|
|
|
#[test]
|
|
|
|
fn create_pointers_in_drop() {
|
|
|
|
let _gil = Python::acquire_gil();
|
|
|
|
|
|
|
|
let ptr;
|
|
|
|
let cnt;
|
|
|
|
{
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
2019-08-24 17:21:45 +00:00
|
|
|
let empty: PyObject = PyTuple::empty(py).into_py(py);
|
2018-05-02 18:49:40 +00:00
|
|
|
ptr = empty.as_ptr();
|
2019-08-24 17:21:45 +00:00
|
|
|
// 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;
|
2019-02-13 20:35:26 +00:00
|
|
|
let inst = Py::new(py, ClassWithDrop {}).unwrap();
|
2018-05-02 18:49:40 +00:00
|
|
|
drop(inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
// empty1 and empty2 are still alive (stored in pointers list)
|
|
|
|
{
|
|
|
|
let _gil = Python::acquire_gil();
|
2018-06-15 19:21:12 +00:00
|
|
|
assert_eq!(cnt + 2, unsafe { ffi::Py_REFCNT(ptr) });
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// empty1 and empty2 should be released
|
|
|
|
{
|
|
|
|
let _gil = Python::acquire_gil();
|
2018-06-15 19:21:12 +00:00
|
|
|
assert_eq!(cnt, unsafe { ffi::Py_REFCNT(ptr) });
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass]
|
2018-05-02 18:49:40 +00:00
|
|
|
struct GCIntegration {
|
|
|
|
self_ref: RefCell<PyObject>,
|
|
|
|
dropped: TestDropCall,
|
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyproto]
|
2018-05-02 18:49:40 +00:00
|
|
|
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();
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn gc_integration() {
|
|
|
|
let drop_called = Arc::new(AtomicBool::new(false));
|
|
|
|
|
|
|
|
{
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
2020-02-15 04:42:25 +00:00
|
|
|
let inst = PyCell::new(
|
2019-02-07 18:07:09 +00:00
|
|
|
py,
|
|
|
|
GCIntegration {
|
|
|
|
self_ref: RefCell::new(py.None()),
|
|
|
|
dropped: TestDropCall {
|
|
|
|
drop_called: Arc::clone(&drop_called),
|
|
|
|
},
|
2018-06-15 19:21:12 +00:00
|
|
|
},
|
2019-02-07 18:07:09 +00:00
|
|
|
)
|
2018-09-28 21:34:57 +00:00
|
|
|
.unwrap();
|
2018-05-02 18:49:40 +00:00
|
|
|
|
2020-02-15 15:24:38 +00:00
|
|
|
let mut borrow = inst.borrow_mut();
|
2020-02-15 04:42:25 +00:00
|
|
|
*borrow.self_ref.borrow_mut() = inst.to_object(py);
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
|
|
|
py.run("import gc; gc.collect()", None, None).unwrap();
|
|
|
|
assert!(drop_called.load(Ordering::Relaxed));
|
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass(gc)]
|
2018-11-12 12:59:19 +00:00
|
|
|
struct GCIntegration2 {}
|
2018-10-03 19:04:24 +00:00
|
|
|
|
2019-07-14 13:44:37 +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) {}
|
2019-07-14 13:44:37 +00:00
|
|
|
}
|
|
|
|
|
2018-05-02 18:49:40 +00:00
|
|
|
#[test]
|
|
|
|
fn gc_integration2() {
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
2020-02-15 04:42:25 +00:00
|
|
|
let inst = PyCell::new(py, GCIntegration2 {}).unwrap();
|
2019-07-14 13:44:37 +00:00
|
|
|
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass(weakref)]
|
2018-11-12 12:59:19 +00:00
|
|
|
struct WeakRefSupport {}
|
2018-10-03 19:04:24 +00:00
|
|
|
|
2018-05-02 18:49:40 +00:00
|
|
|
#[test]
|
|
|
|
fn weakref_support() {
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
2020-02-15 04:42:25 +00:00
|
|
|
let inst = PyCell::new(py, WeakRefSupport {}).unwrap();
|
2018-06-15 19:21:12 +00:00
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
inst,
|
|
|
|
"import weakref; assert weakref.ref(inst)() is inst"
|
|
|
|
);
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
|
2020-02-25 11:02:03 +00:00
|
|
|
// If the base class has weakref support, child class also has weakref.
|
|
|
|
#[pyclass(extends=WeakRefSupport)]
|
|
|
|
struct InheritWeakRef {
|
|
|
|
_value: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn inherited_weakref() {
|
|
|
|
let gil = Python::acquire_gil();
|
|
|
|
let py = gil.python();
|
|
|
|
let inst = PyCell::new(py, (InheritWeakRef { _value: 0 }, WeakRefSupport {})).unwrap();
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
inst,
|
|
|
|
"import weakref; assert weakref.ref(inst)() is inst"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pyclass]
|
2018-05-02 18:49:40 +00:00
|
|
|
struct BaseClassWithDrop {
|
|
|
|
data: Option<Arc<AtomicBool>>,
|
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pymethods]
|
2018-05-02 18:49:40 +00:00
|
|
|
impl BaseClassWithDrop {
|
|
|
|
#[new]
|
2019-12-14 14:16:39 +00:00
|
|
|
fn new() -> BaseClassWithDrop {
|
|
|
|
BaseClassWithDrop { data: None }
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)]
|
2018-05-02 18:49:40 +00:00
|
|
|
struct SubClassWithDrop {
|
|
|
|
data: Option<Arc<AtomicBool>>,
|
|
|
|
}
|
|
|
|
|
2018-07-08 21:33:48 +00:00
|
|
|
#[pymethods]
|
2018-05-02 18:49:40 +00:00
|
|
|
impl SubClassWithDrop {
|
|
|
|
#[new]
|
2020-01-05 07:01:05 +00:00
|
|
|
fn new() -> (Self, BaseClassWithDrop) {
|
|
|
|
(
|
|
|
|
SubClassWithDrop { data: None },
|
|
|
|
BaseClassWithDrop { data: None },
|
|
|
|
)
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2018-05-02 18:49:40 +00:00
|
|
|
|
2020-02-15 04:42:25 +00:00
|
|
|
let obj: &PyCell<SubClassWithDrop> = inst.try_into().unwrap();
|
|
|
|
let mut obj_ref_mut = obj.borrow_mut();
|
|
|
|
obj_ref_mut.data = Some(Arc::clone(&drop_called1));
|
2020-02-21 11:37:35 +00:00
|
|
|
obj_ref_mut.as_mut().data = Some(Arc::clone(&drop_called2));
|
2018-05-02 18:49:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
assert!(drop_called1.load(Ordering::Relaxed));
|
|
|
|
assert!(drop_called2.load(Ordering::Relaxed));
|
|
|
|
}
|