Skip to content
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

[CP][1.4] Fix MRP baseTimeout calculation for LIT (#37696) #38062

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app/CommandSender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ CHIP_ERROR CommandSender::SendCommandRequestInternal(const SessionHandle & sessi
mExchangeCtx.Grab(exchange);
VerifyOrReturnError(!mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);

mExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
mExchangeCtx->SetResponseTimeout(
timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime, true /*isFirstMessageOnExchange*/)));

if (mTimedInvokeTimeoutMs.HasValue())
{
Expand Down
11 changes: 7 additions & 4 deletions src/app/ReadClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -987,12 +987,15 @@ CHIP_ERROR ReadClient::ComputeLivenessCheckTimerTimeout(System::Clock::Timeout *
// So recompute the round-trip timeout directly. Assume MRP, since in practice that is likely what is happening.
auto & peerMRPConfig = mReadPrepareParams.mSessionHolder->GetRemoteMRPConfig();
// Peer will assume we are idle (hence we pass kZero to GetMessageReceiptTimeout()), but will assume we treat it as active
// for the response, so to match the retransmission timeout computation for the message back to the peeer, we should treat
// it as active.
auto roundTripTimeout = mReadPrepareParams.mSessionHolder->GetMessageReceiptTimeout(System::Clock::kZero) +
// for the response, so to match the retransmission timeout computation for the message back to the peer, we should treat
// it as active and handling non-initial message, isFirstMessageOnExchange needs to be set as false for
// GetRetransmissionTimeout.
auto roundTripTimeout =
mReadPrepareParams.mSessionHolder->GetMessageReceiptTimeout(System::Clock::kZero, true /*isFirstMessageOnExchange*/) +
kExpectedIMProcessingTime +
GetRetransmissionTimeout(peerMRPConfig.mActiveRetransTimeout, peerMRPConfig.mIdleRetransTimeout,
System::SystemClock().GetMonotonicTimestamp(), peerMRPConfig.mActiveThresholdTime);
System::SystemClock().GetMonotonicTimestamp(), peerMRPConfig.mActiveThresholdTime,
false /*isFirstMessageOnExchange*/);
*aTimeout = System::Clock::Seconds16(mMaxInterval) + roundTripTimeout;
return CHIP_NO_ERROR;
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/TimedHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ CHIP_ERROR TimedHandler::HandleTimedRequestAction(Messaging::ExchangeContext * a
// will send nothing and the other side will have to time out to realize
// it's missed its window).
auto delay = System::Clock::Milliseconds32(timeoutMs);
aExchangeContext->SetResponseTimeout(
std::max(delay, aExchangeContext->GetSessionHandle()->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
aExchangeContext->SetResponseTimeout(std::max(delay,
aExchangeContext->GetSessionHandle()->ComputeRoundTripTimeout(
app::kExpectedIMProcessingTime, false /*isFirstMessageOnExchange*/)));
ReturnErrorOnFailure(StatusResponse::Send(Status::Success, aExchangeContext, /* aExpectResponse = */ true));

// Now just wait for the client.
Expand Down
2 changes: 1 addition & 1 deletion src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ Optional<System::Clock::Timeout> AutoCommissioner::GetCommandTimeout(DeviceProxy
auto sessionHandle = device->GetSecureSession();
if (sessionHandle.HasValue())
{
timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout);
timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout, true /*isFirstMessageOnExchange*/);
}

// Enforce the spec minimal timeout. Maybe this enforcement should live in
Expand Down
2 changes: 1 addition & 1 deletion src/controller/python/chip/utils/DeviceProxyUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ uint32_t pychip_DeviceProxy_ComputeRoundTripTimeout(DeviceProxy * device, uint32

return deviceProxy->GetSecureSession()
.Value()
->ComputeRoundTripTimeout(System::Clock::Milliseconds32(upperLayerProcessingTimeoutMs))
->ComputeRoundTripTimeout(System::Clock::Milliseconds32(upperLayerProcessingTimeoutMs), true /*isFirstMessageOnExchange*/)
.count();
}

Expand Down
7 changes: 4 additions & 3 deletions src/controller/tests/TestWriteChunking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,10 @@ void TestWriteChunking::RunTest(Instructions instructions)
err = writeClient->SendWriteRequest(sessionHandle);
EXPECT_EQ(err, CHIP_NO_ERROR);

GetIOContext().DriveIOUntil(sessionHandle->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime) +
System::Clock::Seconds16(1),
[&]() { return GetExchangeManager().GetNumActiveExchanges() == 0; });
GetIOContext().DriveIOUntil(
sessionHandle->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime, true /*isFirstMessageOnExchange*/) +
System::Clock::Seconds16(1),
[&]() { return GetExchangeManager().GetNumActiveExchanges() == 0; });

EXPECT_EQ(onGoingPath, app::ConcreteAttributePath());
EXPECT_EQ(status.size(), instructions.expectedStatus.size());
Expand Down
8 changes: 4 additions & 4 deletions src/controller/tests/data_model/TestRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4783,10 +4783,10 @@ System::Clock::Timeout TestRead::ComputeSubscriptionTimeout(System::Clock::Secon
// Add 1000ms of slack to our max interval to make sure we hit the
// subscription liveness timer. 100ms was tried in the past and is not
// sufficient: our process can easily lose the timeslice for 100ms.
const auto & ourMrpConfig = GetDefaultMRPConfig();
auto publisherTransmissionTimeout =
GetRetransmissionTimeout(ourMrpConfig.mActiveRetransTimeout, ourMrpConfig.mIdleRetransTimeout,
System::SystemClock().GetMonotonicTimestamp(), ourMrpConfig.mActiveThresholdTime);
const auto & ourMrpConfig = GetDefaultMRPConfig();
auto publisherTransmissionTimeout = GetRetransmissionTimeout(
ourMrpConfig.mActiveRetransTimeout, ourMrpConfig.mIdleRetransTimeout, System::SystemClock().GetMonotonicTimestamp(),
ourMrpConfig.mActiveThresholdTime, true /* isFirstMessageOnExchange */);

return publisherTransmissionTimeout + aMaxInterval + System::Clock::Milliseconds32(1000);
}
Expand Down
2 changes: 1 addition & 1 deletion src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,7 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
// Clamp to a number of seconds that will not overflow 32-bit
// int when converted to ms.
auto serverTimeoutInSeconds = System::Clock::Seconds16(serverSideProcessingTimeout.unsignedShortValue);
invokeTimeout.SetValue(session->ComputeRoundTripTimeout(serverTimeoutInSeconds));
invokeTimeout.SetValue(session->ComputeRoundTripTimeout(serverTimeoutInSeconds, true /*isFirstMessageOnExchange*/));
}
ReturnErrorOnFailure(commandSender->SendCommandRequest(session, invokeTimeout));

Expand Down
2 changes: 1 addition & 1 deletion src/messaging/ExchangeContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void ExchangeContext::SetResponseExpected(bool inResponseExpected)

void ExchangeContext::UseSuggestedResponseTimeout(Timeout applicationProcessingTimeout)
{
SetResponseTimeout(mSession->ComputeRoundTripTimeout(applicationProcessingTimeout));
SetResponseTimeout(mSession->ComputeRoundTripTimeout(applicationProcessingTimeout, !HasReceivedAtLeastOneMessage()));
}

void ExchangeContext::SetResponseTimeout(Timeout timeout)
Expand Down
12 changes: 10 additions & 2 deletions src/messaging/ReliableMessageProtocolConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ Optional<ReliableMessageProtocolConfig> GetLocalMRPConfig()
}

System::Clock::Timeout GetRetransmissionTimeout(System::Clock::Timeout activeInterval, System::Clock::Timeout idleInterval,
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold)
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold,
bool isFirstMessageOnExchange)
{
auto timeSinceLastActivity = (System::SystemClock().GetMonotonicTimestamp() - lastActivityTime);

Expand All @@ -135,7 +136,14 @@ System::Clock::Timeout GetRetransmissionTimeout(System::Clock::Timeout activeInt
System::Clock::Timestamp timeout(0);
for (uint8_t i = 0; i < CHIP_CONFIG_RMP_DEFAULT_MAX_RETRANS + 1; i++)
{
auto baseInterval = ((timeSinceLastActivity + timeout) < activityThreshold) ? activeInterval : idleInterval;
auto baseInterval = activeInterval;
// If we are calculating the timeout for the initial message, we never know whether the peer is active or not, choose
// active/idle interval from PeerActiveMode of session per 4.11.2.1. Retransmissions.
// If we are calculating the timeout for response message, we know the peer is active, always choose active interval.
if (isFirstMessageOnExchange)
{
baseInterval = ((timeSinceLastActivity + timeout) < activityThreshold) ? activeInterval : idleInterval;
}
timeout += Messaging::ReliableMessageMgr::GetBackoff(baseInterval, i, /* computeMaxPossible */ true);
}

Expand Down
5 changes: 3 additions & 2 deletions src/messaging/ReliableMessageProtocolConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,12 @@ Optional<ReliableMessageProtocolConfig> GetLocalMRPConfig();
* @param[in] idleInterval The idle interval to use for the backoff calculation.
* @param[in] lastActivityTime The last time some activity has been recorded.
* @param[in] activityThreshold The activity threshold for a node to be considered active.
*
* @param[in] isFirstMessageOnExchange Indicates whether this is for the initial message on an exchange.
* @return The maximum transmission time
*/
System::Clock::Timeout GetRetransmissionTimeout(System::Clock::Timeout activeInterval, System::Clock::Timeout idleInterval,
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold);
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold,
bool isFirstMessageOnExchange);

#if CONFIG_BUILD_FOR_HOST_UNIT_TEST

Expand Down
23 changes: 13 additions & 10 deletions src/protocols/secure_channel/CASESession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2418,34 +2418,37 @@ CHIP_ERROR CASESession::OnMessageReceived(ExchangeContext * ec, const PayloadHea

namespace {
System::Clock::Timeout ComputeRoundTripTimeout(ExchangeContext::Timeout serverProcessingTime,
const ReliableMessageProtocolConfig & remoteMrpConfig)
const ReliableMessageProtocolConfig & remoteMrpConfig, bool isFirstMessageOnExchange)
{
// TODO: This is duplicating logic from Session::ComputeRoundTripTimeout. Unfortunately, it's called by
// consumers who do not have a session.
const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
const auto & defaultMRRPConfig = GetDefaultMRPConfig();
const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
return GetRetransmissionTimeout(remoteMrpConfig.mActiveRetransTimeout, remoteMrpConfig.mIdleRetransTimeout,
// Assume peer is idle, as a worst-case assumption (probably true for
// Sigma1, since that will be our initial message on the session, but less
// so for Sigma2).
System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime) +
// The activity time only matters when isFirstMessageOnExchange is false,
// which only happens for Sigma1. In that case, assume peer is idle,
// as a worst-case assumption, and pass System::Clock::kZero.
System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime, isFirstMessageOnExchange) +
serverProcessingTime +
GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
// Peer will assume we are active, since it's
// responding to our message.
System::SystemClock().GetMonotonicTimestamp(), localMRPConfig.mActiveThresholdTime);
// Peer will be responding to our message, so isFirstMessageOnExchange should be false
// and the timestamp does not matter.
System::SystemClock().GetMonotonicTimestamp(), localMRPConfig.mActiveThresholdTime,
false /*isFirstMessageOnExchange*/);
}
} // anonymous namespace

System::Clock::Timeout CASESession::ComputeSigma1ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig)
{
return ComputeRoundTripTimeout(kExpectedSigma1ProcessingTime, remoteMrpConfig);
// Sigma1 is the first message on the exchange.
return ComputeRoundTripTimeout(kExpectedSigma1ProcessingTime, remoteMrpConfig, true /*isFirstMessageOnExchange*/);
}

System::Clock::Timeout CASESession::ComputeSigma2ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig)
{
return ComputeRoundTripTimeout(kExpectedHighProcessingTime, remoteMrpConfig);
// Sigma2 is never the first message on the exchange.
return ComputeRoundTripTimeout(kExpectedHighProcessingTime, remoteMrpConfig, false /*isFirstMessageOnExchange*/);
}

bool CASESession::InvokeBackgroundWorkWatchdog()
Expand Down
10 changes: 6 additions & 4 deletions src/transport/GroupSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,14 @@ class IncomingGroupSession : public Session, public ReferenceCounted<IncomingGro

System::Clock::Timestamp GetMRPBaseTimeout() const override { return System::Clock::kZero; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
VerifyOrDie(false);
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
// There are no timeouts for group sessions.
VerifyOrDie(false);
Expand Down Expand Up @@ -128,13 +129,14 @@ class OutgoingGroupSession : public Session, public ReferenceCounted<OutgoingGro

System::Clock::Timestamp GetMRPBaseTimeout() const override { return System::Clock::kZero; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
VerifyOrDie(false);
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
// There are no timeouts for group sessions.
VerifyOrDie(false);
Expand Down
10 changes: 6 additions & 4 deletions src/transport/SecureSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,15 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec

bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
switch (mPeerAddress.GetTransportType())
{
case Transport::Type::kUdp: {
const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime);
GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
isFirstMessageOnExchange);
}
case Transport::Type::kTcp:
return System::Clock::Seconds16(30);
Expand All @@ -181,7 +182,8 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
switch (mPeerAddress.GetTransportType())
{
Expand All @@ -190,7 +192,7 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec
const auto & defaultMRRPConfig = GetDefaultMRPConfig();
const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
ourLastActivity, localMRPConfig.mActiveThresholdTime);
ourLastActivity, localMRPConfig.mActiveThresholdTime, isFirstMessageOnExchange);
}
case Transport::Type::kTcp:
return System::Clock::Seconds16(30);
Expand Down
10 changes: 6 additions & 4 deletions src/transport/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,18 @@ const OutgoingGroupSession * Session::AsConstOutgoingGroupSession() const
return static_cast<const OutgoingGroupSession *>(this);
}

System::Clock::Timeout Session::ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout)
System::Clock::Timeout Session::ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout,
bool isFirstMessageOnExchange)
{
if (IsGroupSession())
{
return System::Clock::kZero;
}

// Treat us as active for purposes of GetMessageReceiptTimeout(), since the
// other side would be responding to our message.
return GetAckTimeout() + upperlayerProcessingTimeout + GetMessageReceiptTimeout(System::SystemClock().GetMonotonicTimestamp());
// Treat us as active for purposes of GetMessageReceiptTimeout(), pass false into GetMessageReceiptTimeout to
// indicate we are processing non-initial message since the other side would be responding to our message.
return GetAckTimeout(isFirstMessageOnExchange) + upperlayerProcessingTimeout +
GetMessageReceiptTimeout(System::SystemClock().GetMonotonicTimestamp(), false /*isFirstMessageOnExchange*/);
}

uint16_t Session::SessionIdForLogging() const
Expand Down
Loading
Loading