Skip to content

Conversation

@jakirkham
Copy link
Member

Follow up on this discussion: #692 (comment)

Ensure that the acquired buffer is properly released even if an exception is raised. Given we may raise an exception, this is important to check.

cc @vyasr

@jakirkham jakirkham requested a review from a team as a code owner May 22, 2025 17:53
@jakirkham jakirkham added bug Something isn't working non-breaking Introduces a non-breaking change labels May 22, 2025
Copy link
Member Author

@jakirkham jakirkham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to call out the key points of this change below and why they matter

Since a block of code is indented the GitHub diff looks more involved than it actually is

So hopefully this clarifies the nature of the change

)
if not PyBuffer_IsContiguous(&pybuf, b"C"):
self.strides_mv = new_Py_ssize_t_array(self.ndim)
try:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core of this change involves adding a try for the block of code right after PyObject_GetBuffer

Comment on lines +162 to +163
finally:
PyBuffer_Release(&pybuf)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then moving the PyBuffer_Release into a finally block

That way we still release the underlying Py_buffer object if an exception is raised

Comment on lines +134 to +135
if pybuf.suboffsets != NULL:
raise NotImplementedError("Suboffsets are not supported")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example here is one exception we raise

Though it is also possible to wind up with an exception due to some bug in the code below

In any event we want to make sure normal resource cleanup can proceed as usual (without potentially causing segfaults)

Copy link
Contributor

@vyasr vyasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah OK this is fine. #692 (comment) made it sound like you wanted the PyObject_GetBuffer call itself inside a try-except block, which didn't make sense to me since it wouldn't be throwing if that failed. I agree that this change makes sense to avoid leaking the buffer if anything fails in the interim.

@vyasr
Copy link
Contributor

vyasr commented May 22, 2025

/merge

@rapids-bot rapids-bot bot merged commit b2281de into rapidsai:branch-25.06 May 22, 2025
67 checks passed
@jakirkham jakirkham deleted the add_try_finally_pybuf branch May 22, 2025 23:27
@jakirkham
Copy link
Member Author

Thanks Vyas! 🙏

Yeah can understand how what I said there was unclear. Probably I should have commented on the release line or drew a clearer connection between the acquisition & release as I did here

@jakirkham
Copy link
Member Author

Should have clarified one other point

...the PyObject_GetBuffer call itself inside a try-except block, which didn't make sense to me since it wouldn't be throwing if that failed.

PyObject_GetBuffer does raise if it fails. From the docs:

If the exporter cannot provide a buffer of the exact type, it MUST raise BufferError, set view->obj to NULL and return -1.

That said, I don't think we need to do anything special in this case as Py_buffer remains undefined and obj will go through normal ref-count handling

@vyasr
Copy link
Contributor

vyasr commented Jun 10, 2025

Should have clarified one other point

...the PyObject_GetBuffer call itself inside a try-except block, which didn't make sense to me since it wouldn't be throwing if that failed.

PyObject_GetBuffer does raise if it fails. From the docs:

If the exporter cannot provide a buffer of the exact type, it MUST raise BufferError, set view->obj to NULL and return -1.

Yes it can raise, but a Python try-except block is not going to do anything to catch an exception raised by a CPython function. You have to check the return value and then manually reraise, hence why I was confused by the try-except suggestion. We could update the code to do that, but I think we know that we never call this function with an input that will raise so I didn't bother to do that here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants