@@ -658,15 +658,21 @@ static void mqtt_ng_destroy_tx_alias_hash(c_rhash hash)
658658 c_rhash_destroy (hash );
659659}
660660
661+ static void destroy_timeout_monitor_list (struct mqtt_ng_client * client )
662+ {
663+ spinlock_lock (& client -> pending_packets .spinlock );
664+ (void ) JudyLFreeArray (& client -> pending_packets .JudyL , PJE0 );
665+ spinlock_unlock (& client -> pending_packets .spinlock );
666+ __atomic_store_n (& client -> stats .packets_waiting_puback , 0 , __ATOMIC_RELAXED );
667+ }
668+
661669void mqtt_ng_destroy (struct mqtt_ng_client * client )
662670{
663671 transaction_buffer_destroy (& client -> main_buffer );
664672
665673 mqtt_ng_destroy_tx_alias_hash (client -> tx_topic_aliases .stoi_dict );
666674 mqtt_ng_destroy_rx_alias_hash (client -> rx_aliases );
667- spinlock_lock (& client -> pending_packets .spinlock );
668- (void ) JudyLFreeArray (& client -> pending_packets .JudyL , PJE0 );
669- spinlock_unlock (& client -> pending_packets .spinlock );
675+ destroy_timeout_monitor_list (client );
670676 freez (client );
671677}
672678
@@ -798,11 +804,12 @@ static int optimized_add(struct header_buffer *buf, void *data, size_t data_len,
798804 return 0 ;
799805}
800806
801- static void remove_packet_from_timeout_monitor_list (struct mqtt_ng_client * client , uint16_t packet_id )
807+ static void remove_packet_from_timeout_monitor_list_unsafe (struct mqtt_ng_client * client , uint16_t packet_id )
802808{
803- spinlock_lock (& client -> pending_packets .spinlock );
804- (void ) JudyLDel (& client -> pending_packets .JudyL , (Word_t ) packet_id , PJE0 );
805- spinlock_unlock (& client -> pending_packets .spinlock );
809+ int rc = JudyLDel (& client -> pending_packets .JudyL , (Word_t ) packet_id , PJE0 );
810+ // rc = 1 if the packer was deleted, so update statistics
811+ if (likely (rc ))
812+ __atomic_fetch_sub (& client -> stats .packets_waiting_puback , 1 , __ATOMIC_RELAXED );
806813}
807814
808815#define PACKET_TIMEOUT_EPOCH (1704067200L) // Jan 1, 2024 00:00:00 UTC
@@ -813,13 +820,15 @@ static void add_packet_to_timeout_monitor_list(struct mqtt_ng_client *client, ui
813820 time_t now = now_realtime_sec ();
814821 // Add it to the JudyL array
815822 uint32_t * Pvalue = (uint32_t * ) JudyLIns (& client -> pending_packets .JudyL , (Word_t ) packet_id , PJE0 );
816- if (! Pvalue || Pvalue == PJERR ) {
823+ if (Pvalue == PJERR ) {
817824 nd_log (NDLS_DAEMON , NDLP_ERR , "Error inserting packet_id (%" PRIu16 ") into JudyL array." , packet_id );
818825 spinlock_unlock (& client -> pending_packets .spinlock );
819826 return ;
820827 }
821828 * Pvalue = (uint32_t ) ((now - PACKET_TIMEOUT_EPOCH ) + PACKET_ACK_TIMEOUT_SECS );
822829 spinlock_unlock (& client -> pending_packets .spinlock );
830+
831+ __atomic_fetch_add (& client -> stats .packets_waiting_puback , 1 , __ATOMIC_RELAXED );
823832}
824833
825834#define TRY_GENERATE_MESSAGE (generator_function , ...) \
@@ -1016,6 +1025,9 @@ int mqtt_ng_connect(struct mqtt_ng_client *client,
10161025 buffer_purge (& client -> main_buffer .hdr_buffer );
10171026 UNLOCK_HDR_BUFFER (& client -> main_buffer );
10181027
1028+ if (clean_start )
1029+ destroy_timeout_monitor_list (client );
1030+
10191031 spinlock_lock (& client -> tx_topic_aliases .spinlock );
10201032 // according to MQTT spec topic aliases should not be persisted
10211033 // even if clean session is true
@@ -1159,13 +1171,15 @@ static void mark_message_for_gc(struct buffer_fragment *frag)
11591171static int mark_packet_acked (struct mqtt_ng_client * client , uint16_t packet_id )
11601172{
11611173 size_t reclaimable = 0 ;
1174+ spinlock_lock (& client -> pending_packets .spinlock );
11621175 LOCK_HDR_BUFFER (& client -> main_buffer );
11631176 struct buffer_fragment * frag = BUFFER_FIRST_FRAG (& client -> main_buffer .hdr_buffer );
11641177 while (frag ) {
11651178 if ( (frag -> flags & BUFFER_FRAG_MQTT_PACKET_HEAD ) && frag -> packet_id == packet_id ) {
11661179 if (!frag -> sent ) {
11671180 nd_log (NDLS_DAEMON , NDLP_ERR , "Received packet_id (%" PRIu16 ") belongs to MQTT packet which was not yet sent!" , packet_id );
11681181 UNLOCK_HDR_BUFFER (& client -> main_buffer );
1182+ spinlock_unlock (& client -> pending_packets .spinlock );
11691183 return 1 ;
11701184 }
11711185 usec_t latency = now_monotonic_usec () - frag -> sent_monotonic_ut ;
@@ -1178,7 +1192,8 @@ static int mark_packet_acked(struct mqtt_ng_client *client, uint16_t packet_id)
11781192 transaction_buffer_garbage_collect (& client -> main_buffer );
11791193
11801194 UNLOCK_HDR_BUFFER (& client -> main_buffer );
1181- remove_packet_from_timeout_monitor_list (client , packet_id );
1195+ remove_packet_from_timeout_monitor_list_unsafe (client , packet_id );
1196+ spinlock_unlock (& client -> pending_packets .spinlock );
11821197 return 0 ;
11831198 }
11841199
@@ -1189,25 +1204,40 @@ static int mark_packet_acked(struct mqtt_ng_client *client, uint16_t packet_id)
11891204 }
11901205 nd_log (NDLS_DAEMON , NDLP_ERR , "Received packet_id (%" PRIu16 ") is unknown!" , packet_id );
11911206 UNLOCK_HDR_BUFFER (& client -> main_buffer );
1207+ spinlock_unlock (& client -> pending_packets .spinlock );
11921208 return 1 ;
11931209}
11941210
1195- static void check_packet_monitor_list_for_timeouts (struct mqtt_ng_client * client )
1211+ #define MAX_TIMED_OUT_PACKETS (1024)
1212+
1213+ static bool check_packet_monitor_list_for_timeouts (struct mqtt_ng_client * client )
11961214{
1215+ uint16_t timed_out_packets [MAX_TIMED_OUT_PACKETS ];
1216+ size_t timed_out_count = 0 ;
1217+
11971218 spinlock_lock (& client -> pending_packets .spinlock );
11981219 bool first_then_next = true;
11991220 uint32_t * Pvalue ;
12001221 Word_t packet_id = 0 ;
12011222 time_t now = now_realtime_sec ();
1223+
12021224 while ((Pvalue = (uint32_t * ) JudyLFirstThenNext (client -> pending_packets .JudyL , & packet_id , & first_then_next ))) {
12031225 uint32_t expire_time_delta = * Pvalue ;
12041226 if (now >= (PACKET_TIMEOUT_EPOCH + expire_time_delta )) {
1205- spinlock_unlock (& client -> pending_packets .spinlock );
1206- (void ) mark_packet_acked (client , (uint16_t ) packet_id );
1207- spinlock_lock (& client -> pending_packets .spinlock );
1227+ if (timed_out_count < MAX_TIMED_OUT_PACKETS ) {
1228+ timed_out_packets [timed_out_count ++ ] = (uint16_t )packet_id ;
1229+ } else
1230+ break ;
12081231 }
12091232 }
12101233 spinlock_unlock (& client -> pending_packets .spinlock );
1234+
1235+ // Process timeouts outside the lock
1236+ for (size_t i = 0 ; i < timed_out_count ; i ++ ) {
1237+ mark_packet_acked (client , timed_out_packets [i ]);
1238+ }
1239+
1240+ return (timed_out_count == MAX_TIMED_OUT_PACKETS );
12111241}
12121242
12131243#define PUBLISH_SP_SIZE 64
@@ -1244,11 +1274,6 @@ int mqtt_ng_publish(struct mqtt_ng_client *client,
12441274 }
12451275
12461276 int rc = TRY_GENERATE_MESSAGE (mqtt_ng_generate_publish , topic , topic_free , msg , msg_free , msg_len , publish_flags , packet_id , topic_id );
1247- if (rc == MQTT_NG_MSGGEN_BUFFER_OOM ) {
1248- check_packet_monitor_list_for_timeouts (client );
1249- rc = TRY_GENERATE_MESSAGE (mqtt_ng_generate_publish , topic , topic_free , msg , msg_free , msg_len , publish_flags , packet_id , topic_id );
1250- }
1251-
12521277 if (rc == MQTT_NG_MSGGEN_OK )
12531278 add_packet_to_timeout_monitor_list (client , * packet_id );
12541279 return rc ;
@@ -1930,7 +1955,6 @@ static int parse_data(struct mqtt_ng_client *client)
19301955 }
19311956 parser -> state = MQTT_PARSE_MQTT_PACKET_DONE ;
19321957 ping_timeout = 0 ;
1933- check_packet_monitor_list_for_timeouts (client );
19341958 break ;
19351959 case MQTT_CPT_DISCONNECT :
19361960 rc = parse_disconnect_varhdr (client );
@@ -2171,6 +2195,8 @@ int handle_incoming_traffic(struct mqtt_ng_client *client)
21712195 return rc ;
21722196}
21732197
2198+ #define PACKET_TIMEOUT_REPEAT_CHECK (60)
2199+
21742200int mqtt_ng_sync (struct mqtt_ng_client * client )
21752201{
21762202 if (client -> client_state == MQTT_STATE_RAW || client -> client_state == MQTT_STATE_DISCONNECTED )
@@ -2179,6 +2205,15 @@ int mqtt_ng_sync(struct mqtt_ng_client *client)
21792205 if (client -> client_state == MQTT_STATE_ERROR )
21802206 return 1 ;
21812207
2208+ // Check for packet timeouts and cleanup
2209+ static time_t last_maintenance = 0 ;
2210+ if (now_realtime_sec () - last_maintenance >= PACKET_TIMEOUT_REPEAT_CHECK ) {
2211+ // if check packet returns true then we did max cleanup, possibly there are more packets to cleanup
2212+ // so do not update last_maintenance thus forcing check again
2213+ if (likely (!check_packet_monitor_list_for_timeouts (client )))
2214+ last_maintenance = now_realtime_sec ();
2215+ }
2216+
21822217 worker_is_busy (WORKER_ACLK_TRY_SEND_ALL );
21832218
21842219 LOCK_HDR_BUFFER (& client -> main_buffer );
0 commit comments