-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Syncer improvements & Concurrency fixes #9159
Conversation
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.
Looks good, just 1 comment
std::unique_lock<std::mutex> lock(_mutex); | ||
if (_accepting) | ||
_enq_cv.wait( lock, [this]() { |
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.
Why do we wait if we are not accepting?
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.
wait( lock, pred )
is the same as:
while( ! pred() ) {
wait( lock );
}
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.
If we are not accepting, the queue should be empty, so we shouldn't really wait, no?
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.
So you are relying on logic elsewhere that say (accepting = false) is the same as (size = 0)
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.
Well yes, I guess so: compound predicates get less readable because of the !
. Would it be better if I wrote the pred as return _accepting && _queue.size() < _cap;
?
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.
Actually, it gets more complicated...
You want:
while( _accepting && _queue.size() >= _cap ) {
wait();
}
So the logic inside the pred should be return ! _accepting || _queue.size() < _cap;
...
_was_flushed = false; | ||
const auto ready = [this]() { return (_queue.size() > 0) || _need_to_flush; }; | ||
if (!ready() && !_deq_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), ready)) | ||
if( ! _deq_cv.wait_for( lock, |
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.
This if statement is really confusing..
too much "!" operators..
I suggest rewrite it as a positive if so it will be more understandable.
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.
Everything with these predicates is confusing :)
if (_accepting) | ||
_enq_cv.wait( lock, [this]() { | ||
return _queue.size() < _cap; } ); | ||
if( ! _accepting ) |
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.
Shouldn't it be inside the predicate ?
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.
If we stop accepting then someone called stop() which calls clear() which empties out the queue which should make the predicate true and stop.
|
||
// Wait until any dispatched is done... | ||
{ | ||
std::lock_guard< std::mutex > lock( _dispatch_mutex ); |
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.
Its not ensure that the dispatcher completely stopped , it can be on the middle of the last item.
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.
It will make this code wait until the dispatcher is done
if (!ready() && !_deq_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), ready)) | ||
if( ! _deq_cv.wait_for( lock, | ||
std::chrono::milliseconds( timeout_ms ), | ||
[this]() { return ! _accepting || ! _queue.empty(); } ) |
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.
_accepting && ! _queue.empty(); will be more understandable and you will not need the || _queue.empty() )
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.
while( ! pred() )
wait();
if pred() returns true then we still check the following line: _queue.empty()
-- that's why it's there -- and return false.
// Wait until any dispatched is done... | ||
{ | ||
std::lock_guard< std::mutex > lock( _dispatch_mutex ); | ||
assert( _queue.empty() ); |
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.
Asserts will omitted in release builds, and as release is the default for most testing the condition won't be checked
Following the deadlock that Nir experienced, reworked some stuff (and not all because of the deadlock):