Line data Source code
1 : #include "fd_funk_private.h"
2 : #include "../util/racesan/fd_racesan_target.h"
3 :
4 : /* Provide the actual record map implementation */
5 :
6 : #define POOL_NAME fd_funk_rec_pool
7 2556537 : #define POOL_ELE_T fd_funk_rec_t
8 : #define POOL_IDX_T uint
9 2566314 : #define POOL_NEXT map_next
10 : #define POOL_IMPL_STYLE 2
11 : #define POOL_LAZY 1
12 : #include "../util/tmpl/fd_pool_para.c"
13 :
14 : #define MAP_NAME fd_funk_rec_map
15 1380141 : #define MAP_ELE_T fd_funk_rec_t
16 12237 : #define MAP_KEY_T fd_funk_xid_key_pair_t
17 1289526 : #define MAP_KEY pair
18 : #define MAP_KEY_EQ(k0,k1) fd_funk_xid_key_pair_eq((k0),(k1))
19 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
20 1379541 : #define MAP_IDX_T uint
21 3283935 : #define MAP_NEXT map_next
22 123 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
23 5216520 : #define MAP_CNT_WIDTH FD_FUNK_REC_MAP_CNT_WIDTH
24 : #define MAP_IMPL_STYLE 2
25 : #define MAP_PEDANTIC 1
26 : #include "../util/tmpl/fd_map_chain_para.c"
27 :
28 : static fd_funk_txn_t *
29 : fd_funk_rec_txn_borrow( fd_funk_t const * funk,
30 : fd_funk_txn_xid_t const * xid,
31 1276974 : fd_funk_txn_map_query_t * query ) {
32 1276974 : memset( query, 0, sizeof(fd_funk_txn_map_query_t) );
33 1276974 : if( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) return NULL;
34 :
35 1276974 : fd_funk_txn_map_query_t txn_query[1];
36 1276974 : for(;;) {
37 1276974 : int txn_query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, txn_query, 0 );
38 1276974 : if( FD_UNLIKELY( txn_query_err==FD_MAP_ERR_AGAIN ) ) continue;
39 1276974 : if( FD_UNLIKELY( txn_query_err!=FD_MAP_SUCCESS ) ) {
40 0 : FD_LOG_CRIT(( "fd_funk_rec op failed: txn_map_query_try(%lu:%lu) error %i-%s",
41 0 : xid->ul[0], xid->ul[1],
42 0 : txn_query_err, fd_map_strerror( txn_query_err ) ));
43 0 : }
44 1276974 : break;
45 1276974 : }
46 1276974 : fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( txn_query );
47 1276974 : uint txn_state = FD_VOLATILE_CONST( txn->state );
48 1276974 : if( FD_UNLIKELY( txn_state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
49 0 : FD_LOG_CRIT(( "fd_funk_rec op failed: txn %p %lu:%lu state is %u-%s",
50 0 : (void *)txn, xid->ul[0], xid->ul[1],
51 0 : txn_state, fd_funk_txn_state_str( txn_state ) ));
52 0 : }
53 1276974 : return txn;
54 1276974 : }
55 :
56 : static void
57 1276974 : fd_funk_rec_txn_release( fd_funk_txn_map_query_t const * query ) {
58 1276974 : if( !query->ele ) return;
59 0 : if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
60 0 : FD_LOG_CRIT(( "fd_funk_rec_txn_release: fd_funk_txn_map_query_test failed (data race detected?)" ));
61 0 : }
62 0 : }
63 :
64 : fd_funk_rec_t *
65 : fd_funk_rec_query_try( fd_funk_t * funk,
66 : fd_funk_txn_xid_t const * xid,
67 : fd_funk_rec_key_t const * key,
68 1005 : fd_funk_rec_query_t * query ) {
69 1005 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
70 1005 : if( FD_UNLIKELY( !xid ) ) FD_LOG_CRIT(( "NULL xid" ));
71 1005 : if( FD_UNLIKELY( !key ) ) FD_LOG_CRIT(( "NULL key" ));
72 1005 : if( FD_UNLIKELY( !query ) ) FD_LOG_CRIT(( "NULL query" ));
73 :
74 1005 : fd_funk_xid_key_pair_t pair[1];
75 1005 : if( FD_UNLIKELY( fd_funk_txn_xid_eq( xid, funk->shmem->last_publish ) ) ) {
76 0 : fd_funk_txn_xid_set_root( pair->xid );
77 1005 : } else {
78 1005 : fd_funk_txn_xid_copy( pair->xid, xid );
79 1005 : }
80 1005 : fd_funk_rec_key_copy( pair->key, key );
81 :
82 1005 : for(;;) {
83 1005 : int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 );
84 1005 : if( err == FD_MAP_SUCCESS ) break;
85 0 : if( err == FD_MAP_ERR_KEY ) return NULL;
86 0 : if( err == FD_MAP_ERR_AGAIN ) continue;
87 0 : FD_LOG_CRIT(( "query returned err %d", err ));
88 0 : }
89 1005 : return fd_funk_rec_map_query_ele( query );
90 1005 : }
91 :
92 : fd_funk_rec_t *
93 : fd_funk_rec_prepare( fd_funk_t * funk,
94 : fd_funk_txn_xid_t const * xid,
95 : fd_funk_rec_key_t const * key,
96 : fd_funk_rec_prepare_t * prepare,
97 1276974 : int * opt_err ) {
98 1276974 : if( FD_UNLIKELY( !funk ) ) FD_LOG_CRIT(( "NULL funk" ));
99 1276974 : if( FD_UNLIKELY( !xid ) ) FD_LOG_CRIT(( "NULL xid" ));
100 1276974 : if( FD_UNLIKELY( !prepare ) ) FD_LOG_CRIT(( "NULL prepare" ));
101 :
102 1276974 : fd_funk_txn_map_query_t txn_query[1];
103 1276974 : fd_funk_txn_t * txn = fd_funk_rec_txn_borrow( funk, xid, txn_query );
104 :
105 1276974 : memset( prepare, 0, sizeof(fd_funk_rec_prepare_t) );
106 :
107 1276974 : if( !txn ) { /* Modifying last published */
108 0 : if( FD_UNLIKELY( fd_funk_last_publish_is_frozen( funk ) ) ) {
109 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
110 0 : fd_funk_rec_txn_release( txn_query );
111 0 : return NULL;
112 0 : }
113 1276974 : } else {
114 1276974 : if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
115 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_FROZEN );
116 0 : fd_funk_rec_txn_release( txn_query );
117 0 : return NULL;
118 0 : }
119 1276974 : }
120 :
121 1276974 : fd_funk_rec_t * rec = prepare->rec = fd_funk_rec_pool_acquire( funk->rec_pool, NULL, 1, opt_err );
122 1276974 : if( opt_err && *opt_err == FD_POOL_ERR_CORRUPT ) {
123 0 : FD_LOG_CRIT(( "corrupt element returned from funk rec pool" ));
124 0 : }
125 1276974 : if( FD_UNLIKELY( !rec ) ) {
126 0 : fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_REC );
127 0 : fd_funk_rec_txn_release( txn_query );
128 0 : return rec;
129 0 : }
130 :
131 1276974 : fd_funk_val_init( rec );
132 1276974 : if( txn == NULL ) {
133 0 : fd_funk_txn_xid_set_root( rec->pair.xid );
134 1276974 : } else {
135 1276974 : fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
136 1276974 : prepare->rec_head_idx = &txn->rec_head_idx;
137 1276974 : prepare->rec_tail_idx = &txn->rec_tail_idx;
138 1276974 : }
139 1276974 : fd_funk_rec_key_copy( rec->pair.key, key );
140 1276974 : rec->tag = 0;
141 1276974 : rec->pub = 0;
142 1276974 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
143 1276974 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
144 1276974 : fd_funk_rec_txn_release( txn_query );
145 1276974 : return rec;
146 1276974 : }
147 :
148 : #if FD_HAS_ATOMIC
149 :
150 : static void
151 : fd_funk_rec_push_tail( fd_funk_t * funk,
152 1277214 : fd_funk_rec_prepare_t * prepare ) {
153 1277214 : fd_funk_rec_t * rec = prepare->rec;
154 1277214 : uint rec_idx = (uint)( rec - funk->rec_pool->ele );
155 1277214 : uint * rec_head_idx = prepare->rec_head_idx;
156 1277214 : uint * rec_tail_idx = prepare->rec_tail_idx;
157 :
158 1277214 : for(;;) {
159 :
160 : /* Doubly linked list append. Robust in the event of concurrent
161 : publishes. Iteration during publish not supported. Sequence:
162 : - Identify tail element
163 : - Set new element's prev and next pointers
164 : - Set tail element's next pointer
165 : - Set tail pointer */
166 :
167 1277214 : uint rec_prev_idx = FD_VOLATILE_CONST( *rec_tail_idx );
168 1277214 : rec->prev_idx = rec_prev_idx;
169 1277214 : FD_COMPILER_MFENCE();
170 :
171 1277214 : uint * next_idx_p;
172 1277214 : if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
173 395934 : next_idx_p = rec_head_idx;
174 881280 : } else {
175 881280 : next_idx_p = &funk->rec_pool->ele[ rec_prev_idx ].next_idx;
176 881280 : }
177 :
178 1277214 : fd_racesan_hook( "funk_rec_push_tail:next_cas" );
179 1277214 : if( FD_UNLIKELY( !__sync_bool_compare_and_swap( next_idx_p, FD_FUNK_REC_IDX_NULL, rec_idx ) ) ) {
180 : /* Another thread beat us to the punch */
181 0 : FD_SPIN_PAUSE();
182 0 : continue;
183 0 : }
184 :
185 1277214 : fd_racesan_hook( "funk_rec_push_tail:tail_cas" );
186 1277214 : if( FD_UNLIKELY( !__sync_bool_compare_and_swap( rec_tail_idx, rec_prev_idx, rec_idx ) ) ) {
187 : /* This CAS is guaranteed to succeed if the previous CAS passed. */
188 0 : FD_LOG_CRIT(( "Irrecoverable data race encountered while appending to txn rec list (invariant violation?): cas(%p,%u,%u)",
189 0 : (void *)rec_tail_idx, rec_prev_idx, rec_idx ));
190 0 : }
191 :
192 1277214 : break;
193 1277214 : }
194 1277214 : }
195 :
196 : #else
197 :
198 : static void
199 : fd_funk_rec_push_tail( fd_funk_t * funk,
200 : fd_funk_rec_prepare_t * prepare ) {
201 : fd_funk_rec_t * rec = prepare->rec;
202 : uint rec_idx = (uint)( rec - funk->rec_pool->ele );
203 : uint * rec_head_idx = prepare->rec_head_idx;
204 : uint * rec_tail_idx = prepare->rec_tail_idx;
205 : uint rec_prev_idx = *rec_tail_idx;
206 : *rec_tail_idx = rec_idx;
207 : rec->prev_idx = rec_prev_idx;
208 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
209 : if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
210 : *rec_head_idx = rec_idx;
211 : } else {
212 : funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
213 : }
214 : }
215 :
216 : #endif
217 :
218 : void
219 : fd_funk_rec_publish( fd_funk_t * funk,
220 1277214 : fd_funk_rec_prepare_t * prepare ) {
221 1277214 : fd_funk_rec_t * rec = prepare->rec;
222 1277214 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
223 1277214 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
224 1277214 : rec->pub = 1;
225 :
226 1277214 : if( prepare->rec_head_idx ) {
227 1277214 : fd_funk_rec_push_tail( funk, prepare );
228 1277214 : }
229 :
230 1277214 : fd_racesan_hook( "funk_rec_publish:map_insert" );
231 1277214 : int insert_err = fd_funk_rec_map_insert( funk->rec_map, rec, FD_MAP_FLAG_BLOCKING );
232 1277214 : if( insert_err ) {
233 0 : FD_LOG_CRIT(( "fd_funk_rec_map_insert failed (%i-%s)", insert_err, fd_map_strerror( insert_err ) ));
234 0 : }
235 1277214 : }
236 :
237 : int
238 243 : fd_funk_rec_verify( fd_funk_t * funk ) {
239 243 : fd_funk_rec_map_t * rec_map = funk->rec_map;
240 243 : fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
241 243 : ulong rec_max = fd_funk_rec_pool_ele_max( rec_pool );
242 :
243 495348 : # define TEST(c) do { \
244 495348 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
245 495348 : } while(0)
246 :
247 243 : TEST( !fd_funk_rec_map_verify( rec_map ) );
248 243 : TEST( !fd_funk_rec_pool_verify( rec_pool ) );
249 :
250 : /* Iterate (again) over all records in use */
251 :
252 243 : fd_funk_rec_map_shmem_private_chain_t * chain_tbl =
253 243 : fd_funk_rec_map_shmem_private_chain( rec_map->map, 0UL );
254 243 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
255 444819 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
256 444576 : ulong chain_ele_cnt = fd_funk_rec_map_private_vcnt_cnt( chain_tbl[ chain_idx ].ver_cnt );
257 444576 : ulong chain_ele_idx = 0UL;
258 444576 : for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
259 456813 : !fd_funk_rec_map_iter_done( iter );
260 444576 : iter = fd_funk_rec_map_iter_next( iter ), chain_ele_idx++ ) {
261 12237 : fd_funk_rec_t const * rec = fd_funk_rec_map_iter_ele_const( iter );
262 :
263 : /* Make sure every record either links up with the last published
264 : transaction or an in-prep transaction and the flags are sane. */
265 :
266 12237 : fd_funk_txn_xid_t const * xid = fd_funk_rec_xid( rec );
267 :
268 12237 : if( fd_funk_txn_xid_eq_root( xid ) ) { /* This is a record from the last published transaction */
269 :
270 11232 : TEST( fd_funk_txn_xid_eq_root( xid ) );
271 : /* No record linked list at the root txn */
272 11232 : TEST( fd_funk_rec_idx_is_null( rec->prev_idx ) );
273 11232 : TEST( fd_funk_rec_idx_is_null( rec->next_idx ) );
274 :
275 11232 : } else { /* This is a record from an in-prep transaction */
276 :
277 1005 : fd_funk_txn_map_query_t query[1];
278 1005 : TEST( fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 )==FD_MAP_SUCCESS );
279 :
280 1005 : }
281 12237 : }
282 444576 : TEST( chain_ele_idx==chain_ele_cnt );
283 444576 : }
284 :
285 : /* Clear record tags and then verify membership */
286 :
287 889395 : for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool->ele[ rec_idx ].tag = 0;
288 :
289 243 : do {
290 243 : fd_funk_all_iter_t iter[1];
291 12480 : for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
292 12237 : fd_funk_rec_t * rec = fd_funk_all_iter_ele( iter );
293 12237 : if( fd_funk_txn_xid_eq_root( rec->pair.xid ) ) {
294 11232 : TEST( !rec->tag );
295 11232 : rec->tag = 1;
296 11232 : }
297 12237 : }
298 :
299 243 : fd_funk_txn_all_iter_t txn_iter[1];
300 834 : for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
301 591 : fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
302 :
303 591 : uint rec_idx = txn->rec_head_idx;
304 1596 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
305 1005 : TEST( (rec_idx<rec_max) && !rec_pool->ele[ rec_idx ].tag );
306 1005 : rec_pool->ele[ rec_idx ].tag = 1;
307 1005 : fd_funk_rec_query_t query[1];
308 1005 : fd_funk_rec_t const * rec2 = fd_funk_rec_query_try( funk, &txn->xid, rec_pool->ele[ rec_idx ].pair.key, query );
309 1005 : TEST( rec2 == rec_pool->ele + rec_idx );
310 1005 : uint next_idx = rec_pool->ele[ rec_idx ].next_idx;
311 1005 : if( !fd_funk_rec_idx_is_null( next_idx ) ) TEST( rec_pool->ele[ next_idx ].prev_idx==rec_idx );
312 1005 : rec_idx = next_idx;
313 1005 : }
314 591 : }
315 243 : } while(0);
316 :
317 243 : do {
318 243 : fd_funk_txn_all_iter_t txn_iter[1];
319 834 : for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
320 591 : fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
321 :
322 591 : uint rec_idx = txn->rec_tail_idx;
323 1596 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
324 1005 : TEST( (rec_idx<rec_max) && rec_pool->ele[ rec_idx ].tag );
325 1005 : rec_pool->ele[ rec_idx ].tag = 0;
326 1005 : uint prev_idx = rec_pool->ele[ rec_idx ].prev_idx;
327 1005 : if( !fd_funk_rec_idx_is_null( prev_idx ) ) TEST( rec_pool->ele[ prev_idx ].next_idx==rec_idx );
328 1005 : rec_idx = prev_idx;
329 1005 : }
330 591 : }
331 243 : } while(0);
332 :
333 243 : fd_funk_all_iter_t iter[1];
334 12480 : for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
335 12237 : fd_funk_rec_t const * rec = fd_funk_all_iter_ele( iter );
336 12237 : FD_TEST( rec->tag == fd_funk_txn_xid_eq_root( rec->pair.xid ) );
337 12237 : }
338 :
339 243 : # undef TEST
340 :
341 243 : return FD_FUNK_SUCCESS;
342 243 : }
|