LCOV - code coverage report
Current view: top level - waltz/quic - fd_quic_svc_q.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 162 170 95.3 %
Date: 2025-10-27 04:40:00 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    18712162 : #define PRQ_T    fd_quic_svc_event_t
       9    18411655 : #define PRQ_TMP_ST(p,t) do { \
      10    18411655 :                          (p)[0] = (t); \
      11    18411655 :                          t.conn->svc_meta.private.prq_idx = (ulong)((p)-heap); \
      12    18411655 :                        } while( 0 )
      13    18711622 : #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       11040 : fd_quic_svc_timers_footprint( ulong max_conn ) {
      22       11040 :   ulong l = FD_LAYOUT_INIT;
      23       11040 :   l = FD_LAYOUT_APPEND( l, alignof(fd_quic_svc_timers_t), sizeof(fd_quic_svc_timers_t) );
      24       11040 :   l = FD_LAYOUT_APPEND( l, fd_quic_svc_queue_prq_align(), fd_quic_svc_queue_prq_footprint( max_conn ) );
      25       11040 :   l = FD_LAYOUT_FINI(   l, fd_quic_svc_timers_align() );
      26       11040 :   return l;
      27       11040 : }
      28             : 
      29             : ulong
      30       36516 : fd_quic_svc_timers_align( void ) {
      31       36516 :   return fd_ulong_max( alignof( fd_quic_svc_timers_t ),
      32       36516 :                       fd_quic_svc_queue_prq_align() );
      33       36516 : }
      34             : 
      35             : fd_quic_svc_timers_t *
      36             : fd_quic_svc_timers_init( void            * mem,
      37             :                          ulong             max_conn,
      38        3396 :                          fd_quic_state_t * state ) {
      39        3396 :   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        3396 :   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        3396 :   FD_SCRATCH_ALLOC_INIT( l, mem );
      50        3396 :   fd_quic_svc_timers_t * timers  = FD_SCRATCH_ALLOC_APPEND( l,
      51        3396 :                                                             alignof(fd_quic_svc_timers_t),
      52        3396 :                                                             sizeof(fd_quic_svc_timers_t) );
      53        3396 :   uchar                * prq_mem = FD_SCRATCH_ALLOC_APPEND( l,
      54        3396 :                                                             fd_quic_svc_queue_prq_align(),
      55        3396 :                                                             fd_quic_svc_queue_prq_footprint( max_conn ) );
      56             : 
      57             : 
      58           0 :   timers->prq = fd_quic_svc_queue_prq_join( fd_quic_svc_queue_prq_new( prq_mem, max_conn ) );
      59        3396 :   if( FD_UNLIKELY( !timers->prq ) ) FD_LOG_ERR(( "fd_quic_svc_timers_init failed to join prq" ));
      60             : 
      61        3396 :   timers->instant.cnt  = 0U;
      62        3396 :   timers->instant.head = FD_QUIC_SVC_DLIST_IDX_INVAL;
      63        3396 :   timers->instant.tail = FD_QUIC_SVC_DLIST_IDX_INVAL;
      64             : 
      65        3396 :   timers->state = state;
      66             : 
      67        3396 :   return timers;
      68        3396 : }
      69             : 
      70             : void
      71    18492934 : fd_quic_svc_timers_init_conn( fd_quic_conn_t * conn ) {
      72    18492934 :   conn->svc_meta.next_timeout       = LONG_MAX;
      73    18492934 :   conn->svc_meta.private.prq_idx    = FD_QUIC_SVC_PRQ_IDX_INVAL;
      74    18492934 :   conn->svc_meta.private.svc_type   = FD_QUIC_SVC_CNT;
      75    18492934 : }
      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    17845237 :                                fd_quic_conn_t      * conn ) {
      85             : 
      86    17845237 :   uint             conn_idx  = conn->conn_idx;
      87    17845237 :   fd_quic_conn_t * tail_conn = fd_quic_conn_at_idx( state, queue->tail );
      88             : 
      89    17845237 :   *fd_ptr_if( !!queue->cnt, &tail_conn->svc_meta.private.dlist.next , &queue->head) = conn_idx ;
      90    17845237 :   conn->svc_meta.private.dlist.prev = queue->tail;
      91    17845237 :   conn->svc_meta.private.dlist.next = FD_QUIC_SVC_DLIST_IDX_INVAL;
      92    17845237 :   queue->tail         = conn_idx;
      93    17845237 :   queue->cnt++;
      94    17845237 : }
      95             : 
      96             : static inline void
      97             : fd_quic_svc_dlist_remove( fd_quic_svc_queue_t * queue,
      98             :                           fd_quic_state_t     * state,
      99    17845222 :                           fd_quic_conn_t      * conn ) {
     100    17845222 :   uint conn_idx = conn->conn_idx;
     101    17845222 :   uint qhead    = queue->head;
     102    17845222 :   uint qtail    = queue->tail;
     103             : 
     104    17845222 :   fd_quic_conn_t * prev_conn = fd_quic_conn_at_idx( state, conn->svc_meta.private.dlist.prev );
     105    17845222 :   fd_quic_conn_t * next_conn = fd_quic_conn_at_idx( state, conn->svc_meta.private.dlist.next );
     106             : 
     107    17845222 :   *fd_ptr_if( conn_idx == qhead, &queue->head , &prev_conn->svc_meta.private.dlist.next) = conn->svc_meta.private.dlist.next;
     108    17845222 :   *fd_ptr_if( conn_idx == qtail, &queue->tail , &next_conn->svc_meta.private.dlist.prev) = conn->svc_meta.private.dlist.prev;
     109             : 
     110    17845222 :   conn->svc_meta.private.dlist.next = FD_QUIC_SVC_DLIST_IDX_INVAL;
     111    17845222 :   conn->svc_meta.private.dlist.prev = FD_QUIC_SVC_DLIST_IDX_INVAL;
     112    17845222 :   queue->cnt--;
     113    17845222 : }
     114             : 
     115             : /* TASK FUNCTIONS *************************************************/
     116             : 
     117             : void
     118             : fd_quic_svc_timers_cancel( fd_quic_svc_timers_t * timers,
     119       14337 :                            fd_quic_conn_t       * conn ) {
     120             : 
     121       14337 :   uint              svc_type = conn->svc_meta.private.svc_type;
     122       14337 :   fd_quic_state_t * state    = timers->state;
     123             : 
     124       14337 :   if( svc_type == FD_QUIC_SVC_INSTANT ) {
     125          39 :     fd_quic_svc_dlist_remove( &timers->instant, state, conn );
     126       14298 :   } else if( svc_type == FD_QUIC_SVC_DYNAMIC ) {
     127          84 :     fd_quic_svc_queue_prq_remove( timers->prq, conn->svc_meta.private.prq_idx );
     128          84 :   }
     129       14337 :   fd_quic_svc_timers_init_conn( conn );
     130       14337 : }
     131             : 
     132             : void
     133             : fd_quic_svc_timers_schedule( fd_quic_svc_timers_t * timers,
     134             :                              fd_quic_conn_t       * conn,
     135    71435871 :                              long                   now ) {
     136             : 
     137             :   /* if conn null or invalid, do not schedule */
     138    71435871 :   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       13212 :     return;
     142       13212 :   }
     143             : 
     144    71422659 :   fd_quic_state_t * state         = timers->state;
     145    71422659 :   long     const    expiry        = conn->svc_meta.next_timeout;
     146    71422659 :   uint     const    old_svc_type  = conn->svc_meta.private.svc_type;
     147             : 
     148    71422659 :   uint     const    new_svc_type  = expiry == now ? FD_QUIC_SVC_INSTANT : FD_QUIC_SVC_DYNAMIC;
     149    71422659 :   int      const    old_dynamic   = old_svc_type==FD_QUIC_SVC_DYNAMIC;
     150    71422659 :   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    71422659 :   ulong const prq_idx     = fd_ulong_if( both_dynamic, conn->svc_meta.private.prq_idx, 0 );
     154    71422659 :   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    71422659 :   int noop = (old_svc_type==FD_QUIC_SVC_INSTANT) | (both_dynamic & is_increase);
     158    71422659 :   if( noop ) return;
     159             : 
     160             :   /* Cancel existing DYNAMIC timer if it exists */
     161    36256622 :   if( old_dynamic ) {
     162    18109051 :     fd_quic_svc_queue_prq_remove( timers->prq, conn->svc_meta.private.prq_idx );
     163    18109051 :   }
     164             : 
     165             :   /* Schedule in appropriate queue */
     166    36256622 :   conn->svc_meta.private.svc_type = new_svc_type;
     167             : 
     168    36256622 :   if( new_svc_type==FD_QUIC_SVC_INSTANT ) {
     169    17845237 :     fd_quic_svc_dlist_insert_tail( &timers->instant, state, conn );
     170    18411385 :   } else {
     171             :     /* FD_QUIC_SVC_DYNAMIC - use heap */
     172    18411385 :     fd_quic_svc_event_t e = {
     173    18411385 :       .conn    = conn,
     174    18411385 :       .timeout = expiry
     175    18411385 :     };
     176    18411385 :     fd_quic_svc_queue_prq_insert( timers->prq, &e );
     177    18411385 :   }
     178    36256622 : }
     179             : 
     180             : int
     181             : fd_quic_svc_timers_validate( fd_quic_svc_timers_t * timers,
     182          60 :                              fd_quic_t            * quic ) {
     183          60 :   fd_quic_state_t * state = fd_quic_get_state( quic );
     184             : 
     185             :   /* Validate DYNAMIC queue (heap) */
     186          60 :   ulong prq_cnt = fd_quic_svc_queue_prq_cnt( timers->prq );
     187      300111 :   for( ulong i=0; i<prq_cnt; i++ ) {
     188      300051 :     fd_quic_svc_event_t * event = timers->prq + i;
     189      300051 :     fd_quic_conn_t      * conn  = event->conn;
     190             : 
     191             :     /* conn and idx match for dynamic queue */
     192      300051 :     if( FD_UNLIKELY( conn->svc_meta.private.prq_idx != i ) ) return 0;
     193      300051 :     if( FD_UNLIKELY( conn->svc_meta.private.svc_type != FD_QUIC_SVC_DYNAMIC ) ) return 0;
     194             : 
     195             :     /* conn in prq at most once */
     196      300051 :     if( FD_UNLIKELY( conn->visited ) ) return 0;
     197      300051 :     conn->visited = 1U;
     198      300051 :   }
     199             : 
     200             :   /* Validate dlist */
     201          60 :   ulong instant_cnt = 0U;
     202          60 :   uint  curr        = timers->instant.head;
     203          63 :   while( curr != FD_QUIC_SVC_DLIST_IDX_INVAL ) {
     204           3 :     fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, curr );
     205           3 :     if( FD_UNLIKELY( conn->svc_meta.private.svc_type != FD_QUIC_SVC_INSTANT ) ) return 0;
     206           3 :     if( FD_UNLIKELY( conn->visited ) ) return 0;
     207           3 :     conn->visited = 1U;
     208           3 :     curr = conn->svc_meta.private.dlist.next;
     209           3 :     instant_cnt++;
     210           3 :   }
     211          60 :   if( instant_cnt != timers->instant.cnt ) return 0;
     212             : 
     213             :   /* connections not in any queue should have INVALID idx */
     214          60 :   ulong const conn_cnt = quic->limits.conn_cnt;
     215      300558 :   for( ulong i=0; i<conn_cnt; i++ ) {
     216      300498 :     fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, i );
     217      300498 :     if( !conn->visited && conn->svc_meta.private.prq_idx != FD_QUIC_SVC_PRQ_IDX_INVAL ) return 0;
     218      300498 :   }
     219             : 
     220          60 :   return 1;
     221          60 : }
     222             : 
     223             : fd_quic_svc_event_t
     224             : fd_quic_svc_timers_next( fd_quic_svc_timers_t * timers,
     225             :                          long                   now,
     226   138884163 :                          int                    pop ) {
     227   138884163 :   fd_quic_svc_event_t next = { .timeout = LONG_MAX, .conn = NULL };
     228             : 
     229             :   /* Priority: INSTANT > DYNAMIC */
     230             : 
     231             :   /* Check INSTANT queue first */
     232   138884163 :   if( FD_LIKELY( timers->instant.cnt ) ) {
     233    17893318 :     fd_quic_conn_t * conn = fd_quic_conn_at_idx( timers->state, timers->instant.head );
     234    17893318 :     next = (fd_quic_svc_event_t){.conn = conn, .timeout = fd_long_min( now, conn->svc_meta.next_timeout )};
     235             : 
     236    17893318 :     if( FD_LIKELY( pop ) ) {
     237    17845183 :       fd_quic_svc_dlist_remove( &timers->instant, timers->state, conn );
     238    17845183 :       fd_quic_svc_timers_init_conn( conn );
     239    17845183 :     }
     240             : 
     241    17893318 :     return next;
     242    17893318 :   }
     243             : 
     244             :   /* Check DYNAMIC queue (heap) */
     245   120990845 :   if( !fd_quic_svc_queue_prq_cnt( timers->prq ) ) return next;
     246   120985490 :   else if( pop && now < timers->prq[0].timeout ) return next;
     247      105498 :   else {
     248      105498 :     next = timers->prq[0];
     249      105498 :     if( FD_LIKELY( pop ) ) {
     250        2214 :       fd_quic_svc_queue_prq_remove_min( timers->prq );
     251        2214 :       fd_quic_svc_timers_init_conn( next.conn );
     252        2214 :     }
     253      105498 :     return next;
     254      105498 :   }
     255   120990845 : }
     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        2091 :                               long                   now ) {
     261        2091 :   uint svc_type = conn->svc_meta.private.svc_type;
     262             : 
     263        2091 :   if( svc_type == FD_QUIC_SVC_INSTANT ) {
     264           0 :     return (fd_quic_svc_event_t){ .timeout = now, .conn = conn };
     265        2091 :   } else if (svc_type == FD_QUIC_SVC_DYNAMIC) {
     266        2088 :     ulong idx = conn->svc_meta.private.prq_idx;
     267        2088 :     return *(timers->prq + idx);
     268        2088 :   }
     269           3 :   return (fd_quic_svc_event_t){ .timeout = LONG_MAX, .conn = NULL };
     270        2091 : }
     271             : 
     272             : ulong
     273   156616255 : fd_quic_svc_timers_cnt_events( fd_quic_svc_timers_t * timers ) {
     274   156616255 :   return timers->instant.cnt +
     275   156616255 :          fd_quic_svc_queue_prq_cnt( timers->prq );
     276   156616255 : }

Generated by: LCOV version 1.14