Skip to content

Commit d7fe7a9

Browse files
authored
Merge pull request #835 from kngwyu/remove-new-no-ptr
Remove GILPool::new-no-pointer
2 parents db543a0 + 5280a28 commit d7fe7a9

File tree

7 files changed

+58
-72
lines changed

7 files changed

+58
-72
lines changed

examples/rustapi_module/setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,12 @@ def make_rust_extension(module_name):
9393
],
9494
packages=["rustapi_module"],
9595
rust_extensions=[
96-
make_rust_extension("rustapi_module.othermod"),
96+
make_rust_extension("rustapi_module.buf_and_str"),
9797
make_rust_extension("rustapi_module.datetime"),
98+
make_rust_extension("rustapi_module.objstore"),
99+
make_rust_extension("rustapi_module.othermod"),
98100
make_rust_extension("rustapi_module.subclassing"),
99101
make_rust_extension("rustapi_module.test_dict"),
100-
make_rust_extension("rustapi_module.buf_and_str"),
101102
],
102103
install_requires=install_requires,
103104
tests_require=tests_require,

examples/rustapi_module/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod buf_and_str;
22
pub mod datetime;
33
pub mod dict_iter;
4+
pub mod objstore;
45
pub mod othermod;
56
pub mod subclassing;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use pyo3::prelude::*;
2+
3+
#[pyclass]
4+
#[derive(Default)]
5+
pub struct ObjStore {
6+
obj: Vec<PyObject>,
7+
}
8+
9+
#[pymethods]
10+
impl ObjStore {
11+
#[new]
12+
fn new() -> Self {
13+
ObjStore::default()
14+
}
15+
16+
fn push(&mut self, py: Python, obj: &PyAny) {
17+
self.obj.push(obj.to_object(py));
18+
}
19+
}
20+
21+
#[pymodule]
22+
fn objstore(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
23+
m.add_class::<ObjStore>()
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import gc
2+
import platform
3+
import sys
4+
5+
import pytest
6+
7+
from rustapi_module.objstore import ObjStore
8+
9+
PYPY = platform.python_implementation() == "PyPy"
10+
11+
12+
@pytest.mark.skipif(PYPY, reason="PyPy does not have sys.getrefcount")
13+
def test_objstore_doesnot_leak_memory():
14+
N = 10000
15+
message = b'\\(-"-;) Praying that memory leak would not happen..'
16+
before = sys.getrefcount(message)
17+
store = ObjStore()
18+
for _ in range(N):
19+
store.push(message)
20+
del store
21+
gc.collect()
22+
after = sys.getrefcount(message)
23+
24+
assert after - before == 0

src/gil.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl Drop for GILGuard {
107107
fn drop(&mut self) {
108108
unsafe {
109109
let pool: &'static mut ReleasePool = &mut *POOL;
110-
pool.drain(self.python(), self.owned, self.borrowed, true);
110+
pool.drain(self.python(), self.owned, self.borrowed);
111111

112112
ffi::PyGILState_Release(self.gstate);
113113
}
@@ -152,19 +152,15 @@ impl ReleasePool {
152152
vec.set_len(0);
153153
}
154154

155-
pub unsafe fn drain(&mut self, _py: Python, owned: usize, borrowed: usize, pointers: bool) {
155+
pub unsafe fn drain(&mut self, _py: Python, owned: usize, borrowed: usize) {
156156
// Release owned objects(call decref)
157157
while owned < self.owned.len() {
158158
let last = self.owned.pop_back().unwrap();
159159
ffi::Py_DECREF(last.as_ptr());
160160
}
161161
// Release borrowed objects(don't call decref)
162162
self.borrowed.truncate(borrowed);
163-
164-
if pointers {
165-
self.release_pointers();
166-
}
167-
163+
self.release_pointers();
168164
self.obj.clear();
169165
}
170166
}
@@ -176,7 +172,6 @@ pub struct GILPool<'p> {
176172
py: Python<'p>,
177173
owned: usize,
178174
borrowed: usize,
179-
pointers: bool,
180175
no_send: Unsendable,
181176
}
182177

@@ -188,18 +183,6 @@ impl<'p> GILPool<'p> {
188183
py,
189184
owned: p.owned.len(),
190185
borrowed: p.borrowed.len(),
191-
pointers: true,
192-
no_send: Unsendable::default(),
193-
}
194-
}
195-
#[inline]
196-
pub fn new_no_pointers(py: Python) -> GILPool {
197-
let p: &'static mut ReleasePool = unsafe { &mut *POOL };
198-
GILPool {
199-
py,
200-
owned: p.owned.len(),
201-
borrowed: p.borrowed.len(),
202-
pointers: false,
203186
no_send: Unsendable::default(),
204187
}
205188
}
@@ -209,7 +192,7 @@ impl<'p> Drop for GILPool<'p> {
209192
fn drop(&mut self) {
210193
unsafe {
211194
let pool: &'static mut ReleasePool = &mut *POOL;
212-
pool.drain(self.py, self.owned, self.borrowed, self.pointers);
195+
pool.drain(self.py, self.owned, self.borrowed);
213196
}
214197
}
215198
}

src/pyclass.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ where
112112
T: PyClassAlloc,
113113
{
114114
let py = Python::assume_gil_acquired();
115-
let _pool = gil::GILPool::new_no_pointers(py);
115+
let _pool = gil::GILPool::new(py);
116116
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::Layout) as _)
117117
}
118118
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);

tests/test_gc.rs

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use pyo3::class::PyGCProtocol;
22
use pyo3::class::PyTraverseError;
33
use pyo3::class::PyVisit;
44
use pyo3::prelude::*;
5-
use pyo3::types::PyTuple;
6-
use pyo3::{ffi, py_run, AsPyPointer, PyCell, PyTryInto};
5+
use pyo3::{py_run, AsPyPointer, PyCell, PyTryInto};
76
use std::cell::RefCell;
87
use std::sync::atomic::{AtomicBool, Ordering};
98
use std::sync::Arc;
@@ -81,52 +80,6 @@ fn data_is_dropped() {
8180
assert!(drop_called2.load(Ordering::Relaxed));
8281
}
8382

84-
#[pyclass]
85-
struct ClassWithDrop {}
86-
87-
impl Drop for ClassWithDrop {
88-
fn drop(&mut self) {
89-
unsafe {
90-
let py = Python::assume_gil_acquired();
91-
92-
let _empty1: Py<PyTuple> = FromPy::from_py(PyTuple::empty(py), py);
93-
let _empty2: PyObject = PyTuple::empty(py).into_py(py);
94-
let _empty3: &PyAny = py.from_owned_ptr(ffi::PyTuple_New(0));
95-
}
96-
}
97-
}
98-
99-
// Test behavior of pythonrun::register_pointers + type_object::dealloc
100-
#[test]
101-
fn create_pointers_in_drop() {
102-
let _gil = Python::acquire_gil();
103-
104-
let ptr;
105-
let cnt;
106-
{
107-
let gil = Python::acquire_gil();
108-
let py = gil.python();
109-
let empty: PyObject = PyTuple::empty(py).into_py(py);
110-
ptr = empty.as_ptr();
111-
// substract 2, because `PyTuple::empty(py).into_py(py)` increases the refcnt by 2
112-
cnt = empty.get_refcnt() - 2;
113-
let inst = Py::new(py, ClassWithDrop {}).unwrap();
114-
drop(inst);
115-
}
116-
117-
// empty1 and empty2 are still alive (stored in pointers list)
118-
{
119-
let _gil = Python::acquire_gil();
120-
assert_eq!(cnt + 2, unsafe { ffi::Py_REFCNT(ptr) });
121-
}
122-
123-
// empty1 and empty2 should be released
124-
{
125-
let _gil = Python::acquire_gil();
126-
assert_eq!(cnt, unsafe { ffi::Py_REFCNT(ptr) });
127-
}
128-
}
129-
13083
#[allow(dead_code)]
13184
#[pyclass]
13285
struct GCIntegration {

0 commit comments

Comments
 (0)