@@ -474,12 +474,23 @@ static bool header_deserialize(const udpard_bytes_mut_t dgram_payload,
474474/// but it might create a bit more memory pressure on average.
475475typedef struct tx_frame_t
476476{
477+ size_t refcount ; ///< Buffer destroyed when refcount reaches zero.
477478 struct tx_frame_t * next ;
478479 byte_t data [];
479480} tx_frame_t ;
480481
481482static size_t tx_frame_object_size (const size_t mtu ) { return sizeof (tx_frame_t ) + mtu + HEADER_SIZE_BYTES ; }
482483
484+ static udpard_bytes_t tx_frame_view (const tx_frame_t * const frame , const size_t mtu )
485+ {
486+ return (udpard_bytes_t ){ .size = mtu + HEADER_SIZE_BYTES , .data = frame -> data };
487+ }
488+
489+ static tx_frame_t * tx_frame_from_view (const udpard_bytes_t view )
490+ {
491+ return (tx_frame_t * )unbias_ptr (view .data , offsetof(tx_frame_t , data ));
492+ }
493+
483494typedef struct
484495{
485496 uint64_t topic_hash ;
@@ -536,14 +547,14 @@ static bool tx_validate_mem_resources(const udpard_tx_mem_resources_t memory)
536547 (memory .payload .alloc != NULL ) && (memory .payload .free != NULL );
537548}
538549
539- static void tx_transfer_free_payload (const udpard_tx_mem_resources_t mem , tx_transfer_t * const tr )
550+ static void tx_transfer_free_payload (udpard_tx_t * const tx , tx_transfer_t * const tr )
540551{
541552 UDPARD_ASSERT (tr != NULL );
542553 tx_frame_t * frame = tr -> head ;
543554 while (frame != NULL ) {
544555 tx_frame_t * const next = frame -> next ;
545556 const size_t mtu = (frame -> next == NULL ) ? tr -> mtu_last : tr -> mtu ;
546- mem_free ( mem . payload , tx_frame_object_size ( mtu ), frame );
557+ udpard_tx_refcount_dec ( tx , tx_frame_view ( frame , mtu ) );
547558 frame = next ;
548559 }
549560 tr -> head = NULL ;
@@ -553,7 +564,7 @@ static void tx_transfer_free_payload(const udpard_tx_mem_resources_t mem, tx_tra
553564static void tx_transfer_free (udpard_tx_t * const tx , tx_transfer_t * const tr )
554565{
555566 UDPARD_ASSERT (tr != NULL );
556- tx_transfer_free_payload (tx -> memory , tr );
567+ tx_transfer_free_payload (tx , tr );
557568 // Remove the transfer from all indexes.
558569 delist (& tx -> queue [tr -> priority ], & tr -> queue );
559570 if (cavl2_is_inserted (tx -> index_staged , & tr -> index_staged )) {
@@ -627,6 +638,7 @@ static tx_frame_t* tx_spool(const udpard_tx_mem_resources_t memory,
627638 break ;
628639 }
629640 // Populate the frame contents.
641+ tail -> refcount = 1 ;
630642 tail -> next = NULL ;
631643 const byte_t * const read_ptr = ((const byte_t * )payload .data ) + offset ;
632644 prefix_crc = crc_add (prefix_crc , progress , read_ptr );
@@ -735,7 +747,7 @@ static void tx_receive_ack(udpard_rx_t* const rx,
735747 .attempts = tr -> attempts ,
736748 .success = true,
737749 };
738- tx_transfer_free_payload (tx -> memory , tr ); // do this early to release memory before callback
750+ tx_transfer_free_payload (tx , tr ); // do this early to release memory before callback
739751 tr -> feedback (tx , fb );
740752 tx_transfer_free (tx , tr );
741753 }
@@ -861,7 +873,7 @@ static void tx_purge_expired(udpard_tx_t* const self, const udpard_us_t now)
861873 .attempts = tr -> attempts ,
862874 .success = false,
863875 };
864- tx_transfer_free_payload (self -> memory , tr ); // do this early to release memory before callback
876+ tx_transfer_free_payload (self , tr ); // do this early to release memory before callback
865877 if (tr -> feedback != NULL ) {
866878 tr -> feedback (self , fb );
867879 }
@@ -914,19 +926,14 @@ static void tx_eject_pending(udpard_tx_t* const self, const udpard_us_t now)
914926 tx_frame_t * const frame_next = frame -> next ;
915927 const bool last_attempt = tr -> deadline <= tr -> retry_at ;
916928 const bool last_frame = frame_next == NULL ; // if not last attempt we will have to rewind to head
917- const size_t frame_size = last_frame ? tr -> mtu_last : tr -> mtu ;
918- // Transfer ownership to the application if no further attempts will be made to reduce queue/memory pressure.
919- const udpard_bytes_mut_t frame_origin = { .size = last_attempt ? tx_frame_object_size (frame_size ) : 0U ,
920- .data = last_attempt ? frame : NULL };
921929
922930 // Eject the frame.
923931 const udpard_tx_ejection_t ejection = {
924932 .now = now ,
925933 .deadline = tr -> deadline ,
926934 .dscp = self -> dscp_value_per_priority [tr -> priority ],
927935 .destination = tr -> destination ,
928- .datagram_view = { .size = HEADER_SIZE_BYTES + frame_size , .data = frame -> data },
929- .datagram_origin = frame_origin ,
936+ .datagram = tx_frame_view (frame , last_frame ? tr -> mtu_last : tr -> mtu ),
930937 .user_transfer_reference = tr -> user_transfer_reference ,
931938 };
932939 if (!self -> vtable -> eject (self , ejection )) { // The easy case -- no progress was made at this time;
@@ -937,8 +944,10 @@ static void tx_eject_pending(udpard_tx_t* const self, const udpard_us_t now)
937944 if (last_attempt ) { // no need to keep frames that we will no longer use; free early to reduce pressure
938945 UDPARD_ASSERT (tr -> head == tr -> cursor ); // They go together on the last attempt.
939946 tr -> head = frame_next ;
940- self -> enqueued_frames_count -- ; // Ownership transferred to the application.
947+ udpard_tx_refcount_dec (self , ejection .datagram );
948+ self -> enqueued_frames_count -- ;
941949 }
950+ tr -> cursor = frame_next ;
942951
943952 // Finalize the transmission if this was the last frame of the transfer.
944953 if (last_frame ) {
@@ -954,8 +963,6 @@ static void tx_eject_pending(udpard_tx_t* const self, const udpard_us_t now)
954963 cavl2_find_or_insert (
955964 & self -> index_staged , & tr -> retry_at , tx_cavl_compare_staged , & tr -> index_staged , cavl2_trivial_factory );
956965 }
957- } else {
958- tr -> cursor = frame_next ;
959966 }
960967 }
961968}
@@ -969,6 +976,27 @@ void udpard_tx_poll(udpard_tx_t* const self, const udpard_us_t now)
969976 }
970977}
971978
979+ void udpard_tx_refcount_inc (udpard_tx_t * const self , const udpard_bytes_t datagram )
980+ {
981+ if ((self != NULL ) && (datagram .data != NULL )) {
982+ tx_frame_t * const frame = tx_frame_from_view (datagram );
983+ UDPARD_ASSERT (frame -> refcount > 0 ); // NOLINT(*ArrayBound)
984+ frame -> refcount ++ ;
985+ }
986+ }
987+
988+ void udpard_tx_refcount_dec (udpard_tx_t * const self , const udpard_bytes_t datagram )
989+ {
990+ if ((self != NULL ) && (datagram .data != NULL )) {
991+ tx_frame_t * const frame = tx_frame_from_view (datagram );
992+ UDPARD_ASSERT (frame -> refcount > 0 ); // NOLINT(*ArrayBound)
993+ frame -> refcount -- ;
994+ if (frame -> refcount == 0U ) {
995+ mem_free (self -> memory .payload , tx_frame_object_size (datagram .size ), frame );
996+ }
997+ }
998+ }
999+
9721000void udpard_tx_free (udpard_tx_t * const self )
9731001{
9741002 if (self != NULL ) {
0 commit comments