server: Z21 fix race condition for power state

- Read/Write track power state only from EventLoop thread
('m_trackPowerOn' and 'm_emergencyStop' must be accessed
only from EventLoop thread, except on kernel start.)

This fixes infinite async loop power on/off at startup connecting Z21
Dieser Commit ist enthalten in:
Filippo Gentile 2023-05-12 23:08:49 +02:00
Ursprung 5a291f0c91
Commit e0d5375a35
2 geänderte Dateien mit 86 neuen und 66 gelöschten Zeilen

Datei anzeigen

@ -72,48 +72,45 @@ void ClientKernel::receive(const Message& message)
case LAN_X_BC:
if(message == LanXBCTrackPowerOff() || message == LanXBCTrackShortCircuit())
{
if(m_trackPowerOn != TriState::False)
{
m_trackPowerOn = TriState::False;
if(m_onTrackPowerOnChanged)
EventLoop::call(
[this]()
{
EventLoop::call(
[this]()
{
if(m_trackPowerOn != TriState::False)
{
m_trackPowerOn = TriState::False;
if(m_onTrackPowerOnChanged)
m_onTrackPowerOnChanged(false);
});
}
}
});
}
else if(message == LanXBCTrackPowerOn())
{
if(m_trackPowerOn != TriState::True)
{
m_trackPowerOn = TriState::True;
if(m_onTrackPowerOnChanged)
EventLoop::call(
[this]()
{
EventLoop::call(
[this]()
{
if(m_trackPowerOn != TriState::True)
{
m_trackPowerOn = TriState::True;
if(m_onTrackPowerOnChanged)
m_onTrackPowerOnChanged(true);
});
}
}
});
}
break;
case LAN_X_BC_STOPPED:
if(message == LanXBCStopped())
{
if(m_emergencyStop != TriState::True)
{
m_emergencyStop = TriState::True;
if(m_onEmergencyStop)
EventLoop::call(
[this]()
{
EventLoop::call(
[this]()
{
if(m_emergencyStop != TriState::True)
{
m_emergencyStop = TriState::True;
if(m_onEmergencyStop)
m_onEmergencyStop();
});
}
}
});
}
break;
}
@ -246,29 +243,23 @@ void ClientKernel::receive(const Message& message)
&& (reply.centralState & Z21_CENTRALSTATE_SHORTCIRCUIT) == 0;
const TriState trackPowerOn = toTriState(isTrackPowerOn);
if(m_trackPowerOn != trackPowerOn)
{
m_trackPowerOn = trackPowerOn;
const TriState stopState = toTriState(isStop);
if(m_onTrackPowerOnChanged)
EventLoop::call(
[this, isTrackPowerOn]()
{
m_onTrackPowerOnChanged(isTrackPowerOn);
});
}
EventLoop::call([this, trackPowerOn, stopState]()
{
if(m_trackPowerOn != trackPowerOn)
{
m_trackPowerOn = trackPowerOn;
if(m_onTrackPowerOnChanged)
m_onTrackPowerOnChanged(trackPowerOn == TriState::True);
}
if(m_emergencyStop != TriState::True && isStop)
{
m_emergencyStop = TriState::True;
if(m_onEmergencyStop)
EventLoop::call(
[this]()
{
m_onEmergencyStop();
});
}
if(m_emergencyStop != stopState)
{
m_emergencyStop = stopState;
m_onEmergencyStop();
}
});
}
break;
}
@ -296,32 +287,44 @@ void ClientKernel::receive(const Message& message)
void ClientKernel::trackPowerOn()
{
m_ioContext.post(
[this]()
{
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
assert(isEventLoopThread());
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
{
m_ioContext.post(
[this]()
{
send(LanXSetTrackPowerOn());
});
});
}
}
void ClientKernel::trackPowerOff()
{
m_ioContext.post(
[this]()
{
if(m_trackPowerOn != TriState::False)
assert(isEventLoopThread());
if(m_trackPowerOn != TriState::False)
{
m_ioContext.post(
[this]()
{
send(LanXSetTrackPowerOff());
});
});
}
}
void ClientKernel::emergencyStop()
{
m_ioContext.post(
[this]()
{
if(m_emergencyStop != TriState::True)
assert(isEventLoopThread());
if(m_emergencyStop != TriState::True)
{
m_ioContext.post(
[this]()
{
send(LanXSetStop());
});
});
}
}
void ClientKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)

Datei anzeigen

@ -81,7 +81,24 @@ class ClientKernel final : public Kernel
uint8_t m_firmwareVersionMinor;
std::function<void(HardwareType, uint8_t, uint8_t)> m_onHardwareInfoChanged;
/*!
* \brief m_trackPowerOn caches command station track power state.
*
* NOTE: it must be accessed only from event loop thread or from
* Z21::ClientKernel::onStart().
*
* \sa EventLoop
*/
TriState m_trackPowerOn;
/*!
* \brief m_emergencyStop caches command station emergency stop state.
*
* NOTE: it must be accessed only from event loop thread or from
* Z21::ClientKernel::onStart().
*
* \sa EventLoop
*/
TriState m_emergencyStop;
std::function<void(bool)> m_onTrackPowerOnChanged;
std::function<void()> m_onEmergencyStop;