output modules simplify locking for mixer reset and load

- fixes the deadlock in px4io ioctl mixer reset
   - px4io Run() locks (CDev semaphore)
   - mixer load goes through px4io ioctl MIXERIOCRESET which calls MixingOutput::resetMixerThreadSafe()
   - MixingOutput::resetMixerThreadSafe() stores a Command::Type::resetMixer command in an atomic variable, schedules a work queue cycle, then sleep spins until the command is cleared
   - the execution of the cycle eventually calls back into PX4IO::updateOutputs(), which tries to lock (and waits forever)
This commit is contained in:
Daniel Agar
2022-04-14 13:52:11 -04:00
committed by Beat Küng
parent 5d95cc001f
commit 5e05d98fe2
11 changed files with 72 additions and 172 deletions
-90
View File
@@ -331,7 +331,6 @@ bool MixingOutput::updateSubscriptionsStaticMixer(bool allow_wq_switch, bool lim
return true;
}
void MixingOutput::cleanupFunctions()
{
if (_subscription_callback) {
@@ -645,7 +644,6 @@ bool MixingOutput::update()
bool MixingOutput::updateStaticMixer()
{
if (!_mixers) {
handleCommands();
// do nothing until we have a valid mixer
return false;
}
@@ -707,7 +705,6 @@ bool MixingOutput::updateStaticMixer()
setAndPublishActuatorOutputs(num_motor_test, actuator_outputs);
}
handleCommands();
return true;
}
}
@@ -749,8 +746,6 @@ bool MixingOutput::updateStaticMixer()
updateLatencyPerfCounter(actuator_outputs);
}
handleCommands();
return true;
}
@@ -1248,88 +1243,3 @@ int MixingOutput::loadMixer(const char *buf, unsigned len)
_interface.mixerChanged();
return ret;
}
void MixingOutput::handleCommands()
{
if ((Command::Type)_command.command.load() == Command::Type::None) {
return;
}
switch ((Command::Type)_command.command.load()) {
case Command::Type::loadMixer:
_command.result = loadMixer(_command.mixer_buf, _command.mixer_buf_length);
break;
case Command::Type::resetMixer:
resetMixer();
_command.result = 0;
break;
default:
break;
}
// mark as done
_command.command.store((int)Command::Type::None);
}
void MixingOutput::resetMixerThreadSafe()
{
if (_use_dynamic_mixing) {
PX4_ERR("mixer reset unavailable, not using static mixers");
return;
}
if ((Command::Type)_command.command.load() != Command::Type::None) {
// Cannot happen, because we expect only one other thread to call this.
// But as a safety precaution we return here.
PX4_ERR("Command not None");
return;
}
lock();
_command.command.store((int)Command::Type::resetMixer);
_interface.ScheduleNow();
unlock();
// wait until processed
while ((Command::Type)_command.command.load() != Command::Type::None) {
usleep(1000);
}
}
int MixingOutput::loadMixerThreadSafe(const char *buf, unsigned len)
{
if (_use_dynamic_mixing) {
PX4_ERR("mixer load unavailable, not using static mixers");
return -1;
}
if ((Command::Type)_command.command.load() != Command::Type::None) {
// Cannot happen, because we expect only one other thread to call this.
// But as a safety precaution we return here.
PX4_ERR("Command not None");
return -1;
}
lock();
_command.mixer_buf = buf;
_command.mixer_buf_length = len;
_command.command.store((int)Command::Type::loadMixer);
_interface.ScheduleNow();
unlock();
// wait until processed
while ((Command::Type)_command.command.load() != Command::Type::None) {
usleep(1000);
}
return _command.result;
}