fix: node invariant at exception#1454
Conversation
|
|
||
| for ( typename elements_type::const_iterator it = elements.begin() ; | ||
| it != elements.end() ; ++it ) | ||
| BOOST_TRY |
There was a problem hiding this comment.
For review, best to ignore white space changes
|
|
||
| BOOST_RETHROW | ||
| } | ||
| BOOST_CATCH_END |
There was a problem hiding this comment.
See also the part below, it is similar (but not identical). It was just missing here.
| // the behavior of the split exception handler. | ||
| result_elements.clear(); | ||
|
|
||
| auto & elems = rtree::elements(n); |
There was a problem hiding this comment.
Is there a reason for redefining this here? It seems to be the same as elements on line 102, https://github.com/barendgehrels/geometry/blob/ad2761fe3582714d9a6efea8189f2ea25125bda3/include/boost/geometry/index/detail/rtree/rstar/insert.hpp#L102 , which is above the scope of the try-catch block.
There was a problem hiding this comment.
good point I will check it
There was a problem hiding this comment.
The n is a non-const reference so that might be why it is repeated here
There was a problem hiding this comment.
elements.clear(); is also used below.
So probably we should indeed omit it - that makes the two blocks more consistent
I'll try tomorrow
There was a problem hiding this comment.
A bit delayed but I tested it using elements instead, all fine. Will update PR.
| result_elements.clear(); | ||
|
|
||
| auto & elems = rtree::elements(n); | ||
| if (elems.size() > parameters.get_max_elements()) |
There was a problem hiding this comment.
Is this not already implied by the combination of L102, L104, L108? https://github.com/barendgehrels/geometry/blob/ad2761fe3582714d9a6efea8189f2ea25125bda3/include/boost/geometry/index/detail/rtree/rstar/insert.hpp#L102-L108 , i.e. elements is rtree::elements(n), elements_count is max_elements + 1, then we assert elements.size is elements_count which is max_elements+1. Maybe I am missing something here that can change the value of n but it all looks non-modifying.
There was a problem hiding this comment.
This is the essence of the fix - so yes, it is necessary.
The point is that it is only necessary if something was thrown.
And (apparently) I was (already for a year) the only one who got this, in a unit test, always failing for me...
There was a problem hiding this comment.
Sorry, I didn't mean the entire block. I mean maybe remove just the elems.size() > parameters.get_max_elements() check because it is already guaranteed true. For the destroy_element and the pop_back, I understand those are the essence of the fix. I.e. I propose
--- a/include/boost/geometry/index/detail/rtree/rstar/insert.hpp
+++ b/include/boost/geometry/index/detail/rtree/rstar/insert.hpp
@@ -165,12 +165,8 @@ public:
// the behavior of the split exception handler.
result_elements.clear();
- auto & elems = rtree::elements(n);
- if (elems.size() > parameters.get_max_elements())
- {
- destroy_element<MembersHolder>::apply(elems.back(), allocators);
- elems.pop_back();
- }
+ destroy_element<MembersHolder>::apply(elements.back(), allocators);
+ elements.pop_back();As you state in the new comment https://github.com/barendgehrels/geometry/blob/ad2761fe3582714d9a6efea8189f2ea25125bda3/include/boost/geometry/index/detail/rtree/rstar/insert.hpp#L162
// [...] At this point the node
// has not been modified yet and still contains exactly max+1 elements
This was asserted in L108 before the try catch block and nothing that follows is modifying n or elements (the code only takes const iterators).
There was a problem hiding this comment.
It is the catch block where, in the block above, 4 comments are made with MAY THROW.
So I think and prefer the condition should stay...
The specific comment is added to state that there are not more than 1 (earlier I want to make it a while - but that's not necessary)
There was a problem hiding this comment.
The test passes, also without condition - but still I prefer to have the condition there.
There was a problem hiding this comment.
Additional AI input about both:
- Reusing elements: Yes, perfectly fine. elements is defined at line 102 as elements_type & elements = rtree::elements(n); — a reference that remains valid throughout the function since n isn't reassigned. Reusing it makes the catch block read more naturally alongside the surrounding code (which uses elements everywhere), and it's one less name to follow.
- Keeping the if check: Good call. Even though we can prove it's always exactly max+1 given the current call flow, defensive checks like this cost nothing at runtime and protect against:
- Future refactors that might change the call flow
- The catch block being exercised in some unexpected way (e.g., a BOOST_TRY reentry scenario)
- Matching the pattern in visitors/insert.hpp:210 which also has a size() > max_size guard before popping
If someone later changes how overflow is triggered, the guard means the code degrades gracefully instead of corrupting via an unconditional pop_back on an already-valid node.
|
The overall change seems consistent with the other exception handling case to me, so I am in favor. I left two comments on aspect that are unclear to me. Since you are already touching this function, can you make sense of https://github.com/barendgehrels/geometry/blob/ad2761fe3582714d9a6efea8189f2ea25125bda3/include/boost/geometry/index/detail/rtree/rstar/insert.hpp#L109 ? It seems dubious since it checks an unsigned value for positivity and that unsigned value is also purely a function of parameters, seems like it should move to https://github.com/boostorg/geometry/blame/develop/include/boost/geometry/index/parameters.hpp . |
| @@ -125,32 +125,56 @@ class remove_elements_to_reinsert | |||
| // If constructor is used instead of resize() MS implementation leaks here | |||
| sorted_elements.reserve(elements_count); // MAY THROW, STRONG (V, E: alloc, copy) | |||
There was a problem hiding this comment.
Should this line also go inside the TRY CATCH block since it may also throw and would still leave the node in the invalid state? We can also remove the "copy" from the comment, since reserving on an empty vector should never copy anything. This should only throw if the alloc fails.
There was a problem hiding this comment.
Good one. Fixed. AI again:
moved
sorted_elements.reserve(elements_count)inside the BOOST_TRY block so that an allocation failure now triggers the catch handler that pops the overflow element, and updated the comment from(V, E: alloc, copy)to(alloc)since reserving on an empty vector doesn't copy anything — it only allocates.
It is not advised. |
|
Pushed the two additional fixes or amendments, @tinko92 thanks again for your comments. |
tinko92
left a comment
There was a problem hiding this comment.
Thanks for addressing everything and all the work in the PR! I'm fine with merging.
Fixes: #1399
Description
This fixes the errors in
boost_geometry_index_rtree_exceptions_rstthat I encountered before.Now with help of Claude Code, reporting:
Solution
By Claude Code:
I reviewed it myself and it looks completely right, and it solves the problem.
@awulkiew can you verify this as well?