@@ -595,8 +595,8 @@ typedef struct
595595/// The transfer index contains ALL transfers, used for lookup by (topic_hash, transfer_id).
596596typedef struct tx_transfer_t
597597{
598- udpard_tree_t index_staged ; ///< Soonest to be ready on the left. Key: staged_until
599- udpard_tree_t index_deadline ; ///< Soonest to expire on the left. Key: deadline
598+ udpard_tree_t index_staged ; ///< Soonest to be ready on the left. Key: staged_until + transfer identity
599+ udpard_tree_t index_deadline ; ///< Soonest to expire on the left. Key: deadline + transfer identity
600600 udpard_tree_t index_transfer ; ///< Specific transfer lookup for ack management. Key: tx_transfer_key_t
601601 udpard_listed_t queue [UDPARD_IFACE_COUNT_MAX ]; ///< Listed when ready for transmission.
602602 udpard_listed_t agewise ; ///< Listed when created; oldest at the tail.
@@ -734,13 +734,40 @@ static bool tx_ensure_queue_space(udpard_tx_t* const tx, const size_t total_fram
734734 return total_frames_needed <= (tx -> enqueued_frames_limit - tx -> enqueued_frames_count );
735735}
736736
737+ // Key for time-ordered TX indices with stable tiebreaking.
738+ typedef struct
739+ {
740+ udpard_us_t time ;
741+ uint64_t topic_hash ;
742+ uint64_t transfer_id ;
743+ } tx_time_key_t ;
744+
745+ // Compare staged transfers by time then by transfer identity.
737746static int32_t tx_cavl_compare_staged (const void * const user , const udpard_tree_t * const node )
738747{
739- return ((* (const udpard_us_t * )user ) >= CAVL2_TO_OWNER (node , tx_transfer_t , index_staged )-> staged_until ) ? +1 : -1 ;
748+ const tx_time_key_t * const key = (const tx_time_key_t * )user ;
749+ const tx_transfer_t * const tr = CAVL2_TO_OWNER (node , tx_transfer_t , index_staged ); // clang-format off
750+ if (key -> time < tr -> staged_until ) { return -1 ; }
751+ if (key -> time > tr -> staged_until ) { return +1 ; }
752+ if (key -> topic_hash < tr -> topic_hash ) { return -1 ; }
753+ if (key -> topic_hash > tr -> topic_hash ) { return +1 ; }
754+ if (key -> transfer_id < tr -> transfer_id ) { return -1 ; }
755+ if (key -> transfer_id > tr -> transfer_id ) { return +1 ; }
756+ return 0 ; // clang-format on
740757}
758+
759+ // Compare deadlines by time then by transfer identity.
741760static int32_t tx_cavl_compare_deadline (const void * const user , const udpard_tree_t * const node )
742761{
743- return ((* (const udpard_us_t * )user ) >= CAVL2_TO_OWNER (node , tx_transfer_t , index_deadline )-> deadline ) ? +1 : -1 ;
762+ const tx_time_key_t * const key = (const tx_time_key_t * )user ;
763+ const tx_transfer_t * const tr = CAVL2_TO_OWNER (node , tx_transfer_t , index_deadline ); // clang-format off
764+ if (key -> time < tr -> deadline ) { return -1 ; }
765+ if (key -> time > tr -> deadline ) { return +1 ; }
766+ if (key -> topic_hash < tr -> topic_hash ) { return -1 ; }
767+ if (key -> topic_hash > tr -> topic_hash ) { return +1 ; }
768+ if (key -> transfer_id < tr -> transfer_id ) { return -1 ; }
769+ if (key -> transfer_id > tr -> transfer_id ) { return +1 ; }
770+ return 0 ; // clang-format on
744771}
745772static int32_t tx_cavl_compare_transfer (const void * const user , const udpard_tree_t * const node )
746773{
@@ -855,11 +882,18 @@ static void tx_stage_if(udpard_tx_t* const tx, tx_transfer_t* const tr)
855882 const udpard_us_t timeout = tx_ack_timeout (tx -> ack_baseline_timeout , tr -> priority , epoch );
856883 tr -> staged_until += timeout ;
857884 if ((tr -> deadline - timeout ) >= tr -> staged_until ) {
858- (void )cavl2_find_or_insert (& tx -> index_staged , //
859- & tr -> staged_until ,
860- tx_cavl_compare_staged ,
861- & tr -> index_staged ,
862- cavl2_trivial_factory );
885+ // Insert into staged index with deterministic tie-breaking.
886+ const tx_time_key_t key = { .time = tr -> staged_until ,
887+ .topic_hash = tr -> topic_hash ,
888+ .transfer_id = tr -> transfer_id };
889+ // Ensure we didn't collide with another entry that should be unique.
890+ const udpard_tree_t * const tree_staged = cavl2_find_or_insert (& tx -> index_staged , //
891+ & key ,
892+ tx_cavl_compare_staged ,
893+ & tr -> index_staged ,
894+ cavl2_trivial_factory );
895+ UDPARD_ASSERT (tree_staged == & tr -> index_staged );
896+ (void )tree_staged ;
863897 }
864898}
865899
@@ -1049,12 +1083,19 @@ static bool tx_push(udpard_tx_t* const tx,
10491083 tx_stage_if (tx , tr );
10501084 }
10511085 // Add to the deadline index for expiration management.
1052- (void )cavl2_find_or_insert (
1053- & tx -> index_deadline , & tr -> deadline , tx_cavl_compare_deadline , & tr -> index_deadline , cavl2_trivial_factory );
1086+ // Insert into deadline index with deterministic tie-breaking.
1087+ const tx_time_key_t deadline_key = { .time = tr -> deadline ,
1088+ .topic_hash = tr -> topic_hash ,
1089+ .transfer_id = tr -> transfer_id };
1090+ // Ensure we didn't collide with another entry that should be unique.
1091+ const udpard_tree_t * const tree_deadline = cavl2_find_or_insert (
1092+ & tx -> index_deadline , & deadline_key , tx_cavl_compare_deadline , & tr -> index_deadline , cavl2_trivial_factory );
1093+ UDPARD_ASSERT (tree_deadline == & tr -> index_deadline );
1094+ (void )tree_deadline ;
10541095 // Add to the transfer index for incoming ack management.
1055- const tx_transfer_key_t key = { .topic_hash = tr -> topic_hash , .transfer_id = tr -> transfer_id };
1096+ const tx_transfer_key_t transfer_key = { .topic_hash = tr -> topic_hash , .transfer_id = tr -> transfer_id };
10561097 const udpard_tree_t * const tree_transfer = cavl2_find_or_insert (
1057- & tx -> index_transfer , & key , tx_cavl_compare_transfer , & tr -> index_transfer , cavl2_trivial_factory );
1098+ & tx -> index_transfer , & transfer_key , tx_cavl_compare_transfer , & tr -> index_transfer , cavl2_trivial_factory );
10581099 UDPARD_ASSERT (tree_transfer == & tr -> index_transfer ); // ensure no duplicates; checked at the API level
10591100 (void )tree_transfer ;
10601101 // Add to the agewise list for sacrifice management on queue exhaustion.
@@ -1907,11 +1948,23 @@ static int32_t cavl_compare_rx_session_by_remote_uid(const void* const user, con
19071948 return 0 ; // clang-format on
19081949}
19091950
1951+ // Key for reordering deadline ordering with stable tiebreaking.
1952+ typedef struct
1953+ {
1954+ udpard_us_t deadline ;
1955+ uint64_t remote_uid ;
1956+ } rx_reordering_key_t ;
1957+
1958+ // Compare sessions by reordering deadline then by remote UID.
19101959static int32_t cavl_compare_rx_session_by_reordering_deadline (const void * const user , const udpard_tree_t * const node )
19111960{
1912- const udpard_us_t dl_a = * (const udpard_us_t * )user ;
1913- const udpard_us_t dl_b = CAVL2_TO_OWNER (node , rx_session_t , index_reordering_window )-> reordering_window_deadline ;
1914- return (dl_a >= dl_b ) ? +1 : -1 ;
1961+ const rx_reordering_key_t * const key = (const rx_reordering_key_t * )user ;
1962+ const rx_session_t * const ses = CAVL2_TO_OWNER (node , rx_session_t , index_reordering_window ); // clang-format off
1963+ if (key -> deadline < ses -> reordering_window_deadline ) { return -1 ; }
1964+ if (key -> deadline > ses -> reordering_window_deadline ) { return +1 ; }
1965+ if (key -> remote_uid < ses -> remote .uid ) { return -1 ; }
1966+ if (key -> remote_uid > ses -> remote .uid ) { return +1 ; }
1967+ return 0 ; // clang-format on
19151968}
19161969
19171970typedef struct
@@ -2028,8 +2081,11 @@ static void rx_session_ordered_scan_slots(rx_session_t* const self,
20282081 // closure deadline, but we ignore them because the nearest transfer overrides the more distant ones.
20292082 if (slot != NULL ) {
20302083 self -> reordering_window_deadline = slot -> ts_min + self -> port -> reordering_window ;
2031- const udpard_tree_t * res = cavl2_find_or_insert (& rx -> index_session_by_reordering , //-------------
2032- & self -> reordering_window_deadline ,
2084+ // Insert into reordering index with deterministic tie-breaking.
2085+ const rx_reordering_key_t key = { .deadline = self -> reordering_window_deadline ,
2086+ .remote_uid = self -> remote .uid };
2087+ const udpard_tree_t * res = cavl2_find_or_insert (& rx -> index_session_by_reordering , //----------------
2088+ & key ,
20332089 & cavl_compare_rx_session_by_reordering_deadline ,
20342090 & self -> index_reordering_window ,
20352091 & cavl2_trivial_factory );
0 commit comments