Skip to content

Commit 21ba5bb

Browse files
authored
[coap] ensure correct retransmission timer scheduling (openthread#11348)
This commit introduces `CoapBase::ScheduleRetransmissionTimer()`, a new method that calculates the next retransmission timer fire time based on all queued messages in `mPendingRequests` and then schedules (starts or stops) the timer. This method is now used whenever `mPendingRequests` is updated (a new message is added or an existing message is removed). It is also used in `HandleRetransmissionTimer()`, the timer's callback. This change centralizes timer scheduling, simplifying the logic. This also addresses an issue in the existing code where `HandleRetransmissionTimer()` iterated over `mPendingRequests` to determine expired messages and calculate the next fire time. However, finalizing an expired message could trigger its `ResponseHandler` callback, from which the caller may start or abort CoAP message tx and modify `mPendingRequests` and reschedule the timer, leading to incorrect fire time calculations (`NextFireTime` can be incorrect which would then improperly re-schedules the timer, stop it or schedule it to a later time). With this change, `HandleRetransmissionTimer()` first finalizes expired messages (potentially invoking callbacks) and then calls `ScheduleRetransmissionTimer()` to calculate the next fire time based on the updated `mPendingRequests`, ensuring accurate timer scheduling.
1 parent d095eb3 commit 21ba5bb

File tree

2 files changed

+33
-19
lines changed

2 files changed

+33
-19
lines changed

src/core/coap/coap.cpp

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -448,22 +448,47 @@ Error CoapBase::SendHeaderResponse(Message::Code aCode, const Message &aRequest,
448448
return error;
449449
}
450450

451+
void CoapBase::ScheduleRetransmissionTimer(void)
452+
{
453+
NextFireTime nextTime;
454+
Metadata metadata;
455+
456+
for (const Message &message : mPendingRequests)
457+
{
458+
metadata.ReadFrom(message);
459+
460+
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
461+
if (message.IsRequest() && metadata.mObserve && metadata.mAcknowledged)
462+
{
463+
// This is an RFC7641 subscription which is already acknowledged.
464+
// We do not time it out, so skip it when determining the next
465+
// fire time.
466+
continue;
467+
}
468+
#endif
469+
470+
nextTime.UpdateIfEarlier(metadata.mNextTimerShot);
471+
}
472+
473+
mRetransmissionTimer.FireAt(nextTime);
474+
}
475+
451476
void CoapBase::HandleRetransmissionTimer(Timer &aTimer)
452477
{
453478
static_cast<Coap *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleRetransmissionTimer();
454479
}
455480

456481
void CoapBase::HandleRetransmissionTimer(void)
457482
{
458-
NextFireTime nextTime;
483+
TimeMilli now = TimerMilli::GetNow();
459484
Metadata metadata;
460485
Ip6::MessageInfo messageInfo;
461486

462487
for (Message &message : mPendingRequests)
463488
{
464489
metadata.ReadFrom(message);
465490

466-
if (nextTime.GetNow() >= metadata.mNextTimerShot)
491+
if (now >= metadata.mNextTimerShot)
467492
{
468493
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
469494
if (message.IsRequest() && metadata.mObserve && metadata.mAcknowledged)
@@ -483,7 +508,7 @@ void CoapBase::HandleRetransmissionTimer(void)
483508
// Increment retransmission counter and timer.
484509
metadata.mRetransmissionsRemaining--;
485510
metadata.mRetransmissionTimeout *= 2;
486-
metadata.mNextTimerShot = nextTime.GetNow() + metadata.mRetransmissionTimeout;
511+
metadata.mNextTimerShot = now + metadata.mRetransmissionTimeout;
487512
metadata.UpdateIn(message);
488513

489514
// Retransmit
@@ -501,11 +526,9 @@ void CoapBase::HandleRetransmissionTimer(void)
501526
SendCopy(message, messageInfo);
502527
}
503528
}
504-
505-
nextTime.UpdateIfEarlier(metadata.mNextTimerShot);
506529
}
507530

508-
mRetransmissionTimer.FireAt(nextTime);
531+
ScheduleRetransmissionTimer();
509532
}
510533

511534
void CoapBase::FinalizeCoapTransaction(Message &aRequest,
@@ -550,9 +573,8 @@ Message *CoapBase::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopy
550573

551574
SuccessOrExit(error = aMetadata.AppendTo(*messageCopy));
552575

553-
mRetransmissionTimer.FireAtIfEarlier(aMetadata.mNextTimerShot);
554-
555576
mPendingRequests.Enqueue(*messageCopy);
577+
ScheduleRetransmissionTimer();
556578

557579
exit:
558580
FreeAndNullMessageOnError(messageCopy, error);
@@ -561,17 +583,8 @@ Message *CoapBase::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopy
561583

562584
void CoapBase::DequeueMessage(Message &aMessage)
563585
{
564-
mPendingRequests.Dequeue(aMessage);
565-
566-
if (mRetransmissionTimer.IsRunning() && (mPendingRequests.GetHead() == nullptr))
567-
{
568-
mRetransmissionTimer.Stop();
569-
}
570-
571-
aMessage.Free();
572-
573-
// No need to worry that the earliest pending message was removed -
574-
// the timer would just shoot earlier and then it'd be setup again.
586+
mPendingRequests.DequeueAndFree(aMessage);
587+
ScheduleRetransmissionTimer();
575588
}
576589

577590
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE

src/core/coap/coap.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ class CoapBase : public InstanceLocator, private NonCopyable
821821
Message *InitMessage(Message *aMessage, Type aType, Uri aUri);
822822
Message *InitResponse(Message *aMessage, const Message &aRequest);
823823

824+
void ScheduleRetransmissionTimer(void);
824825
static void HandleRetransmissionTimer(Timer &aTimer);
825826
void HandleRetransmissionTimer(void);
826827

0 commit comments

Comments
 (0)