-
Notifications
You must be signed in to change notification settings - Fork 117
Fix forwarded value captured policy #2373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Even capturing by reference would result in a use-after-move-bug Signed-off-by: Dan Hoeflinger <[email protected]>
Signed-off-by: Dan Hoeflinger <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a critical bug in parallel set union operations where execution policies were being incorrectly forwarded within lambda captures. The fix prevents use-after-move errors by removing the forwarding of captured execution policies that would otherwise be moved multiple times.
- Removed
std::forward<_ExecutionPolicy>(__exec)
calls from lambda captures in parallel set union operations - Changed all lambda captures to use the execution policy by value without forwarding
- Applied the fix consistently across multiple conditional branches in the set union algorithm
@danhoeflinger I think we really had not quite correct code before your changes. Let's take a look for example to the code: template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator1, class _RandomAccessIterator2,
class _OutputIterator, class _Compare, class _SetUnionOp>
_OutputIterator
__parallel_set_union_op(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __exec, _RandomAccessIterator1 __first1,
_RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2, _RandomAccessIterator2 __last2,
_OutputIterator __result, _Compare __comp, _SetUnionOp __set_union_op)
{
//...
if (__left_bound_seq_1 == __last1)
{
//{1} < {2}: seq2 is wholly greater than seq1, so, do parallel copying seq1 and seq2
__par_backend::__parallel_invoke(
__backend_tag{}, ::std::forward<_ExecutionPolicy>(__exec),
[=] {
__internal::__pattern_walk2_brick(__tag, ::std::forward<_ExecutionPolicy>(__exec), __first1, __last1,
__result, __copy_range);
},
[=] {
__internal::__pattern_walk2_brick(__tag, ::std::forward<_ExecutionPolicy>(__exec), __first2, __last2,
__result + __n1, __copy_range);
});
return __result + __n1 + __n2;
}
//...
} As we may see here, we call But also we using [=] {
__internal::__pattern_walk2_brick(__tag, ::std::forward<_ExecutionPolicy>(__exec), __first1, __last1,
__result, __copy_range);
}, This mean that So I think this PR in it current state doesn't fix this problem too. I propose to rewrite: if (__left_bound_seq_1 == __last1)
{
//{1} < {2}: seq2 is wholly greater than seq1, so, do parallel copying seq1 and seq2
__par_backend::__parallel_invoke(
__backend_tag{},
__exec, // We real error was here when we had std::forward<_ExecutionPolicy>(__exec)
[=] {
__internal::__pattern_walk2_brick(
__tag,
// As far as I understand this std::forward isn't real problem here at least while we have [=]
std::forward<_ExecutionPolicy>(__exec),
__first1, __last1, __result, __copy_range);
},
[=] {
__internal::__pattern_walk2_brick(
__tag,
// As far as I understand this std::forward isn't real problem here at least while we have [=]
std::forward<_ExecutionPolicy>(__exec),
__first2, __last2, __result + __n1, __copy_range);
});
return __result + __n1 + __n2;
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think additional changes required...
UPD: probably this PR doest't needed at all.
Folks, where have you found "multiple forwards" in the original code? |
Agreed ! |
I understand that forward is merely a type cast. I don't think I did a good job of stating the problem. https://godbolt.org/z/75ejWxY6b The real issue here (as I understand it) is that when capturing by value into the lambda, the type of What I was trying to mention with the "multiple forwards" comment is what is shown in the last option in the godbolt. IF we were to try to "fix" this by capture by reference, the two usages in the lambda would fail with a use-after-move issue. In this exploration, I think that the original version here in this PR is probably not as good as if we were to If I'm missing something here with my explanation and godbolt, please let me know. edit: I should mention that the branchname is misleading, and was not a good name for it as the bug is not a "use after move", but rather a poor usage of |
Signed-off-by: Dan Hoeflinger <[email protected]>
So the problem really has place.
|
As mentioned in offline discussion with @SergeyKopienko, the reason this did not appear as a build error in our existing test suite is because the nature of our "canned" host policies like If someone copies these policies to a non-const instance before calling an API, we would see the issue (before the fixes in this PR). |
Signed-off-by: Dan Hoeflinger <[email protected]>
Signed-off-by: Dan Hoeflinger <[email protected]>
Updated after offline discussion. Changing from moving after value capture to pass directly after capture by reference. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
In this PR we switch from forwarding after a capture by value to a capture by reference and then pass through.
Forwarding doesn't make sense here as the type may no longer match.
branched off from
https://github.com/uxlfoundation/oneDPL/pull/2369/files#r2254402955