diff --git a/lib/ngtcp2_rtb.c b/lib/ngtcp2_rtb.c
index 1f1eb6e2c56d6d252d74e6f1b466fc70a3f10581..d58a6b2ea1adb79689331d0f5a3bf4c5ad1563a6 100644
--- a/lib/ngtcp2_rtb.c
+++ b/lib/ngtcp2_rtb.c
@@ -192,6 +192,7 @@ int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd,
   (*pent)->lost_ts = UINT64_MAX;
   (*pent)->pktlen = pktlen;
   (*pent)->flags = flags;
+  (*pent)->next = NULL;
 
   return 0;
 }
@@ -505,8 +506,6 @@ int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
                    ngtcp2_conn_stat *cstat) {
   int rv;
 
-  ent->next = NULL;
-
   rv = ngtcp2_ksl_insert(&rtb->ents, NULL, &ent->hd.pkt_num, ent);
   if (rv != 0) {
     return rv;
@@ -522,12 +521,16 @@ ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) {
 }
 
 static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
-                       ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat) {
+                       ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent,
+                       ngtcp2_conn_stat *cstat) {
   int rv;
   rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num);
   assert(0 == rv);
   rtb_on_remove(rtb, ent, cstat);
-  ngtcp2_rtb_entry_del(ent, rtb->mem);
+
+  assert(ent->next == NULL);
+
+  ngtcp2_list_insert(ent, pent);
 }
 
 static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
@@ -668,10 +671,11 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   int rv;
   ngtcp2_ksl_it it;
   ngtcp2_ssize num_acked = 0;
-  int rtt_updated = 0;
   ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX;
   int64_t pkt_num;
   ngtcp2_cc *cc = rtb->cc;
+  ngtcp2_rtb_entry *acked_ent = NULL;
+  int ack_eliciting_pkt_acked = 0;
 
   if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) &&
       largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) {
@@ -702,29 +706,16 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     }
 
     ent = ngtcp2_ksl_it_get(&it);
-    if (conn) {
-      rv = rtb_process_acked_pkt(rtb, ent, conn);
-      if (rv != 0) {
-        return rv;
-      }
-      if (largest_ack == pkt_num) {
-        largest_pkt_sent_ts = ent->ts;
-      }
-      if (!rtt_updated && largest_pkt_sent_ts != UINT64_MAX &&
-          (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) {
-        rtt_updated = 1;
-        ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts,
-                               fr->ack_delay_unscaled);
-        if (cc->new_rtt_sample) {
-          cc->new_rtt_sample(cc, cstat, ts);
-        }
-      }
 
-      rtb_on_pkt_acked(rtb, ent, cstat, ts);
-      /* At this point, it is invalided because rtb->ents might be
-         modified. */
+    if (largest_ack == pkt_num) {
+      largest_pkt_sent_ts = ent->ts;
     }
-    rtb_remove(rtb, &it, ent, cstat);
+
+    if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) {
+      ack_eliciting_pkt_acked = 1;
+    }
+
+    rtb_remove(rtb, &it, &acked_ent, ent, cstat);
     ++num_acked;
   }
 
@@ -743,34 +734,59 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
         break;
       }
       ent = ngtcp2_ksl_it_get(&it);
-      if (conn) {
-        rv = rtb_process_acked_pkt(rtb, ent, conn);
-        if (rv != 0) {
-          return rv;
-        }
-        if (!rtt_updated && largest_pkt_sent_ts != UINT64_MAX &&
-            (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) {
-          rtt_updated = 1;
-          ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts,
-                                 fr->ack_delay_unscaled);
-          if (cc->new_rtt_sample) {
-            cc->new_rtt_sample(cc, cstat, ts);
-          }
-        }
 
-        rtb_on_pkt_acked(rtb, ent, cstat, ts);
+      if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) {
+        ack_eliciting_pkt_acked = 1;
       }
-      rtb_remove(rtb, &it, ent, cstat);
+
+      rtb_remove(rtb, &it, &acked_ent, ent, cstat);
       ++num_acked;
     }
 
     ++i;
   }
 
+  if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
+    ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts,
+                           fr->ack_delay_unscaled);
+    if (cc->new_rtt_sample) {
+      cc->new_rtt_sample(cc, cstat, ts);
+    }
+  }
+
   ngtcp2_rst_on_ack_recv(rtb->rst, cstat);
+
+  if (conn) {
+    for (ent = acked_ent; ent; ent = acked_ent) {
+      rv = rtb_process_acked_pkt(rtb, ent, conn);
+      if (rv != 0) {
+        goto fail;
+      }
+
+      rtb_on_pkt_acked(rtb, ent, cstat, ts);
+      acked_ent = ent->next;
+      ngtcp2_rtb_entry_del(ent, rtb->mem);
+    }
+  } else {
+    /* For unit tests */
+    for (ent = acked_ent; ent; ent = acked_ent) {
+      rtb_on_pkt_acked(rtb, ent, cstat, ts);
+      acked_ent = ent->next;
+      ngtcp2_rtb_entry_del(ent, rtb->mem);
+    }
+  }
+
   cc->on_ack_recv(cc, cstat, ts);
 
   return num_acked;
+
+fail:
+  for (ent = acked_ent; ent; ent = acked_ent) {
+    acked_ent = ent->next;
+    ngtcp2_rtb_entry_del(ent, rtb->mem);
+  }
+
+  return rv;
 }
 
 static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat,