Line data Source code
1 : #include "fd_accdb_lineage.h" 2 : #include "../../funk/fd_funk.h" 3 : 4 : void 5 : fd_accdb_lineage_set_fork_slow( fd_accdb_lineage_t * lineage, 6 : fd_funk_t const * funk, 7 240 : fd_funk_txn_xid_t const * xid ) { 8 240 : fd_funk_txn_xid_t next_xid = *xid; 9 240 : fd_funk_txn_t * tip = NULL; 10 : 11 : /* Walk transaction graph, recovering from overruns on-the-fly */ 12 240 : lineage->fork_depth = 0UL; 13 : 14 240 : ulong txn_max = fd_funk_txn_pool_ele_max( funk->txn_pool ); 15 240 : ulong i; 16 324 : for( i=0UL; i<lineage->max_depth; i++ ) { 17 324 : fd_funk_txn_map_query_t query[1]; 18 324 : fd_funk_txn_t * candidate; 19 324 : fd_funk_txn_xid_t found_xid; 20 324 : ulong parent_idx; 21 324 : fd_funk_txn_xid_t parent_xid; 22 324 : retry: 23 : /* Speculatively look up transaction from map */ 24 324 : for(;;) { 25 324 : int query_err = fd_funk_txn_map_query_try( funk->txn_map, &next_xid, NULL, query, 0 ); 26 324 : if( FD_UNLIKELY( query_err==FD_MAP_ERR_AGAIN ) ) { 27 : /* FIXME random backoff */ 28 0 : FD_SPIN_PAUSE(); 29 0 : continue; 30 0 : } 31 324 : if( query_err==FD_MAP_ERR_KEY ) goto done; 32 324 : if( FD_UNLIKELY( query_err!=FD_MAP_SUCCESS ) ) { 33 0 : FD_LOG_CRIT(( "fd_funk_txn_map_query_try failed: %i-%s", query_err, fd_map_strerror( query_err ) )); 34 0 : } 35 324 : break; 36 324 : } 37 : 38 : /* Lookup parent transaction while recovering from overruns 39 : FIXME This would be a lot easier if transactions specified 40 : parent by XID instead of by pointer ... */ 41 324 : candidate = fd_funk_txn_map_query_ele( query ); 42 324 : FD_COMPILER_MFENCE(); 43 324 : do { 44 324 : found_xid = FD_VOLATILE_CONST( candidate->xid ); 45 324 : parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) ); 46 324 : if( fd_funk_txn_idx_is_null( parent_idx ) ) break; 47 84 : if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx )); 48 : 49 84 : FD_COMPILER_MFENCE(); 50 84 : fd_funk_txn_t const * parent = &funk->txn_pool->ele[ parent_idx ]; 51 84 : parent_xid = FD_VOLATILE_CONST( parent->xid ); 52 84 : FD_COMPILER_MFENCE(); 53 : 54 84 : parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) ); 55 84 : if( fd_funk_txn_idx_is_null( parent_idx ) ) break; 56 84 : if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx )); 57 84 : } while(0); 58 324 : FD_COMPILER_MFENCE(); 59 : 60 324 : ulong candidate_idx = (ulong)( candidate - funk->txn_pool->ele ); 61 324 : fd_rwlock_read( &funk->txn_lock[ candidate_idx ] ); 62 : 63 : /* Verify speculative loads by ensuring txn still exists in map */ 64 324 : if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) { 65 0 : fd_rwlock_unread( &funk->txn_lock[ candidate_idx ] ); 66 0 : FD_SPIN_PAUSE(); 67 0 : goto retry; 68 0 : } 69 : 70 324 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &found_xid, &next_xid ) ) ) { 71 0 : FD_LOG_CRIT(( "fd_accdb_load_fork_slow detected memory corruption: expected xid %lu:%lu at %p, found %lu:%lu", 72 0 : next_xid.ul[0], next_xid.ul[1], 73 0 : (void *)candidate, 74 0 : found_xid.ul[0], found_xid.ul[1] )); 75 0 : } 76 : 77 324 : if( !tip ) tip = candidate; /* remember head of fork */ 78 84 : else fd_rwlock_unread( &funk->txn_lock[ candidate_idx ] ); 79 324 : lineage->fork [ i ] = next_xid; 80 324 : lineage->txn_idx[ i ] = (uint)( candidate - funk->txn_pool->ele ); 81 324 : if( fd_funk_txn_idx_is_null( parent_idx ) ) { 82 : /* Reached root */ 83 240 : i++; 84 240 : break; 85 240 : } 86 84 : next_xid = parent_xid; 87 84 : } 88 : 89 240 : done: 90 240 : lineage->fork_depth = i; 91 240 : if( FD_UNLIKELY( lineage->fork_depth==lineage->max_depth ) ) { 92 0 : FD_LOG_CRIT(( "Account database fork depth exceeded max of %lu", lineage->max_depth )); 93 0 : } 94 : 95 : /* Remember head of fork */ 96 240 : if( tip ) { 97 240 : lineage->tip_txn_idx = (ulong)( tip - funk->txn_pool->ele ); 98 240 : fd_rwlock_unread( &funk->txn_lock[ lineage->tip_txn_idx ] ); 99 240 : } else { 100 0 : lineage->tip_txn_idx = ULONG_MAX; /* XID is rooted */ 101 0 : } 102 240 : } 103 : 104 : fd_funk_txn_t * 105 : fd_accdb_lineage_write_check( fd_accdb_lineage_t const * lineage, 106 8814 : fd_funk_t const * funk ) { 107 8814 : ulong txn_idx = lineage->tip_txn_idx; 108 8814 : fd_funk_txn_xid_t const * xid = &lineage->fork[ 0 ]; 109 8814 : if( FD_UNLIKELY( txn_idx==ULONG_MAX ) ) { 110 0 : FD_LOG_CRIT(( "write failed: XID %lu:%lu is rooted", xid->ul[0], xid->ul[1] )); 111 0 : } 112 8814 : if( FD_UNLIKELY( txn_idx >= fd_funk_txn_pool_ele_max( funk->txn_pool ) ) ) { 113 0 : FD_LOG_CRIT(( "memory corruption detected: invalid txn_idx %lu (max %lu)", 114 0 : txn_idx, fd_funk_txn_pool_ele_max( funk->txn_pool ) )); 115 0 : } 116 8814 : fd_funk_txn_t * txn = &funk->txn_pool->ele[ txn_idx ]; 117 8814 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) { 118 0 : FD_LOG_CRIT(( "Failed to modify account: data race detected on fork node (expected XID %lu:%lu, found %lu:%lu)", 119 0 : xid->ul[0], xid->ul[1], 120 0 : txn->xid.ul[0], txn->xid.ul[1] )); 121 0 : } 122 8814 : if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) { 123 0 : FD_LOG_CRIT(( "Failed to modify account: XID %lu:%lu has children/is frozen", xid->ul[0], xid->ul[1] )); 124 0 : } 125 8814 : return txn; 126 8814 : } 127 : 128 : fd_xid_t const * 129 : fd_lineage_xid( fd_accdb_lineage_t const * lineage, 130 81 : ulong slot ) { 131 81 : ulong depth = lineage->fork_depth; 132 138 : for( ulong i=0UL; i<depth; i++ ) { 133 78 : if( lineage->fork[ i ].ul[0] == slot ) { 134 21 : return &lineage->fork[ i ]; 135 21 : } 136 78 : } 137 60 : return NULL; 138 81 : }