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

Generated by: LCOV version 1.14