Line data Source code
1 : #include "fd_progcache_reclaim.h" 2 : #include "fd_progcache_clock.h" 3 : #include "fd_progcache_user.h" 4 : #include "../../util/racesan/fd_racesan_target.h" 5 : 6 : void 7 : fd_prog_reclaim_enqueue( fd_progcache_join_t * join, 8 96 : fd_progcache_rec_t * rec ) { 9 96 : uint idx = (uint)( rec - join->rec.pool->ele ); 10 96 : ulong rec_max = fd_prog_recp_ele_max( join->rec.pool ); 11 96 : if( FD_UNLIKELY( (ulong)idx >= rec_max ) ) 12 0 : FD_LOG_CRIT(( "progcache: corruption detected (reclaim_enqueue rec_idx=%u rec_max=%lu)", idx, rec_max )); 13 96 : rec->reclaim_next = join->rec.reclaim_head; 14 96 : join->rec.reclaim_head = idx; 15 96 : } 16 : 17 : static _Bool 18 : rec_reclaim( fd_progcache_join_t * join, 19 102 : fd_progcache_rec_t * rec ) { 20 : 21 : /* Remove the record from a transaction */ 22 : 23 102 : ulong txn_max = fd_prog_txnp_max( join->txn.pool ); 24 102 : uint txn_idx = atomic_load_explicit( &rec->txn_idx, memory_order_acquire ); 25 102 : if( txn_idx!=UINT_MAX ) { 26 36 : if( FD_UNLIKELY( (ulong)txn_idx >= txn_max ) ) 27 0 : FD_LOG_CRIT(( "progcache: corruption detected (rec_reclaim txn_idx=%u txn_max=%lu)", txn_idx, txn_max )); 28 36 : fd_progcache_txn_t * txn = &join->txn.pool[ txn_idx ]; 29 36 : fd_rwlock_write( &txn->lock ); 30 36 : fd_racesan_hook( "prog_reclaim:pre_cas" ); 31 36 : if( atomic_compare_exchange_strong_explicit( &rec->txn_idx, &txn_idx, UINT_MAX, memory_order_acq_rel, memory_order_acquire ) ) { 32 : /* A transaction may not be deallocated before all records are 33 : unlinked. */ 34 36 : fd_progcache_rec_unlink( join->rec.pool->ele, rec, txn, join->rec.pool->ele_max ); 35 36 : } else { 36 : /* Strong CAS failure implies that another thread is already 37 : unlinking the record (the rooting logic) */ 38 0 : FD_CHECK_CRIT( atomic_load_explicit( &rec->txn_idx, memory_order_relaxed )==UINT_MAX, "concurrency violation" ); 39 0 : } 40 36 : fd_rwlock_unwrite( &txn->lock ); 41 36 : } 42 102 : fd_racesan_hook( "prog_reclaim:post_unlink" ); 43 : 44 : /* Drain existing users 45 : Leave record in locked state (lock is reset when allocating) */ 46 : 47 102 : if( FD_UNLIKELY( !fd_rwlock_trywrite( &rec->lock ) ) ) return 0; 48 : 49 : /* All users are gone, deallocate record */ 50 : 51 96 : rec->reclaim_next = UINT_MAX; 52 96 : fd_progcache_val_free( rec, join ); 53 96 : rec->exists = 0; 54 96 : fd_prog_clock_remove( join->clock.bits, (ulong)( rec - join->rec.pool->ele ) ); 55 96 : fd_prog_recp_release( join->rec.pool, rec ); 56 96 : return 1; 57 102 : } 58 : 59 : ulong 60 2646 : fd_prog_reclaim_work( fd_progcache_join_t * join ) { 61 2646 : ulong rec_max = fd_prog_recp_ele_max( join->rec.pool ); 62 2646 : ulong cnt = 0UL; 63 2646 : uint * prev_p = &join->rec.reclaim_head; 64 2646 : uint cur = join->rec.reclaim_head; 65 2748 : while( cur!=UINT_MAX ) { 66 102 : if( FD_UNLIKELY( (ulong)cur >= rec_max ) ) 67 0 : FD_LOG_CRIT(( "progcache: corruption detected (reclaim_work rec_idx=%u rec_max=%lu)", cur, rec_max )); 68 102 : fd_progcache_rec_t * rec = &join->rec.pool->ele[ cur ]; 69 102 : uint next = rec->reclaim_next; 70 102 : if( rec_reclaim( join, rec ) ) { 71 96 : *prev_p = next; 72 96 : cnt++; 73 96 : } else { 74 6 : prev_p = &rec->reclaim_next; 75 6 : } 76 102 : cur = next; 77 102 : } 78 2646 : return cnt; 79 2646 : } 80 : 81 : long 82 : fd_prog_delete_rec( fd_progcache_join_t * cache, 83 99 : fd_progcache_rec_t * rec ) { 84 99 : if( !rec ) return -1L; 85 : 86 : /* Prepare index removal, and bail if rec is no longer present in map */ 87 99 : struct { 88 99 : fd_prog_recm_txn_t txn[1]; 89 99 : fd_prog_recm_txn_private_info_t info[1]; 90 99 : } _map_txn; 91 99 : fd_prog_recm_txn_t * map_txn = fd_prog_recm_txn_init( _map_txn.txn, cache->rec.map, 1UL ); 92 99 : fd_prog_recm_txn_add( map_txn, &rec->pair, 1 ); 93 99 : int txn_err = fd_prog_recm_txn_try( map_txn, FD_MAP_FLAG_BLOCKING ); 94 99 : if( FD_UNLIKELY( txn_err!=FD_MAP_SUCCESS ) ) 95 0 : FD_LOG_CRIT(( "fd_prog_recm_txn_try failed: %i-%s", txn_err, fd_map_strerror( txn_err ) )); 96 99 : fd_prog_recm_query_t query[1]; 97 99 : int q_err = fd_prog_recm_txn_query( cache->rec.map, &rec->pair, NULL, query, 0 ); 98 99 : if( q_err==FD_MAP_ERR_KEY || query->ele!=rec ) { 99 3 : fd_prog_recm_txn_test( map_txn ); 100 3 : fd_prog_recm_txn_fini( map_txn ); 101 3 : return -1L; 102 3 : } 103 : 104 : /* Drop record */ 105 96 : int rm_err = fd_prog_recm_txn_remove( cache->rec.map, &rec->pair, NULL, query, 0 ); 106 96 : if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) 107 0 : FD_LOG_CRIT(( "fd_prog_recm_txn_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) )); 108 96 : int test_err = fd_prog_recm_txn_test( map_txn ); 109 96 : if( FD_UNLIKELY( test_err!=FD_MAP_SUCCESS ) ) 110 0 : FD_LOG_CRIT(( "fd_prog_recm_txn_test failed: %i-%s", test_err, fd_map_strerror( test_err ) )); 111 96 : fd_prog_recm_txn_fini( map_txn ); 112 : 113 96 : fd_prog_reclaim_enqueue( cache, rec ); 114 96 : return (long)rec->data_max; 115 96 : }