Line data Source code
1 : #ifndef HEADER_fd_src_funk_fd_funk_rec_h 2 : #define HEADER_fd_src_funk_fd_funk_rec_h 3 : 4 : /* fd_funk_rec.h provides APIs for managing funk records */ 5 : 6 : #include "fd_funk_txn.h" /* Includes fd_funk_base.h */ 7 : 8 : /* FD_FUNK_REC_{ALIGN,FOOTPRINT} describe the alignment and footprint of 9 : a fd_funk_rec_t. ALIGN will be a power of 2, footprint will be a 10 : multiple of align. These are provided to facilitate compile time 11 : declarations. */ 12 : 13 : #define FD_FUNK_REC_ALIGN (32UL) 14 : 15 : /* FD_FUNK_REC_IDX_NULL gives the map record idx value used to represent 16 : NULL. This value also set a limit on how large rec_max can be. */ 17 : 18 2869059 : #define FD_FUNK_REC_IDX_NULL (UINT_MAX) 19 : 20 : /* A fd_funk_rec_t describes a funk record. */ 21 : 22 : struct __attribute__((aligned(FD_FUNK_REC_ALIGN))) fd_funk_rec { 23 : 24 : /* These fields are managed by the funk's rec_map */ 25 : 26 : fd_funk_xid_key_pair_t pair; /* Transaction id and record key pair */ 27 : uint map_next; /* Internal use by map */ 28 : 29 : /* These fields are managed by the user */ 30 : 31 : uchar user[ 12 ]; 32 : 33 : /* These fields are managed by funk. TODO: Consider using record 34 : index compression here (much more debatable than in txn itself). */ 35 : 36 : uint next_idx; /* Record map index of next record in its transaction */ 37 : uint prev_idx; /* Record map index of previous record in its transaction */ 38 : 39 : /* Note: use of uint here requires FD_FUNK_REC_VAL_MAX to be at most 40 : (1UL<<28)-1. */ 41 : 42 : ulong val_sz : 28; /* Num bytes in record value, in [0,val_max] */ 43 : ulong val_max : 28; /* Max byte in record value, in [0,FD_FUNK_REC_VAL_MAX], 0 if val_gaddr is 0 */ 44 : ulong tag : 1; /* Used for internal validation */ 45 : ulong val_gaddr; /* Wksp gaddr on record value if any, 0 if val_max is 0 46 : If non-zero, the region [val_gaddr,val_gaddr+val_max) will be a current fd_alloc allocation (such that it is 47 : has tag wksp_tag) and the owner of the region will be the record. The allocator is 48 : fd_funk_alloc(). IMPORTANT! HAS NO GUARANTEED ALIGNMENT! */ 49 : 50 : }; 51 : 52 : typedef struct fd_funk_rec fd_funk_rec_t; 53 : 54 : FD_STATIC_ASSERT( sizeof(fd_funk_rec_t) == 3U*FD_FUNK_REC_ALIGN, record size is wrong ); 55 : 56 : /* fd_funk_rec_map allows for indexing records by their (xid,key) pair. 57 : It is used to store all records of the last published transaction and 58 : the records being updated for a transaction that is in-preparation. 59 : Published records are stored under the pair (root,key). (This is 60 : done so that publishing a transaction doesn't require updating all 61 : transaction id of all the records that were not updated by the 62 : publish.) */ 63 : 64 : #define POOL_NAME fd_funk_rec_pool 65 : #define POOL_ELE_T fd_funk_rec_t 66 : #define POOL_IDX_T uint 67 : #define POOL_NEXT map_next 68 : #define POOL_IMPL_STYLE 1 69 : #define POOL_LAZY 1 70 : #include "../util/tmpl/fd_pool_para.c" 71 : 72 : #define MAP_NAME fd_funk_rec_map 73 51 : #define MAP_ELE_T fd_funk_rec_t 74 : #define MAP_KEY_T fd_funk_xid_key_pair_t 75 : #define MAP_KEY pair 76 177 : #define MAP_KEY_EQ(k0,k1) fd_funk_xid_key_pair_eq((k0),(k1)) 77 5145 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed)) 78 : #define MAP_IDX_T uint 79 51 : #define MAP_NEXT map_next 80 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */ 81 4150176 : #define FD_FUNK_REC_MAP_CNT_WIDTH 43 82 4149774 : #define MAP_CNT_WIDTH FD_FUNK_REC_MAP_CNT_WIDTH 83 : #define MAP_IMPL_STYLE 1 84 : #define MAP_PEDANTIC 1 85 : #include "../util/tmpl/fd_map_chain_para.c" 86 : 87 : typedef fd_funk_rec_map_query_t fd_funk_rec_query_t; 88 : 89 : /* fd_funk_rec_prepare_t represents a new record that has been 90 : prepared but not inserted into the map yet. See documentation for 91 : fd_funk_rec_prepare. */ 92 : 93 : struct _fd_funk_rec_prepare { 94 : fd_funk_rec_t * rec; 95 : uint * rec_head_idx; 96 : uint * rec_tail_idx; 97 : }; 98 : 99 : typedef struct _fd_funk_rec_prepare fd_funk_rec_prepare_t; 100 : 101 : FD_PROTOTYPES_BEGIN 102 : 103 : /* fd_funk_rec_idx_is_null returns 1 if idx is FD_FUNK_REC_IDX_NULL and 104 : 0 otherwise. */ 105 : 106 574833 : FD_FN_CONST static inline int fd_funk_rec_idx_is_null( uint idx ) { return idx==FD_FUNK_REC_IDX_NULL; } 107 : 108 : /* Accessors */ 109 : 110 : /* fd_funk_rec_query_try queries the in-preparation transaction pointed to 111 : by txn for the record whose key matches the key pointed to by key. 112 : If txn is NULL, the query will be done for the funk's last published 113 : transaction. Returns a pointer to current record on success and NULL 114 : on failure. Reasons for failure include txn is neither NULL nor a 115 : pointer to a in-preparation transaction, key is NULL or not a record 116 : in the given transaction. 117 : 118 : The returned pointer is in the caller's address space if the 119 : return value is non-NULL. 120 : 121 : Assumes funk is a current local join (NULL returns NULL), txn is NULL 122 : or points to an in-preparation transaction in the caller's address 123 : space, key points to a record key in the caller's address space (NULL 124 : returns NULL), and no concurrent operations on funk, txn or key. 125 : funk retains no interest in key. The funk retains ownership of any 126 : returned record. 127 : 128 : The query argument remembers the query for later validity testing. 129 : 130 : This is reasonably fast O(1). 131 : 132 : Important safety tip! This function can encounter records 133 : that have the ERASE flag set (i.e. are tombstones of erased 134 : records). fd_funk_rec_query_try will still return the record in this 135 : case, and the application should check for the flag. */ 136 : 137 : fd_funk_rec_t * 138 : fd_funk_rec_query_try( fd_funk_t * funk, 139 : fd_funk_txn_xid_t const * xid, 140 : fd_funk_rec_key_t const * key, 141 : fd_funk_rec_query_t * query ); 142 : 143 : /* fd_funk_rec_query_try_global is the same as fd_funk_rec_query_try but 144 : will query txn's ancestors for key from youngest to oldest if key is 145 : not part of txn. As such, the txn of the returned record may not 146 : match txn but will be the txn of most recent ancestor with the key 147 : otherwise. If xid_out!=NULLL, *xid_out is set to the XID in which 148 : the record was created. 149 : 150 : This is reasonably fast O(in_prep_ancestor_cnt). 151 : 152 : Important safety tip! This function can encounter records 153 : that have the ERASE flag set (i.e. are tombstones of erased 154 : records). fd_funk_rec_query_try_global will return a NULL in this case 155 : but still set *txn_out to the relevant transaction. This behavior 156 : differs from fd_funk_rec_query_try. */ 157 : fd_funk_rec_t const * 158 : fd_funk_rec_query_try_global( fd_funk_t const * funk, 159 : fd_funk_txn_xid_t const * xid, 160 : fd_funk_rec_key_t const * key, 161 : fd_funk_txn_xid_t * xid_out, 162 : fd_funk_rec_query_t * query ); 163 : 164 : /* fd_funk_rec_{pair,xid,key} returns a pointer in the local address 165 : space of the {(transaction id,record key) pair,transaction id,record 166 : key} of a live record. Assumes rec points to a live record in the 167 : caller's address space. The lifetime of the returned pointer is the 168 : same as rec. The value at the pointer will be constant for its 169 : lifetime. */ 170 : 171 0 : FD_FN_CONST static inline fd_funk_xid_key_pair_t const * fd_funk_rec_pair( fd_funk_rec_t const * rec ) { return &rec->pair; } 172 6 : FD_FN_CONST static inline fd_funk_txn_xid_t const * fd_funk_rec_xid ( fd_funk_rec_t const * rec ) { return rec->pair.xid; } 173 0 : FD_FN_CONST static inline fd_funk_rec_key_t const * fd_funk_rec_key ( fd_funk_rec_t const * rec ) { return rec->pair.key; } 174 : 175 : /* fd_funk_rec_prepare creates an unpublished funk record entry. This 176 : is the first step to adding a funk record to a transaction. Record 177 : entry acquisition may fail if the record object pool is exhausted 178 : (FD_FUNK_ERR_REC) or the transaction is not writable 179 : (FD_FUNK_ERR_FROZEN). The returned record entry (located in funk 180 : shared memory) is then either be cancelled or published by the 181 : caller. This record is invisible to funk query or record-iteration 182 : operations until published. Concurrent record preparation is fine. */ 183 : 184 : fd_funk_rec_t * 185 : fd_funk_rec_prepare( fd_funk_t * funk, 186 : fd_funk_txn_xid_t const * xid, 187 : fd_funk_rec_key_t const * key, 188 : fd_funk_rec_prepare_t * prepare, 189 : int * opt_err ); 190 : 191 : /* fd_funk_rec_publish makes a prepared record globally visible. First, 192 : registers a record with the txn's record list, then inserts it into 193 : the record map. Concurrent record publishing is fine, even to the 194 : same transaction. Crashes the application with FD_LOG_CRIT if the 195 : caller attempts to publish the same (txn,xid) key twice. */ 196 : 197 : void 198 : fd_funk_rec_publish( fd_funk_t * funk, 199 : fd_funk_rec_prepare_t * prepare ); 200 : 201 : /* Misc */ 202 : 203 : /* fd_funk_rec_verify verifies the record map. Returns FD_FUNK_SUCCESS 204 : if the record map appears intact and FD_FUNK_ERR_INVAL if not (logs 205 : details). Meant to be called as part of fd_funk_verify. As such, it 206 : assumes funk is non-NULL, fd_funk_{wksp,txn_map,rec_map} have been 207 : verified to work and the txn_map has been verified. */ 208 : 209 : int 210 : fd_funk_rec_verify( fd_funk_t * funk ); 211 : 212 : FD_PROTOTYPES_END 213 : 214 : #endif /* HEADER_fd_src_funk_fd_funk_rec_h */