LCOV - code coverage report
Current view: top level - waltz/quic - fd_quic_svc_q.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 160 170 94.1 %
Date: 2026-10-17 04:27:35 Functions: 12 12 100.0 %

          Line data    Source code
       1             : #include "fd_quic_svc_q.h"
       2             : #include "fd_quic_private.h"
       3             : #include "fd_quic_conn.h"
       4             : 
       5             : /* PRIVATE ************************************************/
       6             : 
       7             : #define PRQ_NAME fd_quic_svc_queue_prq
       8     8611718 : #define PRQ_T    fd_quic_svc_event_t
       9     8311202 : #define PRQ_TMP_ST(p,t) do { \
      10     8311202 :                          (p)[0] = (t); \
      11     8311202 :                          t.conn->svc_meta.private.prq_idx = (ulong)((p)-heap); \
      12     8311202 :                        } while( 0 )
      13     8611169 : #define PRQ_TIMEOUT_T long
      14             : #include "../../util/tmpl/fd_prq.c"
      15             : typedef fd_quic_svc_event_t fd_quic_svc_queue_prq_t;
      16             : 
      17             : 
      18             : /* SETUP FUNCTIONS *************************************************/
      19             : 
      20             : ulong
      21         711 : fd_quic_svc_timers_footprint( ulong max_conn ) {
      22         711 :   ulong l = FD_LAYOUT_INIT;
      23         711 :   l = FD_LAYOUT_APPEND( l, alignof(fd_quic_svc_timers_t), sizeof(fd_quic_svc_timers_t) );
      24         711 :   l = FD_LAYOUT_APPEND( l, fd_quic_svc_queue_prq_align(), fd_quic_svc_queue_prq_footprint( max_conn ) );
      25         711 :   l = FD_LAYOUT_FINI(   l, fd_quic_svc_timers_align() );
      26         711 :   return l;
      27         711 : }
      28             : 
      29             : ulong
      30        2454 : fd_quic_svc_timers_align( void ) {
      31        2454 :   return fd_ulong_max( alignof( fd_quic_svc_timers_t ),
      32        2454 :                       fd_quic_svc_queue_prq_align() );
      33        2454 : }
      34             : 
      35             : fd_quic_svc_timers_t *
      36             : fd_quic_svc_timers_init( void            * mem,
      37             :                          ulong             max_conn,
      38         321 :                          fd_quic_state_t * state ) {
      39         321 :   if( FD_UNLIKELY( !mem ) ) {
      40           0 :     FD_LOG_ERR(( "fd_quic_svc_timers_init called with NULL mem" ));
      41           0 :     return NULL;
      42           0 :   }
      43             : 
      44         321 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_quic_svc_timers_align() ) ) ) {
      45           0 :     FD_LOG_ERR(( "fd_quic_svc_timers_init called with misaligned mem" ));
      46           0 :     return NULL;
      47           0 :   }
      48             : 
      49         321 :   FD_SCRATCH_ALLOC_INIT( l, mem );
      50         321 :   fd_quic_svc_timers_t * timers  = FD_SCRATCH_ALLOC_APPEND( l,
      51         321 :                                                             alignof(fd_quic_svc_timers_t),
      52         321 :                                                             sizeof(fd_quic_svc_timers_t) );
      53         321 :   uchar                * prq_mem = FD_SCRATCH_ALLOC_APPEND( l,
      54         321 :                                                             fd_quic_svc_queue_prq_align(),
      55         321 :                                                             fd_quic_svc_queue_prq_footprint( max_conn ) );
      56             : 
      57             : 
      58         321 :   timers->prq = fd_quic_svc_queue_prq_join( fd_quic_svc_queue_prq_new( prq_mem, max_conn ) );
      59         321 :   if( FD_UNLIKELY( !timers->prq ) ) FD_LOG_ERR(( "fd_quic_svc_timers_init failed to join prq" ));
      60             : 
      61         321 :   timers->instant.cnt  = 0U;
      62         321 :   timers->instant.head = FD_QUIC_SVC_DLIST_IDX_INVAL;
      63         321 :   timers->instant.tail = FD_QUIC_SVC_DLIST_IDX_INVAL;
      64             : 
      65         321 :   timers->state = state;
      66             : 
      67         321 :   return timers;
      68         321 : }
      69             : 
      70             : void
      71     8520235 : fd_quic_svc_timers_init_conn( fd_quic_conn_t * conn ) {
      72     8520235 :   conn->svc_meta.next_timeout       = LONG_MAX;
      73     8520235 :   conn->svc_meta.private.prq_idx    = FD_QUIC_SVC_PRQ_IDX_INVAL;
      74     8520235 :   conn->svc_meta.private.svc_type   = FD_QUIC_SVC_CNT;
      75     8520235 : }
      76             : 
      77             : /* END SETUP FUNCTIONS *********************************************/
      78             : 
      79             : /* DLIST HELPER FUNCTIONS *******************************************/
      80             : 
      81             : static inline void
      82             : fd_quic_svc_dlist_insert_tail( fd_quic_svc_queue_t * queue,
      83             :                                fd_quic_state_t     * state,
      84     7894087 :                                fd_quic_conn_t      * conn ) {
      85             : 
      86     7894087 :   uint             conn_idx  = conn->conn_idx;
      87     7894087 :   fd_quic_conn_t * tail_conn = fd_quic_conn_at_idx( state, queue->tail );
      88             : 
      89     7894087 :   *fd_ptr_if( !!queue->cnt, &tail_conn->svc_meta.private.dlist.next , &queue->head) = conn_idx ;
      90     7894087 :   conn->svc_meta.private.dlist.prev = queue->tail;
      91     7894087 :   conn->svc_meta.private.dlist.next = FD_QUIC_SVC_DLIST_IDX_INVAL;
      92     7894087 :   queue->tail         = conn_idx;
      93     7894087 :   queue->cnt++;
      94     7894087 : }
      95             : 
      96             : static inline void
      97             : fd_quic_svc_dlist_remove( fd_quic_svc_queue_t * queue,
      98             :                           fd_quic_state_t     * state,
      99     7894003 :                           fd_quic_conn_t      * conn ) {
     100     7894003 :   uint conn_idx = conn->conn_idx;
     101     7894003 :   uint qhead    = queue->head;
     102     7894003 :   uint qtail    = queue->tail;
     103             : 
     104     7894003 :   fd_quic_conn_t * prev_conn = fd_quic_conn_at_idx( state, conn->svc_meta.private.dlist.prev );
     105     7894003 :   fd_quic_conn_t * next_conn = fd_quic_conn_at_idx( state, conn->svc_meta.private.dlist.next );
     106             : 
     107     7894003 :   *fd_ptr_if( conn_idx == qhead, &queue->head , &prev_conn->svc_meta.private.dlist.next) = conn->svc_meta.private.dlist.next;
     108     7894003 :   *fd_ptr_if( conn_idx == qtail, &queue->tail , &next_conn->svc_meta.private.dlist.prev) = conn->svc_meta.private.dlist.prev;
     109             : 
     110     7894003 :   conn->svc_meta.private.dlist.next = FD_QUIC_SVC_DLIST_IDX_INVAL;
     111     7894003 :   conn->svc_meta.private.dlist.prev = FD_QUIC_SVC_DLIST_IDX_INVAL;
     112     7894003 :   queue->cnt--;
     113     7894003 : }
     114             : 
     115             : /* TASK FUNCTIONS *************************************************/
     116             : 
     117             : void
     118             : fd_quic_svc_timers_cancel( fd_quic_svc_timers_t * timers,
     119       12237 :                            fd_quic_conn_t       * conn ) {
     120             : 
     121       12237 :   uint              svc_type = conn->svc_meta.private.svc_type;
     122       12237 :   fd_quic_state_t * state    = timers->state;
     123             : 
     124       12237 :   if( svc_type == FD_QUIC_SVC_INSTANT ) {
     125          90 :     fd_quic_svc_dlist_remove( &timers->instant, state, conn );
     126       12147 :   } else if( svc_type == FD_QUIC_SVC_DYNAMIC ) {
     127          99 :     fd_quic_svc_queue_prq_remove( timers->prq, conn->svc_meta.private.prq_idx );
     128          99 :   }
     129       12237 :   fd_quic_svc_timers_init_conn( conn );
     130       12237 : }
     131             : 
     132             : void
     133             : fd_quic_svc_timers_schedule( fd_quic_svc_timers_t * timers,
     134             :                              fd_quic_conn_t       * conn,
     135    31781439 :                              long                   now ) {
     136             : 
     137             :   /* if conn null or invalid, do not schedule */
     138    31781439 :   if( FD_UNLIKELY( !conn || conn->state == FD_QUIC_CONN_STATE_INVALID ) ) {
     139             :     /* cleaner/safer to check in here for now. If function call overhead
     140             :        becomes a constraint, move check to caller */
     141        6009 :     return;
     142        6009 :   }
     143             : 
     144    31775430 :   fd_quic_state_t * state         = timers->state;
     145    31775430 :   long     const    expiry        = conn->svc_meta.next_timeout;
     146    31775430 :   uint     const    old_svc_type  = conn->svc_meta.private.svc_type;
     147             : 
     148    31775430 :   uint     const    new_svc_type  = expiry == now ? FD_QUIC_SVC_INSTANT : FD_QUIC_SVC_DYNAMIC;
     149    31775430 :   int      const    old_dynamic   = old_svc_type==FD_QUIC_SVC_DYNAMIC;
     150    31775430 :   int      const    both_dynamic  = old_dynamic & (new_svc_type==FD_QUIC_SVC_DYNAMIC);
     151             : 
     152             :   /* Speculative is_increase is invalid when !both_dynamic, but safe bc prq_idx==0 */
     153    31775430 :   ulong const prq_idx     = fd_ulong_if( both_dynamic, conn->svc_meta.private.prq_idx, 0 );
     154    31775430 :   int   const is_increase = timers->prq[prq_idx].timeout <= expiry;
     155             : 
     156             :   /* No-op if already INSTANT, or if trying to increase/preserve DYNAMIC expiry */
     157    31775430 :   int noop = (old_svc_type==FD_QUIC_SVC_INSTANT) | (both_dynamic & is_increase);
     158    31775430 :   if( noop ) return;
     159             : 
     160             :   /* Cancel existing DYNAMIC timer if it exists */
     161    16205016 :   if( old_dynamic ) {
     162     8010566 :     fd_quic_svc_queue_prq_remove( timers->prq, conn->svc_meta.private.prq_idx );
     163     8010566 :   }
     164             : 
     165             :   /* Schedule in appropriate queue */
     166    16205016 :   conn->svc_meta.private.svc_type = new_svc_type;
     167             : 
     168    16205016 :   if( new_svc_type==FD_QUIC_SVC_INSTANT ) {
     169     7894087 :     fd_quic_svc_dlist_insert_tail( &timers->instant, state, conn );
     170     8310929 :   } else {
     171             :     /* FD_QUIC_SVC_DYNAMIC - use heap */
     172     8310929 :     fd_quic_svc_event_t e = {
     173     8310929 :       .conn    = conn,
     174     8310929 :       .timeout = expiry
     175     8310929 :     };
     176     8310929 :     fd_quic_svc_queue_prq_insert( timers->prq, &e );
     177     8310929 :   }
     178    16205016 : }
     179             : 
     180             : int
     181             : fd_quic_svc_timers_validate( fd_quic_svc_timers_t * timers,
     182         126 :                              fd_quic_t            * quic ) {
     183         126 :   fd_quic_state_t * state = fd_quic_get_state( quic );
     184             : 
     185             :   /* Validate DYNAMIC queue (heap) */
     186         126 :   ulong prq_cnt = fd_quic_svc_queue_prq_cnt( timers->prq );
     187      300192 :   for( ulong i=0; i<prq_cnt; i++ ) {
     188      300066 :     fd_quic_svc_event_t * event = timers->prq + i;
     189      300066 :     fd_quic_conn_t      * conn  = event->conn;
     190             : 
     191             :     /* conn and idx match for dynamic queue */
     192      300066 :     if( FD_UNLIKELY( conn->svc_meta.private.prq_idx != i ) ) return 0;
     193      300066 :     if( FD_UNLIKELY( conn->svc_meta.private.svc_type != FD_QUIC_SVC_DYNAMIC ) ) return 0;
     194             : 
     195             :     /* conn in prq at most once */
     196      300066 :     if( FD_UNLIKELY( conn->visited ) ) return 0;
     197      300066 :     conn->visited = 1U;
     198      300066 :   }
     199             : 
     200             :   /* Validate dlist */
     201         126 :   ulong instant_cnt = 0U;
     202         126 :   uint  curr        = timers->instant.head;
     203         180 :   while( curr != FD_QUIC_SVC_DLIST_IDX_INVAL ) {
     204          54 :     fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, curr );
     205          54 :     if( FD_UNLIKELY( conn->svc_meta.private.svc_type != FD_QUIC_SVC_INSTANT ) ) return 0;
     206          54 :     if( FD_UNLIKELY( conn->visited ) ) return 0;
     207          54 :     conn->visited = 1U;
     208          54 :     curr = conn->svc_meta.private.dlist.next;
     209          54 :     instant_cnt++;
     210          54 :   }
     211         126 :   if( instant_cnt != timers->instant.cnt ) return 0;
     212             : 
     213             :   /* connections not in any queue should have INVALID idx */
     214         126 :   ulong const conn_cnt = quic->limits.conn_cnt;
     215      300888 :   for( ulong i=0; i<conn_cnt; i++ ) {
     216      300762 :     fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, i );
     217      300762 :     if( !conn->visited && conn->svc_meta.private.prq_idx != FD_QUIC_SVC_PRQ_IDX_INVAL ) return 0;
     218      300762 :   }
     219             : 
     220         126 :   return 1;
     221         126 : }
     222             : 
     223             : fd_quic_svc_event_t
     224             : fd_quic_svc_timers_next( fd_quic_svc_timers_t * timers,
     225             :                          long                   now,
     226   122643506 :                          int                    pop ) {
     227   122643506 :   fd_quic_svc_event_t next = { .timeout = LONG_MAX, .conn = NULL };
     228             : 
     229             :   /* Priority: INSTANT > DYNAMIC */
     230             : 
     231             :   /* Check INSTANT queue first */
     232   122643506 :   if( FD_LIKELY( timers->instant.cnt ) ) {
     233     7941940 :     fd_quic_conn_t * conn = fd_quic_conn_at_idx( timers->state, timers->instant.head );
     234     7941940 :     next = (fd_quic_svc_event_t){.conn = conn, .timeout = fd_long_min( now, conn->svc_meta.next_timeout )};
     235             : 
     236     7941940 :     if( FD_LIKELY( pop ) ) {
     237     7893913 :       fd_quic_svc_dlist_remove( &timers->instant, timers->state, conn );
     238     7893913 :       fd_quic_svc_timers_init_conn( conn );
     239     7893913 :     }
     240             : 
     241     7941940 :     return next;
     242     7941940 :   }
     243             : 
     244             :   /* Check DYNAMIC queue (heap) */
     245   114701566 :   if( !fd_quic_svc_queue_prq_cnt( timers->prq ) ) return next;
     246   114698077 :   else if( pop && now < timers->prq[0].timeout ) return next;
     247      144258 :   else {
     248      144258 :     next = timers->prq[0];
     249      144258 :     if( FD_LIKELY( pop ) ) {
     250         180 :       fd_quic_svc_queue_prq_remove_min( timers->prq );
     251         180 :       fd_quic_svc_timers_init_conn( next.conn );
     252         180 :     }
     253      144258 :     return next;
     254      144258 :   }
     255   114701566 : }
     256             : 
     257             : fd_quic_svc_event_t
     258             : fd_quic_svc_timers_get_event( fd_quic_svc_timers_t * timers,
     259             :                               fd_quic_conn_t       * conn,
     260           3 :                               long                   now ) {
     261           3 :   uint svc_type = conn->svc_meta.private.svc_type;
     262             : 
     263           3 :   if( svc_type == FD_QUIC_SVC_INSTANT ) {
     264           0 :     return (fd_quic_svc_event_t){ .timeout = now, .conn = conn };
     265           3 :   } else if (svc_type == FD_QUIC_SVC_DYNAMIC) {
     266           0 :     ulong idx = conn->svc_meta.private.prq_idx;
     267           0 :     return *(timers->prq + idx);
     268           0 :   }
     269           3 :   return (fd_quic_svc_event_t){ .timeout = LONG_MAX, .conn = NULL };
     270           3 : }
     271             : 
     272             : ulong
     273   130379982 : fd_quic_svc_timers_cnt_events( fd_quic_svc_timers_t * timers ) {
     274   130379982 :   return timers->instant.cnt +
     275   130379982 :          fd_quic_svc_queue_prq_cnt( timers->prq );
     276   130379982 : }

Generated by: LCOV version 1.14