Skip to content

Commit 9252e7d

Browse files
committed
Improve drop_stream function to handle PyArrow ownership transfer correctly
1 parent 2c2e07e commit 9252e7d

1 file changed

Lines changed: 22 additions & 6 deletions

File tree

src/dataframe.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,25 @@ unsafe extern "C" fn drop_stream(capsule: *mut ffi::PyObject) {
6767
if capsule.is_null() {
6868
return;
6969
}
70-
let stream_ptr =
71-
ffi::PyCapsule_GetPointer(capsule, ARROW_STREAM_NAME.as_ptr()) as *mut FFI_ArrowArrayStream;
72-
if stream_ptr.is_null() {
73-
ffi::PyErr_Clear();
74-
return;
70+
71+
// When PyArrow imports this capsule it steals the raw stream pointer and
72+
// sets the capsule's internal pointer to NULL. In that case
73+
// `PyCapsule_IsValid` returns 0 and this destructor must not drop the
74+
// stream as ownership has been transferred to PyArrow. If the capsule was
75+
// never imported, the pointer remains valid and we are responsible for
76+
// freeing the stream here.
77+
if ffi::PyCapsule_IsValid(capsule, ARROW_STREAM_NAME.as_ptr()) == 1 {
78+
let stream_ptr = ffi::PyCapsule_GetPointer(capsule, ARROW_STREAM_NAME.as_ptr())
79+
as *mut FFI_ArrowArrayStream;
80+
if !stream_ptr.is_null() {
81+
drop(Box::from_raw(stream_ptr));
82+
}
7583
}
76-
drop(Box::from_raw(stream_ptr));
84+
85+
// `PyCapsule_GetPointer` sets a Python error on failure. Clear it only
86+
// after the stream has been released (or determined to be owned
87+
// elsewhere).
88+
ffi::PyErr_Clear();
7789
}
7890

7991
// https://github.com/apache/datafusion-python/pull/1016#discussion_r1983239116
@@ -976,6 +988,10 @@ impl PyDataFrame {
976988
!stream_ptr.is_null(),
977989
"ArrowArrayStream pointer should never be null",
978990
);
991+
// The returned capsule allows zero-copy hand-off to PyArrow. When
992+
// PyArrow imports the capsule it assumes ownership of the stream and
993+
// nulls out the capsule's internal pointer so `drop_stream` knows not to
994+
// free it.
979995
let capsule = unsafe {
980996
ffi::PyCapsule_New(
981997
stream_ptr as *mut c_void,

0 commit comments

Comments
 (0)