@@ -1289,24 +1289,35 @@ static void rx_session_destroy(rx_session_t* const ses)
12891289 mem_free (sub -> owner -> mem .rx_session , sizeof (rx_session_t ), ses );
12901290}
12911291
1292- // Returns the most recently updated slot timestamp. At the same time purges stale slots to reclaim memory early.
1292+ typedef struct
1293+ {
1294+ canard_us_t latest_sof_at ;
1295+ byte_t in_progress_slots ;
1296+ } rx_session_state_t ;
1297+
1298+ // Checks the state and purges stale slots to reclaim memory early.
12931299// A slot is stale if it's been idle for a very long time that is usually much larger than the transfer-ID timeout.
1294- static canard_us_t rx_session_scan (rx_session_t * const ses , const canard_us_t now )
1300+ static rx_session_state_t rx_session_scan (rx_session_t * const ses , const canard_us_t now )
12951301{
1296- const canard_us_t deadline = now - later (RX_SESSION_TIMEOUT , ses -> owner -> transfer_id_timeout );
1297- canard_us_t latest = BIG_BANG ;
1302+ const canard_us_t deadline = now - later (RX_SESSION_TIMEOUT , ses -> owner -> transfer_id_timeout );
1303+ rx_session_state_t out = { . latest_sof_at = BIG_BANG , . in_progress_slots = 0 } ;
12981304 FOREACH_SLOT (i ) {
12991305 const rx_slot_t * const slot = ses -> slots [i ];
1300- if (slot != NULL ) {
1301- if (slot -> timestamp < deadline ) {
1302- rx_slot_destroy (ses -> owner , ses -> slots [i ]);
1303- ses -> slots [i ] = NULL ;
1304- } else {
1305- latest = later (latest , slot -> timestamp );
1306+ if (slot == NULL ) {
1307+ continue ;
1308+ }
1309+ CANARD_ASSERT (slot -> timestamp >= 0 );
1310+ if (slot -> timestamp < deadline ) { // Too old, destroy even if in progress -- unlikely to complete anyway.
1311+ rx_slot_destroy (ses -> owner , ses -> slots [i ]);
1312+ ses -> slots [i ] = NULL ;
1313+ } else {
1314+ out .latest_sof_at = later (out .latest_sof_at , slot -> timestamp );
1315+ if (slot -> total_payload_size > 0 ) {
1316+ out .in_progress_slots ++ ;
13061317 }
13071318 }
13081319 }
1309- return latest ;
1320+ return out ;
13101321}
13111322
13121323// Returns false on OOM, no other failure modes. Stores at most extent bytes.
@@ -1431,16 +1442,18 @@ void canard_poll(canard_t* const self, const uint_least8_t tx_ready_iface_bitmap
14311442
14321443 // Drop stale sessions to reclaim memory. This happens when remote peers cease sending data.
14331444 // The oldest is held alive until its session timeout has expired, but notice that it may be different
1434- // depending on the subscription instance if very large transfer-ID values are used (which is not expected) .
1445+ // depending on the subscription instance if large transfer-ID values are used.
14351446 // This means that a stale session that belongs to a subscription with a long timeout may keep other sessions
14361447 // with a shorter timeout alive beyond their expiration time.
14371448 // We accept this because it does not affect correctness (the transfer-ID timeout is checked on reception
14381449 // always); the only downside is that memory reclamation time is bounded in the worst case by the longest
14391450 // transfer-ID timeout among all subscriptions, but this is a reasonable tradeoff for the reduced complexity.
14401451 rx_session_t * const ses = LIST_HEAD (self -> rx .list_session_by_animation , rx_session_t , list_animation );
1441- if ((ses != NULL ) &&
1442- (rx_session_scan (ses , now ) < (now - later (RX_SESSION_TIMEOUT , ses -> owner -> transfer_id_timeout )))) {
1443- rx_session_destroy (ses );
1452+ if (ses != NULL ) {
1453+ const rx_session_state_t state = rx_session_scan (ses , now );
1454+ if ((state .in_progress_slots == 0 ) && (state .latest_sof_at < (now - ses -> owner -> transfer_id_timeout ))) {
1455+ rx_session_destroy (ses );
1456+ }
14441457 }
14451458
14461459 // Process the TX pipeline.
0 commit comments