Line data Source code
1 : #ifndef HEADER_fd_src_disco_node_info_fd_node_info_h 2 : #define HEADER_fd_src_disco_node_info_fd_node_info_h 3 : 4 : /* fd_node_info provides a shared topology object that holds 5 : validator-level information which cannot be represented as simple 6 : scalar metrics (e.g. 32-byte public keys and hashes). 7 : 8 : The replay tile (identity, genesis hash) and the tower tile (vote 9 : account) write it. The watch command and other consumers read it in 10 : a lock-free manner. 11 : 12 : The seqlock read/write helpers require atomics. */ 13 : 14 : #include "../../util/log/fd_log.h" 15 : #include "../../flamenco/fd_flamenco_base.h" 16 : 17 : #include <stdatomic.h> 18 : 19 0 : #define FD_NODE_INFO_MAGIC (0xf17eda2c4e490000UL) /* firedancer ni ver 0 */ 20 : 21 : struct fd_node_info { 22 : /* fields are zero until known */ 23 : fd_pubkey_t vote_account; 24 : fd_pubkey_t identity; 25 : fd_hash_t genesis_hash; 26 : }; 27 : 28 : typedef struct fd_node_info fd_node_info_t; 29 : 30 : struct fd_node_info_box { 31 : ulong magic; /* ==FD_NODE_INFO_MAGIC */ 32 : _Atomic uint seq_lock; /* lsb==1 implies active write */ 33 : 34 : fd_node_info_t info; 35 : }; 36 : 37 : typedef struct fd_node_info_box fd_node_info_box_t; 38 : 39 : FD_PROTOTYPES_BEGIN 40 : 41 : static inline void * 42 0 : fd_node_info_box_new( void * shmem ) { 43 0 : fd_node_info_box_t * ni = (fd_node_info_box_t *)shmem; 44 0 : if( FD_UNLIKELY( !ni ) ) { 45 0 : FD_LOG_WARNING(( "NULL shmem" )); 46 0 : return NULL; 47 0 : } 48 0 : fd_memset( ni, 0, sizeof(fd_node_info_box_t) ); 49 0 : FD_VOLATILE( ni->magic ) = FD_NODE_INFO_MAGIC; 50 0 : return (void *)ni; 51 0 : } 52 : 53 : static inline fd_node_info_box_t * 54 0 : fd_node_info_box_join( void * shni ) { 55 0 : if( FD_UNLIKELY( !shni ) ) { 56 0 : FD_LOG_WARNING(( "NULL shni" )); 57 0 : return NULL; 58 0 : } 59 0 : fd_node_info_box_t * ni = (fd_node_info_box_t *)shni; 60 0 : if( FD_UNLIKELY( ni->magic!=FD_NODE_INFO_MAGIC ) ) { 61 0 : FD_LOG_WARNING(( "bad magic" )); 62 0 : return NULL; 63 0 : } 64 0 : return ni; 65 0 : } 66 : 67 : /* fd_node_info_read does an atomic read of a shared fd_node_info_t. */ 68 : 69 : static inline fd_node_info_t * 70 : fd_node_info_read( fd_node_info_t * dst, 71 0 : fd_node_info_box_t const * src ) { 72 0 : for(;;) { 73 0 : uint lock0 = atomic_load_explicit( &src->seq_lock, memory_order_acquire ); 74 0 : memcpy( dst, &src->info, sizeof(fd_node_info_t) ); 75 0 : atomic_thread_fence( memory_order_acquire ); 76 0 : uint lock1 = atomic_load_explicit( &src->seq_lock, memory_order_relaxed ); 77 0 : if( FD_LIKELY( lock0==lock1 && !(lock0 & 1U) ) ) break; 78 0 : FD_SPIN_PAUSE(); 79 0 : } 80 0 : return dst; 81 0 : } 82 : 83 : /* fd_node_info_write_{begin,end} are used to gain exclusive access to a 84 : node_info for writes. */ 85 : 86 : static inline void 87 0 : fd_node_info_write_begin( fd_node_info_box_t * dst ) { 88 0 : for(;;) { 89 0 : uint lock = atomic_load_explicit( &dst->seq_lock, memory_order_relaxed ); 90 0 : if( FD_LIKELY( !(lock & 1U) && 91 0 : atomic_compare_exchange_weak_explicit( &dst->seq_lock, &lock, lock+1U, 92 0 : memory_order_acquire, memory_order_relaxed ) ) ) { 93 0 : break; 94 0 : } 95 0 : FD_SPIN_PAUSE(); 96 0 : } 97 0 : } 98 : 99 : static inline void 100 0 : fd_node_info_write_end( fd_node_info_box_t * dst ) { 101 : atomic_fetch_add_explicit( &dst->seq_lock, 1U, memory_order_release ); 102 0 : } 103 : 104 : FD_PROTOTYPES_END 105 : 106 : #endif /* HEADER_fd_src_disco_node_info_fd_node_info_h */