Use references explicitly, and avoid dangling references captured by complete_request_handler_ #924
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
After an exception is thrown, I often get SEGFAULTs. Not always.
So I checked with valgrind, and it reliably picks up a problem:
(trimmed for brevity)
So the problem is
handle_rule(BaseRule* rule)
bringsrule
in as a plain pointer parameter.Then, this line creates a lambda that captures the reference to that pointer parameter.
https://github.com/CrowCpp/Crow/blob/25ca2f54e19e7c266d55fb3485d315d639d3f3f0/include/crow/routing.h#L1761C1-L1761C113
That reference becomes dangling after
handle_rule()
completes.The
complete_request_handler
is later called and the reference to the pointer is now dangling.There are three ways to solve this.
One is to change
handle_rule(BaseRule* rule)
to accept a reference to the pointer, iehandle_rule(BaseRule*& rule)
That means it would capture the pointer from way back on this line:
Crow/include/crow/routing.h
Line 1728 in 25ca2f5
The location of that pointer doesn't change, it is an entry in the
rules
vector which isn't supposed to change after the app starts ... right? Actually I'm not sure if that is a valid assumption.Anyway, that approach ... I find that confusing.
You could make it look more consistent with the rest of the code by changing it to an auto&
handle_rule(auto& rule)
then all the parameters are references to something... but it is still hard to reason about lifetimes.
Instead, capture by copy. It's a pointer, there is no reason to capture by reference.
so this line:
https://github.com/CrowCpp/Crow/blob/25ca2f54e19e7c266d55fb3485d315d639d3f3f0/include/crow/routing.h#L1761C1-L1761C113
becomes:
res.complete_request_handler_ = [rule, &ctx, &container, &req, &res, glob_completion_handler] {
But that means the code becomes inconsistent. Some are captured by ref, some by pointer. Hard to know what it should be.
The other way is to use a reference to the rule.
This makes the assumption of lifetimes explicit, and makes the code consistent. And we can capture by reference as before.
ie,
handle_rule(BaseRule& rule)
and the call:
Crow/include/crow/routing.h
Line 1728 in 25ca2f5
becomes:
BaseRule& rule = rules[rule_index];