-
Notifications
You must be signed in to change notification settings - Fork 80
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
[WIP] fix deadlock in concurrent connection disconnection #302
Changes from all commits
bb6fbc1
9e2cce0
066ecc5
6b8fee0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -272,45 +272,59 @@ void MultipleInputsChannelElementBase::clear() | |
bool MultipleInputsChannelElementBase::disconnect(ChannelElementBase::shared_ptr const& channel, bool forward) | ||
{ | ||
if (channel) { | ||
bool was_last = false; | ||
return disconnectSingleInputChannel(channel, forward); | ||
} else if (!forward) { | ||
Inputs removedInputs; | ||
{ | ||
// Remove the channel from the inputs list | ||
RTT::os::MutexLock lock(inputs_lock); | ||
Inputs::iterator found = std::find(inputs.begin(), inputs.end(), channel); | ||
if (found == inputs.end()) { | ||
return false; | ||
} | ||
ChannelElementBase::shared_ptr input = *found; | ||
|
||
if (!forward) { | ||
if (!input->disconnect(this, forward)) { | ||
return false; | ||
} | ||
} | ||
|
||
removeInput(input.get()); // invalidates input | ||
was_last = inputs.empty(); | ||
removedInputs.splice(removedInputs.end(), inputs); | ||
this->removedInputs(removedInputs); | ||
} | ||
|
||
// If the removed input was the last channel and forward is true, disconnect output side, too. | ||
if (was_last && forward) { | ||
return disconnect(0, true); | ||
for (Inputs::iterator it = removedInputs.begin(); it != removedInputs.end(); ++it) { | ||
(*it)->disconnect(this, false); | ||
} | ||
} | ||
|
||
return true; | ||
return ChannelElementBase::disconnect(channel, forward); | ||
} | ||
|
||
} else if (!forward) { | ||
// Disconnect and remove all inputs | ||
bool MultipleInputsChannelElementBase::disconnectSingleInputChannel(ChannelElementBase::shared_ptr const& channel, bool forward) | ||
{ | ||
// Remove the channel from the outputs list | ||
bool was_last = false; | ||
|
||
// Must remove first the item from the output list before we attempt the | ||
// disconnection, or another thread might try to remove it as well | ||
Inputs removedInputs; | ||
{ | ||
RTT::os::MutexLock lock(inputs_lock); | ||
for (Inputs::iterator it = inputs.begin(); it != inputs.end(); ) { | ||
const ChannelElementBase::shared_ptr &input = *it++; | ||
input->disconnect(this, false); | ||
removeInput(input.get()); // invalidates input | ||
Inputs::const_iterator found = std::find(inputs.begin(), inputs.end(), channel); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently the iterator needs to be non-const for std::list::splice for compatibility with C++98. |
||
if (found == inputs.end()) | ||
return false; | ||
|
||
removedInputs.splice(removedInputs.end(), inputs, found); | ||
this->removedInputs(removedInputs); | ||
was_last = inputs.empty(); | ||
} | ||
|
||
if (!forward) { | ||
if (!channel->disconnect(this, false)) { | ||
{ | ||
// Disconnection failed, re-add to the output list | ||
RTT::os::MutexLock lock(inputs_lock); | ||
inputs.splice(inputs.end(), removedInputs); | ||
} | ||
return false; | ||
} | ||
assert(inputs.empty()); | ||
} | ||
|
||
return ChannelElementBase::disconnect(channel, forward); | ||
// If the removed output was the last channel, disconnect input side, too. | ||
if (was_last && forward) { | ||
return disconnect(0, true); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool MultipleInputsChannelElementBase::signalFrom(ChannelElementBase *) | ||
|
@@ -374,58 +388,75 @@ bool MultipleOutputsChannelElementBase::channelReady(ChannelElementBase::shared_ | |
bool MultipleOutputsChannelElementBase::disconnect(ChannelElementBase::shared_ptr const& channel, bool forward) | ||
{ | ||
if (channel) { | ||
// Remove the channel from the outputs list | ||
bool was_last = false; | ||
return disconnectSingleOutputChannel(channel, forward); | ||
} | ||
|
||
if (forward) { | ||
// Disconnect and remove all outputs | ||
Outputs outputs; | ||
{ | ||
RTT::os::MutexLock lock(outputs_lock); | ||
Outputs::iterator found = std::find(outputs.begin(), outputs.end(), channel); | ||
if (found == outputs.end()) { | ||
return false; | ||
} | ||
const Output &output = *found; | ||
outputs.splice(outputs.end(), this->outputs); | ||
} | ||
for (Outputs::iterator it = outputs.begin(); it != outputs.end(); ++it) { | ||
it->channel->disconnect(this, true); | ||
} | ||
} | ||
|
||
if (forward) { | ||
if (!output.channel->disconnect(this, forward)) { | ||
return false; | ||
} | ||
} | ||
return ChannelElementBase::disconnect(channel, forward); | ||
} | ||
|
||
removeOutput(output.channel.get()); // invalidates output | ||
was_last = outputs.empty(); | ||
} | ||
bool MultipleOutputsChannelElementBase::disconnectSingleOutputChannel(ChannelElementBase::shared_ptr const& channel, bool forward) | ||
{ | ||
// Remove the channel from the outputs list | ||
bool was_last = false; | ||
|
||
// If the removed output was the last channel, disconnect input side, too. | ||
if (was_last && !forward) { | ||
return disconnect(0, false); | ||
} | ||
// Must remove first the item from the output list before we attempt the | ||
// disconnection, or another thread might try to remove it as well | ||
Outputs removedOutput; | ||
{ | ||
RTT::os::MutexLock lock(outputs_lock); | ||
Outputs::const_iterator found = std::find(outputs.begin(), outputs.end(), channel); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently the iterator needs to be non-const for std::list::splice for compatibility with C++98. |
||
if (found == outputs.end()) | ||
return false; | ||
|
||
return true; | ||
removedOutput.splice(removedOutput.end(), outputs, found); | ||
was_last = outputs.empty(); | ||
} | ||
|
||
if (forward) { | ||
// Disconnect and remove all outputs | ||
RTT::os::MutexLock lock(outputs_lock); | ||
for (Outputs::iterator it = outputs.begin(); it != outputs.end(); ) { | ||
const Output &output = *it++; | ||
output.channel->disconnect(this, true); | ||
removeOutput(output.channel.get()); // invalidates output | ||
if (!channel->disconnect(this, true)) { | ||
{ | ||
// Disconnection failed, re-add to the output list | ||
RTT::os::MutexLock lock(outputs_lock); | ||
outputs.splice(outputs.end(), removedOutput); | ||
} | ||
return false; | ||
} | ||
assert(outputs.empty()); | ||
} | ||
|
||
return ChannelElementBase::disconnect(channel, forward); | ||
// If the removed output was the last channel, disconnect input side, too. | ||
if (was_last && !forward) { | ||
return disconnect(0, false); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void MultipleOutputsChannelElementBase::removeDisconnectedOutputs() | ||
{ | ||
RTT::os::MutexLock lock(outputs_lock); | ||
for (Outputs::iterator it = outputs.begin(); it != outputs.end(); ) { | ||
const Output &output = *it++; | ||
if (output.disconnected) { | ||
output.channel->disconnect(this, true); | ||
removeOutput(output.channel.get()); // invalidates output | ||
Outputs disconnectedOutputs; | ||
{ | ||
RTT::os::MutexLock lock(outputs_lock); | ||
for (Outputs::iterator it = outputs.begin(); it != outputs.end(); ++it) { | ||
if (it->disconnected) | ||
disconnectedOutputs.splice(disconnectedOutputs.end(), this->outputs, it); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This loop is invalid because removing element |
||
} | ||
} | ||
|
||
for (Outputs::iterator it = disconnectedOutputs.begin(); it != disconnectedOutputs.end(); ++it) { | ||
it->channel->disconnect(this, true); | ||
} | ||
} | ||
|
||
bool MultipleInputsMultipleOutputsChannelElementBase::connected() | ||
|
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.
missing namespace qualifier:
std::find
?