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 */