@@ -58,10 +58,15 @@ typedef unsigned char byte_t; ///< For compatibility with platforms where byte s
5858
5959/// The number of most recent transfers to keep in the history for ACK retransmission and duplicate detection.
6060/// Should be a power of two to allow replacement of modulo operation with a bitwise AND.
61+ ///
62+ /// Implementation node: we used to store bitmask windows instead of a full list of recent transfer-IDs, but they
63+ /// were found to offer no advantage except in the perfect scenario of non-restarting senders, and an increased
64+ /// implementation complexity (more branches, more lines of code), so they were replaced with a simple list.
65+ /// The list works equally well given a non-contiguous transfer-ID stream, unlike the bitmask, thus more robust.
6166#define RX_TRANSFER_HISTORY_COUNT 16U
6267
6368/// In the ORDERED reassembly mode, with the most recently received transfer-ID N, the library will reject
64- /// transfers with transfer-ID less than or equal to N - ORDERING_WINDOW (modulo 2^64) as late.
69+ /// transfers with transfer-ID less than or equal to N- ORDERING_WINDOW (modulo 2^64) as late.
6570#define RX_TRANSFER_ORDERING_WINDOW 1024U
6671
6772#define UDP_PORT 9382U
@@ -1280,7 +1285,6 @@ static void rx_session_ordered_scan_slots(rx_session_t* const self,
12801285 }
12811286 }
12821287 }
1283-
12841288 // The slot needs to be ejected if it's in-sequence, if it's reordering window is closed, or if we're
12851289 // asked to force an ejection and we haven't done so yet.
12861290 // The reordering window timeout implies that earlier transfers will be dropped if ORDERED mode is used.
@@ -1303,14 +1307,12 @@ static void rx_session_ordered_scan_slots(rx_session_t* const self,
13031307 }
13041308 break ; // No more slots can be ejected at this time.
13051309 }
1306-
13071310 // We always pick the next transfer to eject with the nearest transfer-ID, which guarantees that the other
13081311 // DONE transfers will not end up being late.
13091312 // Some of the in-progress slots may be obsoleted by this move, which will be taken care of later.
13101313 UDPARD_ASSERT ((slot != NULL ) && (slot -> state == rx_slot_done ));
13111314 rx_session_eject (self , rx , slot );
13121315 }
1313-
13141316 // Ensure that in-progress slots, if any, have not ended up within the accepted window after the update.
13151317 // We can release them early to avoid holding the payload buffers that won't be used anyway.
13161318 for (size_t i = 0 ; i < RX_SLOT_COUNT ; i ++ ) {
@@ -1387,6 +1389,7 @@ static void rx_session_update_ordered(rx_session_t* const self,
13871389 const rx_frame_t frame ,
13881390 const udpard_mem_deleter_t payload_deleter )
13891391{
1392+ // The queries here may be a bit time-consuming. If this becomes a problem, there are many ways to optimize this.
13901393 const bool is_ejected = rx_session_is_transfer_ejected (self , frame .meta .transfer_id );
13911394 const bool is_late_or_ejected = rx_session_is_transfer_late_or_ejected (self , frame .meta .transfer_id );
13921395 const bool is_interned = rx_session_is_transfer_interned (self , frame .meta .transfer_id );
@@ -1413,6 +1416,9 @@ static void rx_session_update_ordered(rx_session_t* const self,
14131416 rx_session_ordered_scan_slots (self , rx , ts , false);
14141417 }
14151418 } else { // retransmit ACK if needed
1419+ // Note: transfers that are no longer retained in the history will not solicit an ACK response,
1420+ // meaning that the sender will not get a confirmation if the retransmitted transfer is too old.
1421+ // We assume that RX_TRANSFER_HISTORY_COUNT is enough to cover all sensible use cases.
14161422 if ((is_interned || is_ejected ) && frame .meta .flag_ack && (frame .base .offset == 0U )) {
14171423 rx_session_on_ack_mandate (self , rx , frame .meta .priority , frame .meta .transfer_id , frame .base .payload );
14181424 }
0 commit comments