Remove GILPool::new_no_pointer
This commit is contained in:
parent
dd240a6f00
commit
5280a281c9
|
@ -93,11 +93,12 @@ setup(
|
||||||
],
|
],
|
||||||
packages=["rustapi_module"],
|
packages=["rustapi_module"],
|
||||||
rust_extensions=[
|
rust_extensions=[
|
||||||
make_rust_extension("rustapi_module.othermod"),
|
make_rust_extension("rustapi_module.buf_and_str"),
|
||||||
make_rust_extension("rustapi_module.datetime"),
|
make_rust_extension("rustapi_module.datetime"),
|
||||||
|
make_rust_extension("rustapi_module.objstore"),
|
||||||
|
make_rust_extension("rustapi_module.othermod"),
|
||||||
make_rust_extension("rustapi_module.subclassing"),
|
make_rust_extension("rustapi_module.subclassing"),
|
||||||
make_rust_extension("rustapi_module.test_dict"),
|
make_rust_extension("rustapi_module.test_dict"),
|
||||||
make_rust_extension("rustapi_module.buf_and_str"),
|
|
||||||
],
|
],
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
tests_require=tests_require,
|
tests_require=tests_require,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod buf_and_str;
|
pub mod buf_and_str;
|
||||||
pub mod datetime;
|
pub mod datetime;
|
||||||
pub mod dict_iter;
|
pub mod dict_iter;
|
||||||
|
pub mod objstore;
|
||||||
pub mod othermod;
|
pub mod othermod;
|
||||||
pub mod subclassing;
|
pub mod subclassing;
|
||||||
|
|
24
examples/rustapi_module/src/objstore.rs
Normal file
24
examples/rustapi_module/src/objstore.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ObjStore {
|
||||||
|
obj: Vec<PyObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl ObjStore {
|
||||||
|
#[new]
|
||||||
|
fn new() -> Self {
|
||||||
|
ObjStore::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, py: Python, obj: &PyAny) {
|
||||||
|
self.obj.push(obj.to_object(py));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymodule]
|
||||||
|
fn objstore(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
|
m.add_class::<ObjStore>()
|
||||||
|
}
|
24
examples/rustapi_module/tests/test_objstore.py
Normal file
24
examples/rustapi_module/tests/test_objstore.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import gc
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from rustapi_module.objstore import ObjStore
|
||||||
|
|
||||||
|
PYPY = platform.python_implementation() == "PyPy"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(PYPY, reason="PyPy does not have sys.getrefcount")
|
||||||
|
def test_objstore_doesnot_leak_memory():
|
||||||
|
N = 10000
|
||||||
|
message = b'\\(-"-;) Praying that memory leak would not happen..'
|
||||||
|
before = sys.getrefcount(message)
|
||||||
|
store = ObjStore()
|
||||||
|
for _ in range(N):
|
||||||
|
store.push(message)
|
||||||
|
del store
|
||||||
|
gc.collect()
|
||||||
|
after = sys.getrefcount(message)
|
||||||
|
|
||||||
|
assert after - before == 0
|
25
src/gil.rs
25
src/gil.rs
|
@ -107,7 +107,7 @@ impl Drop for GILGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let pool: &'static mut ReleasePool = &mut *POOL;
|
let pool: &'static mut ReleasePool = &mut *POOL;
|
||||||
pool.drain(self.python(), self.owned, self.borrowed, true);
|
pool.drain(self.python(), self.owned, self.borrowed);
|
||||||
|
|
||||||
ffi::PyGILState_Release(self.gstate);
|
ffi::PyGILState_Release(self.gstate);
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ impl ReleasePool {
|
||||||
vec.set_len(0);
|
vec.set_len(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn drain(&mut self, _py: Python, owned: usize, borrowed: usize, pointers: bool) {
|
pub unsafe fn drain(&mut self, _py: Python, owned: usize, borrowed: usize) {
|
||||||
// Release owned objects(call decref)
|
// Release owned objects(call decref)
|
||||||
while owned < self.owned.len() {
|
while owned < self.owned.len() {
|
||||||
let last = self.owned.pop_back().unwrap();
|
let last = self.owned.pop_back().unwrap();
|
||||||
|
@ -160,11 +160,7 @@ impl ReleasePool {
|
||||||
}
|
}
|
||||||
// Release borrowed objects(don't call decref)
|
// Release borrowed objects(don't call decref)
|
||||||
self.borrowed.truncate(borrowed);
|
self.borrowed.truncate(borrowed);
|
||||||
|
self.release_pointers();
|
||||||
if pointers {
|
|
||||||
self.release_pointers();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.obj.clear();
|
self.obj.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +172,6 @@ pub struct GILPool<'p> {
|
||||||
py: Python<'p>,
|
py: Python<'p>,
|
||||||
owned: usize,
|
owned: usize,
|
||||||
borrowed: usize,
|
borrowed: usize,
|
||||||
pointers: bool,
|
|
||||||
no_send: Unsendable,
|
no_send: Unsendable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,18 +183,6 @@ impl<'p> GILPool<'p> {
|
||||||
py,
|
py,
|
||||||
owned: p.owned.len(),
|
owned: p.owned.len(),
|
||||||
borrowed: p.borrowed.len(),
|
borrowed: p.borrowed.len(),
|
||||||
pointers: true,
|
|
||||||
no_send: Unsendable::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn new_no_pointers(py: Python) -> GILPool {
|
|
||||||
let p: &'static mut ReleasePool = unsafe { &mut *POOL };
|
|
||||||
GILPool {
|
|
||||||
py,
|
|
||||||
owned: p.owned.len(),
|
|
||||||
borrowed: p.borrowed.len(),
|
|
||||||
pointers: false,
|
|
||||||
no_send: Unsendable::default(),
|
no_send: Unsendable::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,7 +192,7 @@ impl<'p> Drop for GILPool<'p> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let pool: &'static mut ReleasePool = &mut *POOL;
|
let pool: &'static mut ReleasePool = &mut *POOL;
|
||||||
pool.drain(self.py, self.owned, self.borrowed, self.pointers);
|
pool.drain(self.py, self.owned, self.borrowed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ where
|
||||||
T: PyClassAlloc,
|
T: PyClassAlloc,
|
||||||
{
|
{
|
||||||
let py = Python::assume_gil_acquired();
|
let py = Python::assume_gil_acquired();
|
||||||
let _pool = gil::GILPool::new_no_pointers(py);
|
let _pool = gil::GILPool::new(py);
|
||||||
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::Layout) as _)
|
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::Layout) as _)
|
||||||
}
|
}
|
||||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||||
|
|
|
@ -2,8 +2,7 @@ use pyo3::class::PyGCProtocol;
|
||||||
use pyo3::class::PyTraverseError;
|
use pyo3::class::PyTraverseError;
|
||||||
use pyo3::class::PyVisit;
|
use pyo3::class::PyVisit;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::PyTuple;
|
use pyo3::{py_run, AsPyPointer, PyCell, PyTryInto};
|
||||||
use pyo3::{ffi, py_run, AsPyPointer, PyCell, PyTryInto};
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -81,52 +80,6 @@ fn data_is_dropped() {
|
||||||
assert!(drop_called2.load(Ordering::Relaxed));
|
assert!(drop_called2.load(Ordering::Relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct ClassWithDrop {}
|
|
||||||
|
|
||||||
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);
|
|
||||||
let _empty3: &PyAny = py.from_owned_ptr(ffi::PyTuple_New(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
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)]
|
#[allow(dead_code)]
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct GCIntegration {
|
struct GCIntegration {
|
||||||
|
|
Loading…
Reference in a new issue