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 : FD_STATIC_ASSERT( FD_HAS_ATOMIC, fd_node_info requires atomics ); 18 : 19 : #include <stdatomic.h> 20 : 21 0 : #define FD_NODE_INFO_MAGIC (0xf17eda2c4e490000UL) /* firedancer ni ver 0 */ 22 : 23 : struct fd_node_info { 24 : /* fields are zero until known */ 25 : fd_pubkey_t vote_account; 26 : fd_pubkey_t identity; 27 : fd_hash_t genesis_hash; 28 : }; 29 : 30 : typedef struct fd_node_info fd_node_info_t; 31 : 32 : struct fd_node_info_box { 33 : ulong magic; /* ==FD_NODE_INFO_MAGIC */ 34 : _Atomic uint seq_lock; /* lsb==1 implies active write */ 35 : 36 : fd_node_info_t info; 37 : }; 38 : 39 : typedef struct fd_node_info_box fd_node_info_box_t; 40 : 41 : FD_PROTOTYPES_BEGIN 42 : 43 : static inline void * 44 0 : fd_node_info_box_new( void * shmem ) { 45 0 : fd_node_info_box_t * ni = (fd_node_info_box_t *)shmem; 46 0 : if( FD_UNLIKELY( !ni ) ) { 47 0 : FD_LOG_WARNING(( "NULL shmem" )); 48 0 : return NULL; 49 0 : } 50 0 : fd_memset( ni, 0, sizeof(fd_node_info_box_t) ); 51 0 : FD_VOLATILE( ni->magic ) = FD_NODE_INFO_MAGIC; 52 0 : return (void *)ni; 53 0 : } 54 : 55 : static inline fd_node_info_box_t * 56 0 : fd_node_info_box_join( void * shni ) { 57 0 : if( FD_UNLIKELY( !shni ) ) { 58 0 : FD_LOG_WARNING(( "NULL shni" )); 59 0 : return NULL; 60 0 : } 61 0 : fd_node_info_box_t * ni = (fd_node_info_box_t *)shni; 62 0 : if( FD_UNLIKELY( ni->magic!=FD_NODE_INFO_MAGIC ) ) { 63 0 : FD_LOG_WARNING(( "bad magic" )); 64 0 : return NULL; 65 0 : } 66 0 : return ni; 67 0 : } 68 : 69 : #if FD_HAS_ATOMIC 70 : 71 : /* fd_node_info_read does an atomic read of a shared fd_node_info_t. */ 72 : 73 : static inline fd_node_info_t * 74 : fd_node_info_read( fd_node_info_t * dst, 75 0 : fd_node_info_box_t const * src ) { 76 0 : for(;;) { 77 0 : uint lock0 = atomic_load_explicit( &src->seq_lock, memory_order_acquire ); 78 0 : memcpy( dst, &src->info, sizeof(fd_node_info_t) ); 79 0 : atomic_thread_fence( memory_order_acquire ); 80 0 : uint lock1 = atomic_load_explicit( &src->seq_lock, memory_order_relaxed ); 81 0 : if( FD_LIKELY( lock0==lock1 && !(lock0 & 1U) ) ) break; 82 0 : FD_SPIN_PAUSE(); 83 0 : } 84 0 : return dst; 85 0 : } 86 : 87 : /* fd_node_info_write_{begin,end} are used to gain exclusive access to a 88 : node_info for writes. */ 89 : 90 : static inline void 91 0 : fd_node_info_write_begin( fd_node_info_box_t * dst ) { 92 0 : for(;;) { 93 0 : uint lock = atomic_load_explicit( &dst->seq_lock, memory_order_relaxed ); 94 0 : if( FD_LIKELY( !(lock & 1U) && 95 0 : atomic_compare_exchange_weak_explicit( &dst->seq_lock, &lock, lock+1U, 96 0 : memory_order_acquire, memory_order_relaxed ) ) ) { 97 0 : break; 98 0 : } 99 0 : FD_SPIN_PAUSE(); 100 0 : } 101 0 : } 102 : 103 : static inline void 104 0 : fd_node_info_write_end( fd_node_info_box_t * dst ) { 105 : atomic_fetch_add_explicit( &dst->seq_lock, 1U, memory_order_release ); 106 0 : } 107 : 108 : #endif /* FD_HAS_ATOMIC */ 109 : 110 : FD_PROTOTYPES_END 111 : 112 : #endif /* HEADER_fd_src_disco_node_info_fd_node_info_h */