Skip to content

Commit aec309c

Browse files
committed
fix memoryview avoid double release buffer
1 parent 2740d4b commit aec309c

File tree

4 files changed

+41
-19
lines changed

4 files changed

+41
-19
lines changed

vm/src/builtins/bytearray.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ impl PyByteArray {
115115
Ok(())
116116
}
117117

118+
#[cfg(debug_assertions)]
119+
#[pyproperty]
120+
fn exports(&self) -> usize {
121+
self.exports.load()
122+
}
123+
118124
#[inline]
119125
fn inner(&self) -> PyRwLockReadGuard<'_, PyBytesInner> {
120126
self.inner.read()

vm/src/builtins/memory.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
use crossbeam_utils::atomic::AtomicCell;
2020
use itertools::Itertools;
2121
use num_traits::ToPrimitive;
22-
use std::fmt::Debug;
22+
use std::{fmt::Debug, mem::ManuallyDrop};
2323

2424
#[derive(FromArgs)]
2525
pub struct PyMemoryViewNewArgs {
@@ -29,7 +29,8 @@ pub struct PyMemoryViewNewArgs {
2929
#[pyclass(module = false, name = "memoryview")]
3030
#[derive(Debug)]
3131
pub struct PyMemoryView {
32-
buffer: PyBuffer,
32+
// avoid double release when memoryview had released the buffer before drop
33+
buffer: ManuallyDrop<PyBuffer>,
3334
// the released memoryview does not mean the buffer is destoryed
3435
// because the possible another memeoryview is viewing from it
3536
released: AtomicCell<bool>,
@@ -93,7 +94,7 @@ impl PyMemoryView {
9394
let stop = options.len * options.itemsize;
9495
let format_spec = Self::parse_format(&options.format, vm)?;
9596
Ok(PyMemoryView {
96-
buffer,
97+
buffer: ManuallyDrop::new(buffer),
9798
released: AtomicCell::new(false),
9899
start: 0,
99100
stop,
@@ -114,7 +115,7 @@ impl PyMemoryView {
114115
let itemsize = options.itemsize;
115116
let format_spec = Self::parse_format(&options.format, vm)?;
116117
Ok(PyMemoryView {
117-
buffer,
118+
buffer: ManuallyDrop::new(buffer),
118119
released: AtomicCell::new(false),
119120
start: range.start * itemsize,
120121
stop: range.end * itemsize,
@@ -132,7 +133,6 @@ impl PyMemoryView {
132133
#[pymethod]
133134
pub fn release(&self) {
134135
if self.released.compare_exchange(false, true).is_ok() {
135-
self.buffer.manually_release = true;
136136
self.buffer.release();
137137
}
138138
}
@@ -689,6 +689,16 @@ impl AsBuffer for PyMemoryView {
689689
}
690690
}
691691

692+
impl Drop for PyMemoryView {
693+
fn drop(&mut self) {
694+
if self.released.load() {
695+
unsafe { self.buffer.drop_without_release() };
696+
} else {
697+
unsafe { ManuallyDrop::drop(&mut self.buffer) };
698+
}
699+
}
700+
}
701+
692702
impl AsMapping for PyMemoryView {
693703
fn as_mapping(_zelf: &PyObjectView<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
694704
PyMappingMethods {

vm/src/protocol/buffer.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{
1212
};
1313
use std::{borrow::Cow, fmt::Debug};
1414

15+
#[allow(clippy::type_complexity)]
1516
pub struct BufferMethods {
1617
// always reflecting the whole bytes of the most top object
1718
pub obj_bytes: fn(&PyBuffer) -> BorrowedValue<[u8]>,
@@ -45,8 +46,6 @@ impl Debug for BufferMethods {
4546
pub struct PyBuffer {
4647
pub obj: PyObjectRef,
4748
pub options: BufferOptions,
48-
// if true, don't call release when drop
49-
pub manually_release: bool,
5049
methods: &'static BufferMethods,
5150
}
5251

@@ -55,7 +54,6 @@ impl PyBuffer {
5554
let zelf = Self {
5655
obj,
5756
options,
58-
manually_release: false,
5957
methods,
6058
};
6159
zelf.retain();
@@ -132,12 +130,23 @@ impl PyBuffer {
132130
.unwrap_or_else(|| self.obj_bytes_mut())
133131
}
134132

133+
// WARNING: should always try to clone from the contiguous first
135134
pub(crate) fn _collect_bytes(&self, buf: &mut Vec<u8>) {
136135
self.methods
137136
.collect_bytes
138137
.map(|f| f(self, buf))
139138
.unwrap_or_else(|| buf.extend_from_slice(&self.obj_bytes()))
140139
}
140+
141+
// drop PyBuffer without calling release
142+
// after this function, the owner should use forget()
143+
// or wrap PyBuffer in the ManaullyDrop to prevent drop()
144+
pub(crate) unsafe fn drop_without_release(&mut self) {
145+
// self.obj = PyObjectRef::from_raw(0 as *const PyObject);
146+
// self.options = BufferOptions::default();
147+
std::ptr::drop_in_place(&mut self.obj);
148+
std::ptr::drop_in_place(&mut self.options);
149+
}
141150
}
142151

143152
#[derive(Debug, Clone)]
@@ -193,9 +202,7 @@ impl TryFromBorrowedObject for PyBuffer {
193202
// but it is not supported by Rust
194203
impl Drop for PyBuffer {
195204
fn drop(&mut self) {
196-
if !self.manually_release {
197-
self.release();
198-
}
205+
self.release();
199206
}
200207
}
201208

vm/src/stdlib/io.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -876,9 +876,12 @@ mod _io {
876876
} else {
877877
let v = std::mem::take(&mut self.buffer);
878878
let writebuf = VecBuffer::new(v).into_ref(vm);
879-
let memobj =
880-
PyMemoryView::from_buffer_range(writebuf.clone().into_readonly_pybuffer(), buf_range, vm)?
881-
.into_ref(vm);
879+
let memobj = PyMemoryView::from_buffer_range(
880+
writebuf.clone().into_readonly_pybuffer(),
881+
buf_range,
882+
vm,
883+
)?
884+
.into_ref(vm);
882885

883886
// TODO: loop if write() raises an interrupt
884887
let res = vm.call_method(self.raw.as_ref().unwrap(), "write", (memobj.clone(),));
@@ -3286,11 +3289,7 @@ mod _io {
32863289
len: self.buffer.read().cursor.get_ref().len(),
32873290
..Default::default()
32883291
};
3289-
let buffer = PyBuffer::new(
3290-
self.clone().into_object(),
3291-
options,
3292-
&BYTES_IO_BUFFER_METHODS,
3293-
);
3292+
let buffer = PyBuffer::new(self.into_object(), options, &BYTES_IO_BUFFER_METHODS);
32943293
let view = PyMemoryView::from_buffer(buffer, vm)?;
32953294
Ok(view)
32963295
}

0 commit comments

Comments
 (0)