LCOV - code coverage report
Current view: top level - disco/bundle - fd_keepalive.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 34 36 94.4 %
Date: 2025-08-05 05:04:49 Functions: 10 24 41.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_bundle_fd_keepalive_h
       2             : #define HEADER_fd_src_disco_bundle_fd_keepalive_h
       3             : 
       4             : /* fd_keepalive.h provides an API to periodically generate keepalive
       5             :    events.  The general usage is as follows:
       6             :    - Configure a target keepalive interval and timeout
       7             :    - Periodically check back whether a keepalive is due, or whether a
       8             :      keepalive timed out
       9             :    - Notify the object whenever a keepalive was sent or a keepalive ACK
      10             :      was received. */
      11             : 
      12             : #include "../../util/rng/fd_rng.h"
      13             : 
      14             : struct fd_keepalive {
      15             :   long ts_next_tx;   /* Timestamp when to send next ping */
      16             :   long ts_deadline;  /* Timestamp by which ping ACK is expected to be received */
      17             :   long interval;
      18             :   long timeout;
      19             : 
      20             :   long ts_last_tx;   /* Timestamp of last ping sent */
      21             :   long ts_last_rx;   /* Timestamp of last ping ACK received */
      22             : 
      23             :   uint inflight : 1;
      24             : };
      25             : 
      26             : typedef struct fd_keepalive fd_keepalive_t;
      27             : 
      28             : FD_PROTOTYPES_BEGIN
      29             : 
      30             : static inline long
      31             : fd_keepalive_interval_reload( fd_rng_t * rng,
      32          48 :                               long       interval ) {
      33          48 :   long i2 = interval>>1;
      34          48 :   return i2 + (long)fd_rng_ulong_roll( rng, (ulong)i2 );
      35          48 : }
      36             : 
      37             : static inline fd_keepalive_t *
      38             : fd_keepalive_init( fd_keepalive_t * ka,
      39             :                    fd_rng_t *       rng,
      40             :                    long             interval,
      41             :                    long             timeout,
      42          42 :                    long             now ) {
      43          42 :   memset( ka, 0, sizeof(fd_keepalive_t) );
      44          42 :   if( FD_UNLIKELY( interval<2L || !timeout ) ) return NULL;
      45          42 :   ka->interval = interval;
      46          42 :   ka->timeout  = timeout;
      47          42 :   ka->ts_next_tx = now + (long)fd_keepalive_interval_reload( rng, ka->interval );
      48          42 :   return ka;
      49          42 : }
      50             : 
      51             : /* fd_keepalive_should_tx returns 1 if the caller should send out a new
      52             :    keepalive probe.  Otherwise, returns 0.  Always returns 0 if interval
      53             :    is zero (thus has no-op behavior for a zeroed keepalive struct). */
      54             : 
      55             : static inline int
      56             : fd_keepalive_should_tx( fd_keepalive_t const * ka,
      57          57 :                         long                   now ) {
      58          57 :   return (!!ka->interval) & (ka->ts_next_tx <= now) & (!ka->inflight);
      59          57 : }
      60             : 
      61             : static inline void
      62             : fd_keepalive_tx( fd_keepalive_t * ka,
      63             :                  fd_rng_t *       rng,
      64           6 :                  long             now ) {
      65           6 :   long delay = (long)fd_keepalive_interval_reload( rng, ka->interval );
      66           6 :   ka->ts_last_tx  = now;
      67           6 :   ka->ts_next_tx += delay;
      68           6 :   if( FD_UNLIKELY( ka->ts_next_tx < now ) ) {
      69           0 :     ka->ts_next_tx = now + delay;
      70           0 :   }
      71           6 :   ka->ts_deadline = ka->ts_last_tx + ka->timeout;
      72           6 :   ka->inflight    = 1;
      73           6 : }
      74             : 
      75             : static inline int
      76             : fd_keepalive_is_timeout( fd_keepalive_t const * ka,
      77         195 :                          long                   now ) {
      78         195 :   return (!!ka->inflight) & (ka->ts_deadline <= now);
      79         195 : }
      80             : 
      81             : static inline long
      82             : fd_keepalive_rx( fd_keepalive_t * ka,
      83           3 :                  long             now ) {
      84           3 :   long rtt = now - ka->ts_last_tx;
      85           3 :   if( !ka->inflight ) rtt = 0L;
      86           3 :   ka->ts_deadline = 0L;
      87           3 :   ka->ts_last_rx  = now;
      88           3 :   ka->inflight    = 0;
      89           3 :   return rtt;
      90           3 : }
      91             : 
      92             : FD_PROTOTYPES_END
      93             : 
      94             : #endif /* HEADER_fd_src_disco_bundle_fd_keepalive_h */

Generated by: LCOV version 1.14