Skip to content
2 changes: 1 addition & 1 deletion Svc/ActiveRateGroup/ActiveRateGroup.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Svc {
output port RateGroupMemberOut: [ActiveRateGroupOutputPorts] Sched

@ Ping input port for health
async input port PingIn: Ping
async input port PingIn: Ping drop

@ Ping output port for health
output port PingOut: Ping
Expand Down
4 changes: 2 additions & 2 deletions Svc/BufferLogger/BufferLogger.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ module Svc {
async input port comIn: Fw.Com

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping

async input port schedIn: Svc.Sched
async input port schedIn: Svc.Sched drop

# ----------------------------------------------------------------------
# Special ports
Expand Down
4 changes: 2 additions & 2 deletions Svc/CmdDispatcher/CmdDispatcher.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ module Svc {
async input port seqCmdBuff: [CmdDispatcherSequencePorts] Fw.Com hook

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Run port used to emit telemetry
async input port run: Svc.Sched
async input port run: Svc.Sched drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
4 changes: 2 additions & 2 deletions Svc/CmdSequencer/CmdSequencer.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ module Svc {
async input port cmdResponseIn: Fw.CmdResponse

@ Ping in port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping out port
output port pingOut: Svc.Ping
Expand All @@ -86,7 +86,7 @@ module Svc {
output port comCmdOut: Fw.Com

@ Schedule in port
async input port schedIn: Svc.Sched
async input port schedIn: Svc.Sched drop

@ Notifies that a sequence has started running
output port seqStartOut: Svc.CmdSeqIn
Expand Down
17 changes: 16 additions & 1 deletion Svc/ComAggregator/ComAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

namespace Svc {

namespace {
//! Queue slots that must remain free for a timeout signal to be enqueued: the timeout itself plus one
//! in-flight 'fill' and one in-flight 'status' signal (each bounded to one message by the com protocol).
constexpr FwSizeType TIMEOUT_QUEUE_HEADROOM = 3;
} // namespace

// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------
Expand Down Expand Up @@ -55,7 +61,16 @@ void ComAggregator ::timeout_handler(FwIndexType portNum, U32 context) {
//
// Behaviorally, this solution will work exactly like the naive implementation with an infinite queue depth, but
// prevents queue overflow when using finite queues.
if (this->m_allow_timeout) {
//
// Even so, timeout remains the only signal source without flow control: 'fill' and 'status' are each bounded
// to one in-flight message by the com protocol, but the rate group keeps delivering ticks while this
// component's dispatch thread is stalled (downstream backpressure, thread starvation). In the FILL state
// (m_allow_timeout true) such a stall would still fill the queue with timeout signals and trip the queue-full
// assertion in the autocoded signal send. Ticks are periodic and idempotent, so additionally skip the signal
// unless the queue has headroom for it plus the in-flight flow-controlled signals; a skipped tick is simply
// retried on the next cycle.
if (this->m_allow_timeout &&
(this->m_queue.getMessagesAvailable() + TIMEOUT_QUEUE_HEADROOM <= this->m_queue.getDepth())) {
this->aggregationMachine_sendSignal_timeout();
}
}
Expand Down
6 changes: 6 additions & 0 deletions Svc/ComAggregator/test/ut/ComAggregatorTestMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ TEST(OffNominal, TimeoutOverflowPrevention) {
tester.test_timeout();
}

TEST(OffNominal, TimeoutOverflowPreventionFillState) {
Svc::ComAggregatorTester tester;
tester.test_initial();
tester.test_timeout_overflow_prevention_fill_state();
}

TEST(Nominal, HoldWhileWaiting) {
Svc::ComAggregatorTester tester;
tester.test_initial();
Expand Down
42 changes: 42 additions & 0 deletions Svc/ComAggregator/test/ut/ComAggregatorTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,48 @@ void ComAggregatorTester ::test_timeout_overflow_prevention() {
this->clearHistory();
}

//! Tests that a stalled dispatch thread in the FILL state cannot flood the queue with timeout
//! signals past its depth, and that the component recovers cleanly once draining resumes
void ComAggregatorTester ::test_timeout_overflow_prevention_fill_state() {
// Precondition: initial has run. The state machine is now in the FILL state
// (m_allow_timeout == true) with an empty aggregation buffer and an empty queue. Unlike
// test_timeout_overflow_prevention (which drives m_allow_timeout to false via a comStatusIn
// failure before flooding, exercising the WAIT_STATUS-adjacent gate from the pre-existing
// fix), this test floods while m_allow_timeout stays true, exercising the FILL-state gap.
ASSERT_EQ(this->component.m_queue.getMessagesAvailable(), 0);

// Simulate a stalled dispatch thread: the rate group keeps delivering 'timeout' ticks (this
// port is invoked directly here, exactly as a rate-group member call would) while nothing
// drains the component's message queue via dispatchOne. Loop well past the queue depth --
// without the FILL-state headroom check, this would keep enqueueing right up to depth and
// trip the autocoded queue-full assertion inside aggregationMachine_sendSignal_timeout.
for (FwSizeType i = 0; i < static_cast<FwSizeType>(TEST_INSTANCE_QUEUE_DEPTH) * 2; i++) {
this->invoke_to_timeout(0, 0);
// The queue must never be allowed to reach its full depth: headroom is reserved for the
// (at most one each) in-flight 'fill'/'status' signals, so FILL-state flooding cannot trip
// the queue-full assert no matter how long the simulated stall runs.
ASSERT_LT(this->component.m_queue.getMessagesAvailable(), static_cast<FwSizeType>(TEST_INSTANCE_QUEUE_DEPTH));
}
// With the aggregation buffer empty, isNotEmpty is false, so every dispatched timeout is a
// no-op transition (see test_timeout_zero); flooding has no side effects of its own.
ASSERT_from_dataOut_SIZE(0);

// Confirm recovery once the (simulated) dispatch stall clears and draining resumes: every
// queued signal is still processable without error, and the queue returns to empty.
// (dispatchOne's underlying doDispatch() issues a BLOCKING queue receive, so drain exactly the
// snapshotted number of queued messages via dispatchCurrentMessages rather than looping until
// an "empty" status that a blocking dispatch will never produce.)
ASSERT_EQ(this->dispatchCurrentMessages(this->component),
Svc::ComAggregatorComponentBase::MsgDispatchStatus::MSG_DISPATCH_OK);
ASSERT_EQ(this->component.m_queue.getMessagesAvailable(), 0);
ASSERT_from_dataOut_SIZE(0);
this->clearHistory();

// Normal fill/timeout operation resumes cleanly after the flood and drain.
this->test_fill_multi();
this->test_timeout();
}

void ComAggregatorTester ::test_timeout_zero() {
// Precondition: initialize has run
this->invoke_to_timeout(0, 0);
Expand Down
4 changes: 4 additions & 0 deletions Svc/ComAggregator/test/ut/ComAggregatorTester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class ComAggregatorTester final : public ComAggregatorGTestBase {
//! Tests timeout operation
void test_timeout_overflow_prevention();

//! Tests that a stalled dispatch thread in the FILL state cannot flood the queue with timeout
//! signals past its depth, and that the component recovers cleanly once draining resumes
void test_timeout_overflow_prevention_fill_state();

//! Tests timeout operation sends no empty buffer
void test_timeout_zero();

Expand Down
2 changes: 1 addition & 1 deletion Svc/ComLogger/ComLogger.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Svc {
async input port comIn: Fw.Com

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
2 changes: 1 addition & 1 deletion Svc/DpCatalog/DpCatalog.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module Svc {
# Component specific ports

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
2 changes: 1 addition & 1 deletion Svc/DpManager/DpManager.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Svc {
# ----------------------------------------------------------------------

@ Schedule in port
async input port schedIn: Svc.Sched
async input port schedIn: Svc.Sched drop

# ----------------------------------------------------------------------
# Ports for handling buffer requests
Expand Down
2 changes: 1 addition & 1 deletion Svc/DpWriter/DpWriter.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Svc {
# ----------------------------------------------------------------------

@ Schedule in port
async input port schedIn: Svc.Sched
async input port schedIn: Svc.Sched drop

# ----------------------------------------------------------------------
# Ports for handling data products
Expand Down
2 changes: 1 addition & 1 deletion Svc/EventManager/EventManager.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module Svc {
output port FatalAnnounce: Svc.FatalEvent

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
4 changes: 2 additions & 2 deletions Svc/FileDownlink/FileDownlink.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Svc {
# ----------------------------------------------------------------------

@ Run input port
async input port Run: Svc.Sched
async input port Run: Svc.Sched drop

@ Mutexed Sendfile input port
guarded input port SendFile: Svc.SendFileRequest
Expand All @@ -23,7 +23,7 @@ module Svc {
output port bufferSendOut: Fw.BufferSend

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
2 changes: 1 addition & 1 deletion Svc/FileManager/FileManager.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Svc {
# ----------------------------------------------------------------------

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Scheduler input port for rate group operations
sync input port schedIn: Sched
Expand Down
2 changes: 1 addition & 1 deletion Svc/FileUplink/FileUplink.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module Svc {
output port bufferSendOut: Fw.BufferSend

@ Ping in
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping out
output port pingOut: Svc.Ping
Expand Down
2 changes: 1 addition & 1 deletion Svc/FpySequencer/FpySequencer.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module Svc {

@ Ping in port
# TODO should ping have highest prio? or lowest?
async input port pingIn: Svc.Ping priority 10 assert
async input port pingIn: Svc.Ping priority 10 drop

@ port to trigger a wakeup or timeout check. increase frequency
@ to increase temporal resolution of sequencer
Expand Down
2 changes: 1 addition & 1 deletion Svc/PrmDb/PrmDb.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ module Svc {
async input port setPrm: Fw.PrmSet

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
4 changes: 2 additions & 2 deletions Svc/TlmChan/TlmChan.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ module Svc {
guarded input port TlmGet: Fw.TlmGet

@ Run port for starting packet send cycle
async input port Run: Svc.Sched
async input port Run: Svc.Sched drop

@ Packet send port
output port PktSend: Fw.Com

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping
Expand Down
6 changes: 3 additions & 3 deletions Svc/TlmPacketizer/TlmPacketizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ void TlmPacketizer::setPacketList(const TlmPacketizerPacketList& packetList,
entry.ignored = false;
entry.channelSize = packetList.list[pktEntry]->list[tlmEntry].size;
// the offset into the buffer will be the current packet length
// the offset must fit within FwSignedSizeType to allow for negative values
FW_ASSERT(packetLen <= static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max()),
// the offset must fit within I16 to allow for the -1 sentinel value
FW_ASSERT(packetLen <= static_cast<FwSizeType>(std::numeric_limits<I16>::max()),
static_cast<FwAssertArgType>(packetLen));
entry.packetOffset[pktEntry] = static_cast<FwSignedSizeType>(packetLen);
entry.packetOffset[pktEntry] = static_cast<I16>(packetLen);

packetLen += entry.channelSize;

Expand Down
4 changes: 2 additions & 2 deletions Svc/TlmPacketizer/TlmPacketizer.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ module Svc {
async input port controlIn: EnableSection

@ Ping input port
async input port pingIn: Svc.Ping
async input port pingIn: Svc.Ping drop

@ Ping output port
output port pingOut: Svc.Ping

@ Run port for starting packet send cycle
async input port Run: Svc.Sched
async input port Run: Svc.Sched drop

@ Input configuration port
async input port configureSectionGroupRate: ConfigureGroupRate
Expand Down
2 changes: 1 addition & 1 deletion Svc/TlmPacketizer/TlmPacketizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class TlmPacketizer final : public TlmPacketizerComponentBase, public Fw::ParamE
FwChanIdType id; //!< telemetry id stored in slot
// Offsets into packet buffers.
// -1 means that channel is not in that packet
FwSignedSizeType packetOffset[MAX_PACKETIZER_PACKETS];
I16 packetOffset[MAX_PACKETIZER_PACKETS];
FwSizeType channelSize; //!< max serialized size of the channel in bytes
bool ignored; //!< ignored channel id
bool hasValue; //!< if the entry has received a value at least once
Expand Down
Loading