Line data Source code
1 : #include "fd_progcache_admin.h"
2 : #include "fd_progcache_rec.h"
3 :
4 : /* Algorithm to estimate size of cache metadata structures (rec_pool
5 : object pool and rec_map hashchain table).
6 :
7 : FIXME Carefully balance this */
8 :
9 : static ulong
10 : fd_progcache_est_rec_max1( ulong wksp_footprint,
11 0 : ulong mean_cache_entry_size ) {
12 0 : return wksp_footprint / mean_cache_entry_size;
13 0 : }
14 :
15 : ulong
16 : fd_progcache_est_rec_max( ulong wksp_footprint,
17 0 : ulong mean_cache_entry_size ) {
18 0 : ulong est = fd_progcache_est_rec_max1( wksp_footprint, mean_cache_entry_size );
19 0 : if( FD_UNLIKELY( est>(1UL<<31) ) ) FD_LOG_ERR(( "fd_progcache_est_rec_max(wksp_footprint=%lu,mean_cache_entry_size=%lu) failed: invalid parameters", wksp_footprint, mean_cache_entry_size ));
20 0 : return fd_ulong_max( est, 2048UL );
21 0 : }
22 :
23 : fd_progcache_admin_t *
24 : fd_progcache_admin_join( fd_progcache_admin_t * ljoin,
25 45 : void * shfunk ) {
26 45 : if( FD_UNLIKELY( !ljoin ) ) {
27 0 : FD_LOG_WARNING(( "NULL ljoin" ));
28 0 : return NULL;
29 0 : }
30 45 : if( FD_UNLIKELY( !shfunk ) ) {
31 0 : FD_LOG_WARNING(( "NULL shfunk" ));
32 0 : return NULL;
33 0 : }
34 :
35 45 : memset( ljoin, 0, sizeof(fd_progcache_admin_t) );
36 45 : if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk ) ) ) {
37 0 : FD_LOG_CRIT(( "fd_funk_join failed" ));
38 0 : }
39 :
40 45 : return ljoin;
41 45 : }
42 :
43 : void *
44 : fd_progcache_admin_leave( fd_progcache_admin_t * ljoin,
45 42 : void ** opt_shfunk ) {
46 42 : if( FD_UNLIKELY( !ljoin ) ) FD_LOG_CRIT(( "NULL ljoin" ));
47 :
48 42 : if( FD_UNLIKELY( !fd_funk_leave( ljoin->funk, opt_shfunk ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
49 :
50 42 : return ljoin;
51 42 : }
52 :
53 : /* Begin transaction-level operations. It is assumed that funk_txn data
54 : structures are not concurrently modified. This includes txn_pool and
55 : txn_map. */
56 :
57 : void
58 : fd_progcache_txn_attach_child( fd_progcache_admin_t * cache,
59 : fd_funk_txn_xid_t const * xid_parent,
60 96 : fd_funk_txn_xid_t const * xid_new ) {
61 96 : FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: created with parent %lu:%lu",
62 96 : (void *)cache->funk,
63 96 : xid_new ->ul[0], xid_new ->ul[1],
64 96 : xid_parent->ul[0], xid_parent->ul[1] ));
65 96 : fd_funk_txn_prepare( cache->funk, xid_parent, xid_new );
66 96 : }
67 :
68 : static void
69 : fd_progcache_txn_cancel_one( fd_progcache_admin_t * cache,
70 60 : fd_funk_txn_t * txn ) {
71 60 : FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: cancel", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
72 :
73 60 : fd_funk_t * funk = cache->funk;
74 60 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( txn->child_head_cidx ) ||
75 60 : !fd_funk_txn_idx_is_null( txn->child_tail_cidx ) ) ) {
76 0 : FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: txn at %p with xid %lu:%lu has children (data corruption?)",
77 0 : (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
78 0 : }
79 :
80 : /* Phase 1: Drain users from transaction */
81 :
82 60 : fd_rwlock_write( txn->lock );
83 60 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_CANCEL;
84 :
85 : /* Phase 2: Remove records */
86 :
87 111 : while( !fd_funk_rec_idx_is_null( txn->rec_head_idx ) ) {
88 51 : fd_funk_rec_t * rec = &funk->rec_pool->ele[ txn->rec_head_idx ];
89 51 : uint next_idx = rec->next_idx;
90 51 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
91 51 : if( FD_LIKELY( !fd_funk_rec_idx_is_null( next_idx ) ) ) {
92 0 : funk->rec_pool->ele[ next_idx ].prev_idx = FD_FUNK_REC_IDX_NULL;
93 0 : }
94 :
95 51 : fd_funk_val_flush( rec, funk->alloc, funk->wksp );
96 :
97 51 : fd_funk_rec_query_t query[1];
98 51 : int remove_err = fd_funk_rec_map_remove( funk->rec_map, &rec->pair, NULL, query, FD_MAP_FLAG_BLOCKING );
99 51 : if( FD_UNLIKELY( remove_err ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
100 :
101 51 : fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
102 :
103 51 : txn->rec_head_idx = next_idx;
104 51 : if( fd_funk_rec_idx_is_null( next_idx ) ) txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
105 51 : }
106 :
107 : /* Phase 3: Remove transaction from fork graph */
108 :
109 60 : uint self_cidx = fd_funk_txn_cidx( (ulong)( txn-funk->txn_pool->ele ) );
110 60 : uint prev_cidx = txn->sibling_prev_cidx; ulong prev_idx = fd_funk_txn_idx( prev_cidx );
111 60 : uint next_cidx = txn->sibling_next_cidx; ulong next_idx = fd_funk_txn_idx( next_cidx );
112 60 : if( !fd_funk_txn_idx_is_null( next_idx ) ) {
113 0 : funk->txn_pool->ele[ next_idx ].sibling_prev_cidx = prev_cidx;
114 0 : }
115 60 : if( !fd_funk_txn_idx_is_null( prev_idx ) ) {
116 0 : funk->txn_pool->ele[ prev_idx ].sibling_next_cidx = next_cidx;
117 0 : }
118 60 : if( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) {
119 30 : fd_funk_txn_t * parent = &funk->txn_pool->ele[ fd_funk_txn_idx( txn->parent_cidx ) ];
120 30 : if( parent->child_head_cidx==self_cidx ) parent->child_head_cidx = next_cidx;
121 30 : if( parent->child_tail_cidx==self_cidx ) parent->child_tail_cidx = prev_cidx;
122 30 : } else {
123 30 : if( funk->shmem->child_head_cidx==self_cidx ) funk->shmem->child_head_cidx = next_cidx;
124 30 : if( funk->shmem->child_tail_cidx==self_cidx ) funk->shmem->child_tail_cidx = prev_cidx;
125 30 : }
126 :
127 : /* Phase 4: Remove transcation from index */
128 :
129 60 : fd_funk_txn_map_query_t query[1];
130 60 : int remove_err = fd_funk_txn_map_remove( funk->txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
131 60 : if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
132 0 : FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: fd_funk_txn_map_remove(%lu:%lu) failed: %i-%s",
133 0 : txn->xid.ul[0], txn->xid.ul[1], remove_err, fd_map_strerror( remove_err ) ));
134 0 : }
135 :
136 : /* Phase 5: Free transaction object */
137 :
138 60 : fd_rwlock_unwrite( txn->lock );
139 60 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
140 60 : fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
141 60 : }
142 :
143 : /* Cancels txn and all children */
144 :
145 : static void
146 : fd_progcache_txn_cancel_tree( fd_progcache_admin_t * cache,
147 60 : fd_funk_txn_t * txn ) {
148 78 : for(;;) {
149 78 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
150 78 : if( fd_funk_txn_idx_is_null( child_idx ) ) break;
151 18 : fd_funk_txn_t * child = &cache->funk->txn_pool->ele[ child_idx ];
152 18 : fd_progcache_txn_cancel_tree( cache, child );
153 18 : }
154 60 : fd_progcache_txn_cancel_one( cache, txn );
155 60 : }
156 :
157 : /* Cancels all left/right siblings */
158 :
159 : static void
160 : fd_progcache_txn_cancel_prev_list( fd_progcache_admin_t * cache,
161 33 : fd_funk_txn_t * txn ) {
162 33 : ulong self_idx = (ulong)( txn - cache->funk->txn_pool->ele );
163 33 : for(;;) {
164 33 : ulong prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
165 33 : if( FD_UNLIKELY( prev_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
166 33 : if( fd_funk_txn_idx_is_null( prev_idx ) ) break;
167 0 : fd_funk_txn_t * sibling = &cache->funk->txn_pool->ele[ prev_idx ];
168 0 : fd_progcache_txn_cancel_tree( cache, sibling );
169 0 : }
170 33 : }
171 :
172 : static void
173 : fd_progcache_txn_cancel_next_list( fd_progcache_admin_t * cache,
174 75 : fd_funk_txn_t * txn ) {
175 75 : ulong self_idx = (ulong)( txn - cache->funk->txn_pool->ele );
176 75 : for(;;) {
177 75 : ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
178 75 : if( FD_UNLIKELY( next_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
179 75 : if( fd_funk_txn_idx_is_null( next_idx ) ) break;
180 0 : fd_funk_txn_t * sibling = &cache->funk->txn_pool->ele[ next_idx ];
181 0 : fd_progcache_txn_cancel_tree( cache, sibling );
182 0 : }
183 75 : }
184 :
185 : void
186 : fd_progcache_txn_cancel( fd_progcache_admin_t * cache,
187 42 : fd_funk_txn_xid_t const * xid ) {
188 42 : fd_funk_t * funk = cache->funk;
189 :
190 42 : fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
191 42 : if( FD_UNLIKELY( !txn ) ) {
192 0 : FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
193 0 : }
194 :
195 42 : fd_progcache_txn_cancel_next_list( cache, txn );
196 42 : fd_progcache_txn_cancel_tree( cache, txn );
197 42 : }
198 :
199 : /* fd_progcache_gc_root cleans up a stale "rooted" version of a
200 : record. */
201 :
202 : static void
203 : fd_progcache_gc_root( fd_progcache_admin_t * cache,
204 15 : fd_funk_xid_key_pair_t const * pair ) {
205 15 : fd_funk_t * funk = cache->funk;
206 :
207 : /* Phase 1: Remove record from map if found */
208 :
209 15 : fd_funk_rec_query_t query[1];
210 15 : int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
211 15 : if( rm_err==FD_MAP_ERR_KEY ) return;
212 6 : if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) ));
213 :
214 : /* Phase 2: Invalidate record */
215 :
216 6 : fd_funk_rec_t * old_rec = query->ele;
217 6 : memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
218 6 : FD_COMPILER_MFENCE();
219 :
220 : /* Phase 3: Free record */
221 :
222 6 : old_rec->map_next = FD_FUNK_REC_IDX_NULL;
223 6 : fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
224 6 : fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
225 6 : cache->metrics.gc_root_cnt++;
226 6 : }
227 :
228 : /* fd_progcache_gc_invalidation cleans up a "cache invalidate" record,
229 : which may not exist at the database root. */
230 :
231 : static void
232 : fd_progcache_gc_invalidation( fd_progcache_admin_t * cache,
233 9 : fd_funk_rec_t * rec ) {
234 9 : fd_funk_t * funk = cache->funk;
235 :
236 : /* Phase 1: Remove record from map if found */
237 :
238 9 : fd_funk_xid_key_pair_t pair = rec->pair;
239 9 : fd_funk_rec_query_t query[1];
240 9 : int rm_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
241 9 : if( rm_err==FD_MAP_ERR_KEY ) return;
242 9 : if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) ));
243 9 : if( FD_UNLIKELY( query->ele!=rec ) ) {
244 0 : FD_LOG_CRIT(( "Found record collision in program cache: xid=%lu:%lu key=%016lx%016lx%016lx%016lx ele0=%u ele1=%u",
245 0 : pair.xid->ul[0], pair.xid->ul[1],
246 0 : fd_ulong_bswap( pair.key->ul[0] ),
247 0 : fd_ulong_bswap( pair.key->ul[1] ),
248 0 : fd_ulong_bswap( pair.key->ul[2] ),
249 0 : fd_ulong_bswap( pair.key->ul[3] ),
250 0 : (uint)( query->ele - funk->rec_pool->ele ),
251 0 : (uint)( rec - funk->rec_pool->ele ) ));
252 0 : }
253 :
254 : /* Phase 2: Invalidate record */
255 :
256 9 : memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
257 9 : FD_COMPILER_MFENCE();
258 :
259 : /* Phase 3: Free record */
260 :
261 9 : rec->map_next = FD_FUNK_REC_IDX_NULL;
262 9 : fd_funk_val_flush( rec, funk->alloc, funk->wksp );
263 9 : fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
264 9 : }
265 :
266 : /* fd_progcache_publish_recs publishes all of a progcache's records.
267 : It is assumed at this point that the txn has no more concurrent
268 : users. */
269 :
270 : static void
271 : fd_progcache_publish_recs( fd_progcache_admin_t * cache,
272 33 : fd_funk_txn_t * txn ) {
273 : /* Iterate record list */
274 33 : uint head = txn->rec_head_idx;
275 33 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
276 33 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
277 48 : while( !fd_funk_rec_idx_is_null( head ) ) {
278 15 : fd_funk_rec_t * rec = &cache->funk->rec_pool->ele[ head ];
279 :
280 : /* Evict previous value from hash chain */
281 15 : fd_funk_xid_key_pair_t pair[1];
282 15 : fd_funk_rec_key_copy( pair->key, rec->pair.key );
283 15 : fd_funk_txn_xid_set_root( pair->xid );
284 15 : fd_progcache_gc_root( cache, pair );
285 15 : uint next = rec->next_idx;
286 :
287 15 : fd_progcache_rec_t * prec = fd_funk_val( rec, cache->funk->wksp );
288 15 : FD_TEST( prec );
289 15 : if( FD_UNLIKELY( prec->invalidate ) ) {
290 : /* Drop cache invalidate records */
291 9 : fd_progcache_gc_invalidation( cache, rec );
292 9 : cache->metrics.gc_root_cnt++;
293 9 : } else {
294 : /* Migrate record to root */
295 6 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
296 6 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
297 6 : fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } };
298 6 : fd_funk_txn_xid_st_atomic( rec->pair.xid, &root );
299 6 : cache->metrics.root_cnt++;
300 6 : }
301 :
302 15 : head = next; /* next record */
303 15 : }
304 33 : }
305 :
306 : /* fd_progcache_txn_publish_one merges an in-prep transaction whose
307 : parent is the last published, into the parent. */
308 :
309 : static void
310 : fd_progcache_txn_publish_one( fd_progcache_admin_t * cache,
311 33 : fd_funk_txn_xid_t const * xid ) {
312 33 : fd_funk_t * funk = cache->funk;
313 :
314 : /* Phase 1: Mark transaction as "last published" */
315 :
316 33 : fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
317 33 : if( FD_UNLIKELY( !txn ) ) {
318 0 : FD_LOG_CRIT(( "fd_progcache_publish failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
319 0 : }
320 33 : FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: publish", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
321 33 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
322 0 : FD_LOG_CRIT(( "fd_progcache_publish failed: txn with xid %lu:%lu is not a child of the last published txn", xid->ul[0], xid->ul[1] ));
323 0 : }
324 33 : fd_funk_txn_xid_st_atomic( funk->shmem->last_publish, xid );
325 :
326 : /* Phase 2: Drain users from transaction */
327 :
328 33 : fd_rwlock_write( txn->lock );
329 33 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_PUBLISH;
330 :
331 : /* Phase 3: Migrate records */
332 :
333 33 : fd_progcache_publish_recs( cache, txn );
334 :
335 : /* Phase 4: Remove transaction from fork graph
336 :
337 : Because the transaction has no more records, removing it from the
338 : fork graph has no visible side effects to concurrent query ops
339 : (always return "no found") or insert ops (refuse to write to a
340 : "publish" state txn). */
341 :
342 33 : { /* Adjust the parent pointers of the children to point to "last published" */
343 33 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
344 39 : while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
345 6 : funk->txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
346 6 : child_idx = fd_funk_txn_idx( funk->txn_pool->ele[ child_idx ].sibling_next_cidx );
347 6 : }
348 33 : }
349 :
350 : /* Phase 5: Remove transaction from index
351 :
352 : The transaction is now an orphan and won't get any new records. */
353 :
354 33 : fd_funk_txn_map_query_t query[1];
355 33 : int remove_err = fd_funk_txn_map_remove( funk->txn_map, xid, NULL, query, 0 );
356 33 : if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
357 0 : FD_LOG_CRIT(( "fd_progcache_publish failed: fd_funk_txn_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
358 0 : }
359 :
360 : /* Phase 6: Free transaction object */
361 :
362 33 : fd_rwlock_unwrite( txn->lock );
363 33 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
364 33 : txn->parent_cidx = UINT_MAX;
365 33 : txn->sibling_prev_cidx = UINT_MAX;
366 33 : txn->sibling_next_cidx = UINT_MAX;
367 33 : txn->child_head_cidx = UINT_MAX;
368 33 : txn->child_tail_cidx = UINT_MAX;
369 33 : fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
370 33 : }
371 :
372 : void
373 : fd_progcache_txn_advance_root( fd_progcache_admin_t * cache,
374 33 : fd_funk_txn_xid_t const * xid ) {
375 33 : fd_funk_t * funk = cache->funk;
376 :
377 33 : fd_funk_txn_t * txn = fd_funk_txn_query( xid, funk->txn_map );
378 33 : if( FD_UNLIKELY( !txn ) ) {
379 0 : FD_LOG_CRIT(( "fd_progcache_txn_advance_root failed: txn with xid %lu:%lu not found", xid->ul[0], xid->ul[1] ));
380 0 : }
381 :
382 33 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
383 0 : FD_LOG_CRIT(( "fd_progcache_txn_advance_root: parent of txn %lu:%lu is not root", xid->ul[0], xid->ul[1] ));
384 0 : }
385 :
386 33 : fd_progcache_txn_cancel_prev_list( cache, txn );
387 33 : fd_progcache_txn_cancel_next_list( cache, txn );
388 33 : txn->sibling_prev_cidx = UINT_MAX;
389 33 : txn->sibling_next_cidx = UINT_MAX;
390 :
391 : /* Children of transaction are now children of root */
392 33 : funk->shmem->child_head_cidx = txn->child_head_cidx;
393 33 : funk->shmem->child_tail_cidx = txn->child_tail_cidx;
394 :
395 33 : fd_progcache_txn_publish_one( cache, fd_funk_txn_xid( txn ) );
396 33 : }
397 :
398 : /* reset_txn_list does a depth-first traversal of the txn tree.
399 : Detaches all recs from txns by emptying rec linked lists. */
400 :
401 : static void
402 : reset_txn_list( fd_funk_t * funk,
403 0 : ulong txn_head_idx ) {
404 0 : fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
405 0 : for( ulong idx = txn_head_idx;
406 0 : !fd_funk_txn_idx_is_null( idx );
407 0 : ) {
408 0 : fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
409 0 : fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
410 0 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
411 0 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
412 0 : reset_txn_list( funk, txn->child_head_cidx );
413 0 : idx = fd_funk_txn_idx( txn->sibling_next_cidx );
414 0 : }
415 0 : }
416 :
417 : /* reset_rec_map frees all records in a funk instance. */
418 :
419 : static void
420 0 : reset_rec_map( fd_funk_t * funk ) {
421 0 : fd_wksp_t * wksp = funk->wksp;
422 0 : fd_alloc_t * alloc = funk->alloc;
423 0 : fd_funk_rec_map_t * rec_map = funk->rec_map;
424 0 : fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
425 :
426 0 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
427 0 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
428 0 : for(
429 0 : fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
430 0 : !fd_funk_rec_map_iter_done( iter );
431 0 : ) {
432 0 : fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( iter );
433 0 : ulong next = fd_funk_rec_map_private_idx( rec->map_next );;
434 :
435 : /* Remove rec object from map */
436 0 : fd_funk_rec_map_query_t rec_query[1];
437 0 : int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
438 0 : fd_funk_rec_key_t key; fd_funk_rec_key_copy( &key, rec->pair.key );
439 0 : if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
440 :
441 : /* Free rec resources */
442 0 : fd_funk_val_flush( rec, alloc, wksp );
443 0 : fd_funk_rec_pool_release( rec_pool, rec, 1 );
444 0 : iter.ele_idx = next;
445 0 : }
446 0 : }
447 0 : }
448 :
449 : void
450 0 : fd_progcache_reset( fd_progcache_admin_t * cache ) {
451 0 : fd_funk_t * funk = cache->funk;
452 0 : reset_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
453 0 : reset_rec_map( funk );
454 0 : }
455 :
456 : /* clear_txn_list does a depth-first traversal of the txn tree.
457 : Removes all txns. */
458 :
459 : static void
460 : clear_txn_list( fd_funk_t * funk,
461 0 : ulong txn_head_idx ) {
462 0 : fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
463 0 : fd_funk_txn_map_t * txn_map = funk->txn_map;
464 0 : for( ulong idx = txn_head_idx;
465 0 : !fd_funk_txn_idx_is_null( idx );
466 0 : ) {
467 0 : fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
468 0 : fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
469 0 : ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
470 0 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
471 0 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
472 0 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
473 0 : txn->child_head_cidx = UINT_MAX;
474 0 : txn->child_tail_cidx = UINT_MAX;
475 0 : txn->parent_cidx = UINT_MAX;
476 0 : txn->sibling_prev_cidx = UINT_MAX;
477 0 : txn->sibling_next_cidx = UINT_MAX;
478 0 : clear_txn_list( funk, child_idx );
479 0 : fd_funk_txn_map_query_t query[1];
480 0 : int rm_err = fd_funk_txn_map_remove( txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
481 0 : if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) ));
482 0 : txn->state = FD_FUNK_TXN_STATE_FREE;
483 0 : int free_err = fd_funk_txn_pool_release( txn_pool, txn, 1 );
484 0 : if( FD_UNLIKELY( free_err!=FD_POOL_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_pool_release failed (%i)", free_err ));
485 0 : idx = next_idx;
486 0 : }
487 0 : funk->shmem->child_head_cidx = UINT_MAX;
488 0 : funk->shmem->child_tail_cidx = UINT_MAX;
489 0 : }
490 :
491 : void
492 0 : fd_progcache_clear( fd_progcache_admin_t * cache ) {
493 0 : fd_funk_t * funk = cache->funk;
494 0 : clear_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
495 0 : reset_rec_map( funk );
496 0 : }
497 :
498 : void
499 42 : fd_progcache_verify( fd_progcache_admin_t * cache ) {
500 42 : FD_TEST( fd_funk_verify( cache->funk )==FD_FUNK_SUCCESS );
501 42 : }
|