2 * Copyright (c) 2020-2021 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * This file implements the CHIP reliable message protocol.
26 #include <messaging/ReliableMessageMgr.h>
28 #include <messaging/ErrorCategory.h>
29 #include <messaging/Flags.h>
30 #include <messaging/ReliableMessageContext.h>
31 #include <support/BitFlags.h>
32 #include <support/CHIPFaultInjection.h>
33 #include <support/CodeUtils.h>
34 #include <support/logging/CHIPLogging.h>
39 ReliableMessageMgr::RetransTableEntry::RetransTableEntry() : rc(nullptr), nextRetransTimeTick(0), sendCount(0) {}
41 ReliableMessageMgr::ReliableMessageMgr(std::array<ExchangeContext, CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS> & contextPool) :
42 mContextPool(contextPool), mSystemLayer(nullptr), mSessionMgr(nullptr), mCurrentTimerExpiry(0),
43 mTimerIntervalShift(CHIP_CONFIG_RMP_TIMER_DEFAULT_PERIOD_SHIFT)
46 ReliableMessageMgr::~ReliableMessageMgr() {}
48 void ReliableMessageMgr::Init(chip::System::Layer * systemLayer, SecureSessionMgr * sessionMgr)
50 mSystemLayer = systemLayer;
51 mSessionMgr = sessionMgr;
53 mTimeStampBase = System::Timer::GetCurrentEpoch();
54 mCurrentTimerExpiry = 0;
57 void ReliableMessageMgr::Shutdown()
61 mSystemLayer = nullptr;
62 mSessionMgr = nullptr;
64 // Clear the retransmit table
65 for (RetransTableEntry & rEntry : mRetransTable)
67 ClearRetransTable(rEntry);
71 uint64_t ReliableMessageMgr::GetTickCounterFromTimePeriod(uint64_t period)
73 return (period >> mTimerIntervalShift);
76 uint64_t ReliableMessageMgr::GetTickCounterFromTimeDelta(uint64_t newTime)
78 return GetTickCounterFromTimePeriod(newTime - mTimeStampBase);
81 #if defined(RMP_TICKLESS_DEBUG)
82 void ReliableMessageMgr::TicklessDebugDumpRetransTable(const char * log)
84 ChipLogProgress(ExchangeManager, log);
86 for (RetransTableEntry & entry : mRetransTable)
90 ChipLogProgress(ExchangeManager, "EC:%04" PRIX16 " MsgId:%08" PRIX32 " NextRetransTimeCtr:%04" PRIX16, entry.rc,
91 entry.msgId, entry.nextRetransTimeTick);
96 void ReliableMessageMgr::TicklessDebugDumpRetransTable(const char * log)
100 #endif // RMP_TICKLESS_DEBUG
102 void ReliableMessageMgr::ExecuteActions()
104 #if defined(RMP_TICKLESS_DEBUG)
105 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::ExecuteActions");
108 ExecuteForAllContext([](ReliableMessageContext * rc) {
109 if (rc->IsAckPending())
111 if (0 == rc->mNextAckTimeTick)
113 #if defined(RMP_TICKLESS_DEBUG)
114 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::ExecuteActions sending ACK");
116 // Send the Ack in a SecureChannel::StandaloneAck message
117 rc->SendStandaloneAckMessage();
118 rc->SetAckPending(false);
123 TicklessDebugDumpRetransTable("ReliableMessageMgr::ExecuteActions Dumping mRetransTable entries before processing");
125 // Retransmit / cancel anything in the retrans table whose retrans timeout
127 for (RetransTableEntry & entry : mRetransTable)
129 ReliableMessageContext * rc = entry.rc;
130 CHIP_ERROR err = CHIP_NO_ERROR;
132 if (!rc || entry.nextRetransTimeTick != 0)
135 uint8_t sendCount = entry.sendCount;
137 if (sendCount == rc->mConfig.mMaxRetrans)
139 err = CHIP_ERROR_MESSAGE_NOT_ACKNOWLEDGED;
141 ChipLogError(ExchangeManager, "Failed to Send CHIP MsgId:%08" PRIX32 " sendCount: %" PRIu8 " max retries: %" PRIu8,
142 entry.retainedBuf.GetMsgId(), sendCount, rc->mConfig.mMaxRetrans);
145 ClearRetransTable(entry);
148 // Resend from Table (if the operation fails, the entry is cleared)
149 if (err == CHIP_NO_ERROR)
150 err = SendFromRetransTable(&entry);
152 if (err == CHIP_NO_ERROR)
154 // If the retransmission was successful, update the passive timer
155 entry.nextRetransTimeTick = static_cast<uint16_t>(rc->GetCurrentRetransmitTimeoutTick());
157 ChipLogProgress(ExchangeManager, "Retransmit MsgId:%08" PRIX32 " Send Cnt %d", entry.retainedBuf.GetMsgId(),
162 if (err != CHIP_NO_ERROR && rc->mDelegate)
163 rc->mDelegate->OnSendError(err);
166 TicklessDebugDumpRetransTable("ReliableMessageMgr::ExecuteActions Dumping mRetransTable entries after processing");
169 static void TickProceed(uint16_t & time, uint64_t ticks)
173 time = static_cast<uint16_t>(time - ticks);
181 void ReliableMessageMgr::ExpireTicks()
183 uint64_t now = System::Timer::GetCurrentEpoch();
185 // Number of full ticks elapsed since last timer processing. We always round down
186 // to the previous tick. If we are between tick boundaries, the extra time since the
187 // last virtual tick is not accounted for here (it will be accounted for when resetting
188 // the ReliableMessageProtocol timer)
189 uint64_t deltaTicks = GetTickCounterFromTimeDelta(now);
191 #if defined(RMP_TICKLESS_DEBUG)
192 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::ExpireTicks at %" PRIu64 ", %" PRIu64 ", %u", now, mTimeStampBase,
196 ExecuteForAllContext([deltaTicks](ReliableMessageContext * rc) {
197 if (rc->IsAckPending())
199 // Decrement counter of Ack timestamp by the elapsed timer ticks
200 TickProceed(rc->mNextAckTimeTick, deltaTicks);
201 #if defined(RMP_TICKLESS_DEBUG)
202 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::ExpireTicks set mNextAckTimeTick to %u", rc->mNextAckTimeTick);
207 for (RetransTableEntry & entry : mRetransTable)
209 ReliableMessageContext * rc = entry.rc;
212 // Decrement Retransmit timeout by elapsed timeticks
213 TickProceed(entry.nextRetransTimeTick, deltaTicks);
214 #if defined(RMP_TICKLESS_DEBUG)
215 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::ExpireTicks set nextRetransTimeTick to %u",
216 entry.nextRetransTimeTick);
218 } // rc entry is allocated
221 // Re-Adjust the base time stamp to the most recent tick boundary
222 mTimeStampBase += (deltaTicks << mTimerIntervalShift);
224 #if defined(RMP_TICKLESS_DEBUG)
225 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::ExpireTicks mTimeStampBase to %" PRIu64, mTimeStampBase);
229 void ReliableMessageMgr::Timeout(System::Layer * aSystemLayer, void * aAppState, System::Error aError)
231 ReliableMessageMgr * manager = reinterpret_cast<ReliableMessageMgr *>(aAppState);
233 VerifyOrDie((aSystemLayer != nullptr) && (manager != nullptr));
235 #if defined(RMP_TICKLESS_DEBUG)
236 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::Timeout\n");
239 // Make sure all tick counts are sync'd to the current time
240 manager->ExpireTicks();
242 // Execute any actions that are due this tick
243 manager->ExecuteActions();
245 // Calculate next physical wakeup
246 manager->StartTimer();
249 CHIP_ERROR ReliableMessageMgr::AddToRetransTable(ReliableMessageContext * rc, RetransTableEntry ** rEntry)
252 CHIP_ERROR err = CHIP_NO_ERROR;
254 VerifyOrDie(rc != nullptr && rc->mExchange != nullptr);
256 for (RetransTableEntry & entry : mRetransTable)
258 // Check the exchContext pointer for finding an empty slot in Table
261 // Expire any virtual ticks that have expired so all wakeup sources reflect the current time
266 entry.retainedBuf = EncryptedPacketBufferHandle();
270 // Increment the reference count
280 ChipLogError(ExchangeManager, "mRetransTable Already Full");
281 err = CHIP_ERROR_RETRANS_TABLE_FULL;
287 void ReliableMessageMgr::StartRetransmision(RetransTableEntry * entry)
289 VerifyOrDie(entry != nullptr && entry->rc != nullptr);
291 entry->nextRetransTimeTick = static_cast<uint16_t>(entry->rc->GetCurrentRetransmitTimeoutTick() +
292 GetTickCounterFromTimeDelta(System::Timer::GetCurrentEpoch()));
294 // Check if the timer needs to be started and start it.
298 void ReliableMessageMgr::PauseRetransmision(ReliableMessageContext * rc, uint32_t PauseTimeMillis)
300 for (RetransTableEntry & entry : mRetransTable)
304 entry.nextRetransTimeTick = static_cast<uint16_t>(entry.nextRetransTimeTick + (PauseTimeMillis >> mTimerIntervalShift));
310 void ReliableMessageMgr::ResumeRetransmision(ReliableMessageContext * rc)
312 for (RetransTableEntry & entry : mRetransTable)
316 entry.nextRetransTimeTick = 0;
322 bool ReliableMessageMgr::CheckAndRemRetransTable(ReliableMessageContext * rc, uint32_t ackMsgId)
324 for (RetransTableEntry & entry : mRetransTable)
326 if ((entry.rc == rc) && entry.retainedBuf.GetMsgId() == ackMsgId)
328 // Clear the entry from the retransmision table.
329 ClearRetransTable(entry);
332 ChipLogProgress(ExchangeManager, "Rxd Ack; Removing MsgId:%08" PRIX32 " from Retrans Table", ackMsgId);
341 CHIP_ERROR ReliableMessageMgr::SendFromRetransTable(RetransTableEntry * entry)
343 CHIP_ERROR err = CHIP_NO_ERROR;
344 ReliableMessageContext * rc = entry->rc;
348 err = mSessionMgr->SendEncryptedMessage(entry->rc->mExchange->GetSecureSession(), std::move(entry->retainedBuf),
349 &entry->retainedBuf);
351 if (err == CHIP_NO_ERROR)
353 // Update the counters
359 ChipLogError(ExchangeManager, "Crit-err %ld when sending CHIP MsgId:%08" PRIX32 ", send tries: %d", long(err),
360 entry->retainedBuf.GetMsgId(), entry->sendCount);
362 ClearRetransTable(*entry);
367 ChipLogError(ExchangeManager, "Table entry invalid");
373 void ReliableMessageMgr::ClearRetransTable(ReliableMessageContext * rc)
375 for (RetransTableEntry & entry : mRetransTable)
379 // Clear the retransmit table entry.
380 ClearRetransTable(entry);
385 void ReliableMessageMgr::ClearRetransTable(RetransTableEntry & rEntry)
389 VerifyOrDie(rEntry.rc->mExchange != nullptr);
391 // Expire any virtual ticks that have expired so all wakeup sources reflect the current time
394 rEntry.rc->Release();
397 // Clear all other fields
398 rEntry = RetransTableEntry();
400 // Schedule next physical wakeup, unless shutting down
406 void ReliableMessageMgr::FailRetransTableEntries(ReliableMessageContext * rc, CHIP_ERROR err)
408 for (RetransTableEntry & entry : mRetransTable)
412 // Remove the entry from the retransmission table.
413 ClearRetransTable(entry);
415 // Application callback OnSendError.
416 rc->mDelegate->OnSendError(err);
421 void ReliableMessageMgr::StartTimer()
423 CHIP_ERROR res = CHIP_NO_ERROR;
424 uint64_t nextWakeTimeTick = UINT64_MAX;
425 bool foundWake = false;
427 // When do we need to next wake up to send an ACK?
429 ExecuteForAllContext([&nextWakeTimeTick, &foundWake](ReliableMessageContext * rc) {
430 if (rc->IsAckPending() && rc->mNextAckTimeTick < nextWakeTimeTick)
432 nextWakeTimeTick = rc->mNextAckTimeTick;
434 #if defined(RMP_TICKLESS_DEBUG)
435 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::StartTimer next ACK time %u", nextWakeTimeTick);
440 for (RetransTableEntry & entry : mRetransTable)
442 ReliableMessageContext * rc = entry.rc;
445 // When do we need to next wake up for ReliableMessageProtocol retransmit?
446 if (entry.nextRetransTimeTick < nextWakeTimeTick)
448 nextWakeTimeTick = entry.nextRetransTimeTick;
450 #if defined(RMP_TICKLESS_DEBUG)
451 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::StartTimer RetransTime %u", nextWakeTimeTick);
459 // Set timer for next tick boundary - subtract the elapsed time from the current tick
460 System::Timer::Epoch timerExpiryEpoch = (nextWakeTimeTick << mTimerIntervalShift) + mTimeStampBase;
462 #if defined(RMP_TICKLESS_DEBUG)
463 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::StartTimer wake at %" PRIu64 " ms (%" PRIu64 " %" PRIu64 ")",
464 timerExpiryEpoch, nextWakeTimeTick, mTimeStampBase);
466 if (timerExpiryEpoch != mCurrentTimerExpiry)
468 // If the tick boundary has expired in the past (delayed processing of event due to other system activity),
469 // expire the timer immediately
470 uint64_t now = System::Timer::GetCurrentEpoch();
471 uint64_t timerArmValue = (timerExpiryEpoch > now) ? timerExpiryEpoch - now : 0;
473 #if defined(RMP_TICKLESS_DEBUG)
474 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::StartTimer set timer for %" PRIu64, timerArmValue);
477 res = mSystemLayer->StartTimer((uint32_t) timerArmValue, Timeout, this);
479 VerifyOrDieWithMsg(res == CHIP_NO_ERROR, ExchangeManager, "Cannot start ReliableMessageMgr::Timeout\n");
480 mCurrentTimerExpiry = timerExpiryEpoch;
481 #if defined(RMP_TICKLESS_DEBUG)
485 ChipLogProgress(ExchangeManager, "ReliableMessageMgr::StartTimer timer already set for %" PRIu64, timerExpiryEpoch);
491 #if defined(RMP_TICKLESS_DEBUG)
492 ChipLogProgress(ExchangeManager, "Not setting ReliableMessageProtocol timeout at %" PRIu64,
493 System::Timer::GetCurrentEpoch());
498 TicklessDebugDumpRetransTable("ReliableMessageMgr::StartTimer Dumping mRetransTable entries after setting wakeup times");
501 void ReliableMessageMgr::StopTimer()
503 mSystemLayer->CancelTimer(Timeout, this);
506 int ReliableMessageMgr::TestGetCountRetransTable()
510 for (RetransTableEntry & entry : mRetransTable)
512 ReliableMessageContext * rc = entry.rc;
520 } // namespace Messaging