2 * Copyright (c) 2020 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/ReliableMessageManager.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 ReliableMessageManager::RetransTableEntry::RetransTableEntry() :
40 rc(nullptr), msgBuf(nullptr), msgId(0), msgSendFlags(0), nextRetransTimeTick(0), sendCount(0)
43 ReliableMessageManager::ReliableMessageManager() :
44 mTimeStampBase(System::Timer::GetCurrentEpoch()), mCurrentTimerExpiry(0),
45 mTimerIntervalShift(CHIP_CONFIG_RMP_TIMER_DEFAULT_PERIOD_SHIFT)
48 ReliableMessageManager::~ReliableMessageManager() {}
50 void ReliableMessageManager::ProcessDelayedDeliveryMessage(ReliableMessageContext * rc, uint32_t PauseTimeMillis)
52 // Expire any virtual ticks that have expired so all wakeup sources reflect the current time
55 // Go through the retrans table entries for that node and adjust the timer.
56 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
58 // Exchcontext is the sentinel object to ascertain validity of the element
59 if (RetransTable[i].rc && RetransTable[i].rc == rc)
61 // Paustime is specified in milliseconds; Update retrans values
62 RetransTable[i].nextRetransTimeTick =
63 static_cast<uint16_t>(RetransTable[i].nextRetransTimeTick + (PauseTimeMillis >> mTimerIntervalShift));
65 } // for loop in table entry
67 // Schedule next physical wakeup
72 * Return a tick counter value given a time period.
74 * @param[in] newTime Timestamp value of in milliseconds.
76 * @return Tick count for the time period.
78 uint64_t ReliableMessageManager::GetTickCounterFromTimePeriod(uint64_t period)
80 return (period >> mTimerIntervalShift);
84 * Return a tick counter value between the given time and the stored time.
86 * @param[in] newTime Timestamp value of in milliseconds.
88 * @return Tick count of the difference between the given time and the stored time.
90 uint64_t ReliableMessageManager::GetTickCounterFromTimeDelta(uint64_t newTime)
92 return GetTickCounterFromTimePeriod(newTime - mTimeStampBase);
95 #if defined(RMP_TICKLESS_DEBUG)
96 void ReliableMessageManager::TicklessDebugDumpRetransTable(const char * log)
98 ChipLogProgress(ExchangeManager, log);
100 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
102 if (RetransTable[i].rc)
104 ChipLogProgress(ExchangeManager, "EC:%04" PRIX16 " MsgId:%08" PRIX32 " NextRetransTimeCtr:%04" PRIX16,
105 RetransTable[i].rc, RetransTable[i].msgId, RetransTable[i].nextRetransTimeTick);
110 void ReliableMessageManager::TicklessDebugDumpRetransTable(const char * log)
114 #endif // RMP_TICKLESS_DEBUG
117 * Iterate through active exchange contexts and retrans table entries. If an
118 * action needs to be triggered by ReliableMessageProtocol time facilities,
119 * execute that action.
121 void ReliableMessageManager::ExecuteActions()
123 #if defined(RMP_TICKLESS_DEBUG)
124 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExecuteActions");
127 ExecuteForAllContext([](ReliableMessageContext * rc) {
128 if (rc->IsAckPending())
130 if (0 == rc->mNextAckTimeTick)
132 #if defined(RMP_TICKLESS_DEBUG)
133 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExecuteActions sending ACK");
135 // Send the Ack in a Common::Null message
136 rc->SendCommonNullMessage();
137 rc->SetAckPending(false);
142 TicklessDebugDumpRetransTable("ReliableMessageManager::ExecuteActions Dumping RetransTable entries before processing");
144 // Retransmit / cancel anything in the retrans table whose retrans timeout
146 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
148 ReliableMessageContext * rc = RetransTable[i].rc;
149 CHIP_ERROR err = CHIP_NO_ERROR;
151 if (!rc || RetransTable[i].nextRetransTimeTick != 0)
154 uint8_t sendCount = RetransTable[i].sendCount;
156 if (sendCount == rc->mConfig.mMaxRetrans)
158 err = CHIP_ERROR_MESSAGE_NOT_ACKNOWLEDGED;
160 ChipLogError(ExchangeManager, "Failed to Send CHIP MsgId:%08" PRIX32 " sendCount: %" PRIu8 " max retries: %" PRIu8,
161 RetransTable[i].msgId, sendCount, rc->mConfig.mMaxRetrans);
164 ClearRetransmitTable(RetransTable[i]);
167 // Resend from Table (if the operation fails, the entry is cleared)
168 if (err == CHIP_NO_ERROR)
169 err = SendFromRetransTable(&(RetransTable[i]));
171 if (err == CHIP_NO_ERROR)
173 // If the retransmission was successful, update the passive timer
174 RetransTable[i].nextRetransTimeTick = static_cast<uint16_t>(rc->GetCurrentRetransmitTimeoutTick());
176 ChipLogProgress(ExchangeManager, "Retransmit MsgId:%08" PRIX32 " Send Cnt %d", RetransTable[i].msgId,
177 RetransTable[i].sendCount);
181 if (err != CHIP_NO_ERROR)
182 rc->mDelegate->OnSendError(err);
185 TicklessDebugDumpRetransTable("ReliableMessageManager::ExecuteActions Dumping RetransTable entries after processing");
188 static void TickProceed(uint16_t & time, uint64_t ticks)
192 time = static_cast<uint16_t>(time - ticks);
201 * Calculate number of virtual ReliableMessageProtocol ticks that have expired
202 * since we last called this function. Iterate through active exchange contexts
203 * and retrans table entries, subtracting expired virtual ticks to synchronize
204 * wakeup times with the current system time. Do not perform any actions beyond
205 * updating tick counts, actions will be performed by the physical
206 * ReliableMessageProtocol timer tick expiry.
209 void ReliableMessageManager::ExpireTicks()
211 uint64_t now = System::Timer::GetCurrentEpoch();
213 // Number of full ticks elapsed since last timer processing. We always round down
214 // to the previous tick. If we are between tick boundaries, the extra time since the
215 // last virtual tick is not accounted for here (it will be accounted for when resetting
216 // the ReliableMessageProtocol timer)
217 uint64_t deltaTicks = GetTickCounterFromTimeDelta(now);
219 #if defined(RMP_TICKLESS_DEBUG)
220 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExpireTicks at %" PRIu64 ", %" PRIu64 ", %u", now, mTimeStampBase,
224 ExecuteForAllContext([deltaTicks](ReliableMessageContext * rc) {
225 if (rc->IsAckPending())
227 // Decrement counter of Ack timestamp by the elapsed timer ticks
228 TickProceed(rc->mNextAckTimeTick, deltaTicks);
229 #if defined(RMP_TICKLESS_DEBUG)
230 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExpireTicks set mNextAckTimeTick to %u",
231 rc->mNextAckTimeTick);
236 // Process Throttle Time
237 // Check Throttle timeout stored in EC to set/unset Throttle flag
238 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
240 ReliableMessageContext * rc = RetransTable[i].rc;
243 // Process Retransmit Table
244 // Decrement Throttle timeout by elapsed timeticks
245 TickProceed(rc->mThrottleTimeoutTick, deltaTicks);
246 #if defined(RMP_TICKLESS_DEBUG)
247 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExpireTicks set mThrottleTimeoutTick to %u",
248 RetransTable[i].nextRetransTimeTick);
251 // Decrement Retransmit timeout by elapsed timeticks
252 TickProceed(RetransTable[i].nextRetransTimeTick, deltaTicks);
253 #if defined(RMP_TICKLESS_DEBUG)
254 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExpireTicks set nextRetransTimeTick to %u",
255 RetransTable[i].nextRetransTimeTick);
257 } // rc entry is allocated
260 // Re-Adjust the base time stamp to the most recent tick boundary
261 mTimeStampBase += (deltaTicks << mTimerIntervalShift);
263 #if defined(RMP_TICKLESS_DEBUG)
264 ChipLogProgress(ExchangeManager, "ReliableMessageManager::ExpireTicks mTimeStampBase to %" PRIu64, mTimeStampBase);
269 * Handle physical wakeup of system due to ReliableMessageProtocol wakeup.
272 void ReliableMessageManager::Timeout(System::Layer * aSystemLayer, void * aAppState, System::Error aError)
274 ReliableMessageManager * manager = reinterpret_cast<ReliableMessageManager *>(aAppState);
276 VerifyOrDie((aSystemLayer != nullptr) && (manager != nullptr));
278 #if defined(RMP_TICKLESS_DEBUG)
279 ChipLogProgress(ExchangeManager, "ReliableMessageManager::Timeout\n");
282 // Make sure all tick counts are sync'd to the current time
283 manager->ExpireTicks();
285 // Execute any actions that are due this tick
286 manager->ExecuteActions();
288 // Calculate next physical wakeup
289 manager->StartTimer();
293 * Add a CHIP message into the retransmission table to be subsequently resent if a corresponding acknowledgment
294 * is not received within the retransmission timeout.
296 * @param[in] rc A pointer to the ExchangeContext object.
298 * @param[in] msgBuf A pointer to the message buffer holding the CHIP message to be retransmitted.
300 * @param[in] messageId The message identifier of the stored CHIP message.
302 * @param[out] rEntry A pointer to a pointer of a retransmission table entry added into the table.
304 * @retval #CHIP_ERROR_RETRANS_TABLE_FULL If there is no empty slot left in the table for addition.
305 * @retval #CHIP_NO_ERROR On success.
308 CHIP_ERROR ReliableMessageManager::AddToRetransTable(ReliableMessageContext * rc, System::PacketBuffer * msgBuf, uint32_t messageId,
309 uint16_t msgSendFlags, RetransTableEntry ** rEntry)
312 CHIP_ERROR err = CHIP_NO_ERROR;
314 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
316 // Check the exchContext pointer for finding an empty slot in Table
317 if (!RetransTable[i].rc)
319 // Expire any virtual ticks that have expired so all wakeup sources reflect the current time
322 RetransTable[i].rc = rc;
323 RetransTable[i].msgId = messageId;
324 RetransTable[i].msgBuf = msgBuf;
325 RetransTable[i].msgSendFlags = msgSendFlags;
326 RetransTable[i].sendCount = 0;
327 RetransTable[i].nextRetransTimeTick = static_cast<uint16_t>(
328 rc->GetCurrentRetransmitTimeoutTick() + GetTickCounterFromTimeDelta(System::Timer::GetCurrentEpoch()));
330 *rEntry = &RetransTable[i];
331 // Increment the reference count
335 // Check if the timer needs to be started and start it.
343 ChipLogError(ExchangeManager, "RetransTable Already Full");
344 err = CHIP_ERROR_RETRANS_TABLE_FULL;
350 void ReliableMessageManager::PauseRetransTable(ReliableMessageContext * rc, uint32_t PauseTimeMillis)
352 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
354 if (RetransTable[i].rc == rc)
356 RetransTable[i].nextRetransTimeTick =
357 static_cast<uint16_t>(RetransTable[i].nextRetransTimeTick + (PauseTimeMillis >> mTimerIntervalShift));
363 void ReliableMessageManager::ResumeRetransTable(ReliableMessageContext * rc)
365 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
367 if (RetransTable[i].rc == rc)
369 RetransTable[i].nextRetransTimeTick = 0;
375 bool ReliableMessageManager::CheckAndRemRetransTable(ReliableMessageContext * rc, uint32_t ackMsgId)
377 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
379 if ((RetransTable[i].rc == rc) && RetransTable[i].msgId == ackMsgId)
381 // Clear the entry from the retransmision table.
382 ClearRetransmitTable(RetransTable[i]);
385 ChipLogProgress(ExchangeManager, "Rxd Ack; Removing MsgId:%08" PRIX32 " from Retrans Table", ackMsgId);
395 * Send the specified entry from the retransmission table.
397 * @param[in] entry A pointer to a retransmission table entry object that needs to be sent.
399 * @return #CHIP_NO_ERROR On success, else corresponding CHIP_ERROR returned from SendMessage.
402 CHIP_ERROR ReliableMessageManager::SendFromRetransTable(RetransTableEntry * entry)
404 CHIP_ERROR err = CHIP_NO_ERROR;
405 ReliableMessageContext * rc = entry->rc;
407 // To trigger a call to OnSendError, set the number of transmissions so
408 // that the next call to ExecuteActions will abort this entry,
409 // restart the timer immediately, and ExitNow.
411 CHIP_FAULT_INJECT(FaultInjection::kFault_RMPSendError, entry->sendCount = static_cast<uint8_t>(rc->mConfig.mMaxRetrans + 1);
412 entry->nextRetransTimeTick = 0; StartTimer(); ExitNow());
416 // Locally store the start and length;
417 uint8_t * p = entry->msgBuf->Start();
418 uint16_t len = entry->msgBuf->DataLength();
420 // Send the message through
421 uint16_t msgSendFlags = entry->msgSendFlags;
422 SetFlag(msgSendFlags, MessageFlagValues::kMessageFlag_RetainBuffer);
423 err = SendMessage(rc, entry->msgBuf, msgSendFlags);
425 // Reset the msgBuf start pointer and data length after sending
426 entry->msgBuf->SetStart(p);
427 entry->msgBuf->SetDataLength(len);
429 // Update the counters
434 ChipLogError(ExchangeManager, "Table entry invalid");
437 VerifyOrExit(err != CHIP_NO_ERROR, err = CHIP_NO_ERROR);
439 // Any error generated during initial sending is evaluated for criticality which would
440 // qualify it to be reportable back to the caller. If it is non-critical then
441 // err is set to CHIP_NO_ERROR.
442 if (IsSendErrorNonCritical(err))
444 ChipLogError(ExchangeManager, "Non-crit err %ld sending CHIP MsgId:%08" PRIX32 " from retrans table", long(err),
451 ChipLogError(ExchangeManager, "Crit-err %ld when sending CHIP MsgId:%08" PRIX32 ", send tries: %d", long(err), entry->msgId,
454 ClearRetransmitTable(*entry);
462 * Clear entries matching a specified ExchangeContext.
464 * @param[in] rc A pointer to the ExchangeContext object.
467 void ReliableMessageManager::ClearRetransmitTable(ReliableMessageContext * rc)
469 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
471 if (RetransTable[i].rc == rc)
473 // Clear the retransmit table entry.
474 ClearRetransmitTable(RetransTable[i]);
480 * Clear an entry in the retransmission table.
482 * @param[in] rEntry A reference to the RetransTableEntry object.
485 void ReliableMessageManager::ClearRetransmitTable(RetransTableEntry & rEntry)
489 // Expire any virtual ticks that have expired so all wakeup sources reflect the current time
492 rEntry.rc->Release();
497 System::PacketBuffer::Free(rEntry.msgBuf);
498 rEntry.msgBuf = nullptr;
501 // Clear all other fields
502 rEntry = RetransTableEntry();
504 // Schedule next physical wakeup
510 * Fail entries matching a specified ExchangeContext.
512 * @param[in] rc A pointer to the ExchangeContext object.
514 * @param[in] err The error for failing table entries.
517 void ReliableMessageManager::FailRetransmitTableEntries(ReliableMessageContext * rc, CHIP_ERROR err)
519 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
521 if (RetransTable[i].rc == rc)
523 // Remove the entry from the retransmission table.
524 ClearRetransmitTable(RetransTable[i]);
526 // Application callback OnSendError.
527 rc->mDelegate->OnSendError(err);
533 * Iterate through active exchange contexts and retrans table entries.
534 * Determine how many ReliableMessageProtocol ticks we need to sleep before we
535 * need to physically wake the CPU to perform an action. Set a timer to go off
536 * when we next need to wake the system.
538 void ReliableMessageManager::StartTimer()
540 CHIP_ERROR res = CHIP_NO_ERROR;
541 uint64_t nextWakeTimeTick = UINT64_MAX;
542 bool foundWake = false;
544 // When do we need to next wake up to send an ACK?
546 ExecuteForAllContext([&nextWakeTimeTick, &foundWake](ReliableMessageContext * rc) {
547 if (rc->IsAckPending() && rc->mNextAckTimeTick < nextWakeTimeTick)
549 nextWakeTimeTick = rc->mNextAckTimeTick;
551 #if defined(RMP_TICKLESS_DEBUG)
552 ChipLogProgress(ExchangeManager, "ReliableMessageManager::StartTimer next ACK time %u", nextWakeTimeTick);
557 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
559 ReliableMessageContext * rc = RetransTable[i].rc;
562 // When do we need to next wake up for throttle retransmission?
563 if (rc->mThrottleTimeoutTick != 0 && rc->mThrottleTimeoutTick < nextWakeTimeTick)
565 nextWakeTimeTick = rc->mThrottleTimeoutTick;
567 #if defined(RMP_TICKLESS_DEBUG)
568 ChipLogProgress(ExchangeManager, "ReliableMessageManager::StartTimer throttle timeout %u", nextWakeTimeTick);
572 // When do we need to next wake up for ReliableMessageProtocol retransmit?
573 if (RetransTable[i].nextRetransTimeTick < nextWakeTimeTick)
575 nextWakeTimeTick = RetransTable[i].nextRetransTimeTick;
577 #if defined(RMP_TICKLESS_DEBUG)
578 ChipLogProgress(ExchangeManager, "ReliableMessageManager::StartTimer RetransTime %u", nextWakeTimeTick);
586 // Set timer for next tick boundary - subtract the elapsed time from the current tick
587 System::Timer::Epoch timerExpiryEpoch = (nextWakeTimeTick << mTimerIntervalShift) + mTimeStampBase;
589 #if defined(RMP_TICKLESS_DEBUG)
590 ChipLogProgress(ExchangeManager, "ReliableMessageManager::StartTimer wake at %" PRIu64 " ms (%" PRIu64 " %" PRIu64 ")",
591 timerExpiryEpoch, nextWakeTimeTick, mTimeStampBase);
593 if (timerExpiryEpoch != mCurrentTimerExpiry)
595 // If the tick boundary has expired in the past (delayed processing of event due to other system activity),
596 // expire the timer immediately
597 uint64_t now = System::Timer::GetCurrentEpoch();
598 uint64_t timerArmValue = (timerExpiryEpoch > now) ? timerExpiryEpoch - now : 0;
600 #if defined(RMP_TICKLESS_DEBUG)
601 ChipLogProgress(ExchangeManager, "ReliableMessageManager::StartTimer set timer for %" PRIu64, timerArmValue);
604 res = mSystemLayer->StartTimer((uint32_t) timerArmValue, Timeout, this);
606 VerifyOrDieWithMsg(res == CHIP_NO_ERROR, ExchangeManager, "Cannot start ReliableMessageManager::Timeout\n");
607 mCurrentTimerExpiry = timerExpiryEpoch;
608 #if defined(RMP_TICKLESS_DEBUG)
612 ChipLogProgress(ExchangeManager, "ReliableMessageManager::StartTimer timer already set for %" PRIu64, timerExpiryEpoch);
618 #if defined(RMP_TICKLESS_DEBUG)
619 ChipLogProgress(ExchangeManager, "Not setting ReliableMessageProtocol timeout at %" PRIu64,
620 System::Timer::GetCurrentEpoch());
625 TicklessDebugDumpRetransTable("ReliableMessageManager::StartTimer Dumping RetransTable entries after setting wakeup times");
628 void ReliableMessageManager::StopTimer()
630 mSystemLayer->CancelTimer(Timeout, this);
633 int ReliableMessageManager::TestGetCountRetransTable()
636 for (int i = 0; i < CHIP_CONFIG_RMP_RETRANS_TABLE_SIZE; i++)
638 ReliableMessageContext * rc = RetransTable[i].rc;
645 } // namespace Messaging