Line data Source code
1 : #include "fd_accdb_admin_v1.h"
2 : #include "../fd_flamenco_base.h"
3 :
4 : FD_STATIC_ASSERT( alignof(fd_accdb_admin_v1_t)<=alignof(fd_accdb_admin_t), layout );
5 : FD_STATIC_ASSERT( sizeof (fd_accdb_admin_v1_t)<=sizeof(fd_accdb_admin_t), layout );
6 :
7 : fd_accdb_admin_t *
8 : fd_accdb_admin_v1_init( fd_accdb_admin_t * admin_,
9 : void * shfunk,
10 12 : void * shlocks ) {
11 12 : if( FD_UNLIKELY( !admin_ ) ) {
12 0 : FD_LOG_WARNING(( "NULL ljoin" ));
13 0 : return NULL;
14 0 : }
15 :
16 12 : fd_accdb_admin_v1_t * admin = (fd_accdb_admin_v1_t *)admin_;
17 12 : memset( admin, 0, sizeof(fd_accdb_admin_t) );
18 12 : admin->base.accdb_type = FD_ACCDB_TYPE_V1;
19 12 : admin->base.vt = &fd_accdb_admin_v1_vt;
20 :
21 12 : if( FD_UNLIKELY( !fd_funk_join( admin->funk, shfunk, shlocks ) ) ) {
22 0 : FD_LOG_CRIT(( "fd_funk_join failed" ));
23 0 : }
24 :
25 12 : return admin_;
26 12 : }
27 :
28 : static fd_accdb_admin_v1_t *
29 109514 : downcast( fd_accdb_admin_t * admin ) {
30 109514 : if( FD_UNLIKELY( !admin ) ) {
31 0 : FD_LOG_CRIT(( "NULL admin" ));
32 0 : }
33 109514 : if( FD_UNLIKELY( admin->base.accdb_type!=FD_ACCDB_TYPE_V1 ) ) {
34 0 : FD_LOG_CRIT(( "corrupt accdb_admin handle" ));
35 0 : }
36 109514 : return (fd_accdb_admin_v1_t *)admin;
37 109514 : }
38 :
39 : void
40 12 : fd_accdb_admin_v1_fini( fd_accdb_admin_t * admin_ ) {
41 12 : fd_accdb_admin_v1_t * admin = downcast( admin_ );
42 12 : if( FD_UNLIKELY( !fd_funk_leave( admin->funk, NULL, NULL ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
43 12 : memset( admin, 0, sizeof(fd_accdb_admin_base_t) );
44 12 : }
45 :
46 : fd_funk_t *
47 24607 : fd_accdb_admin_v1_funk( fd_accdb_admin_t * admin ) {
48 24607 : fd_accdb_admin_v1_t * a = downcast( admin );
49 24607 : return a->funk;
50 24607 : }
51 :
52 : fd_funk_txn_xid_t
53 0 : fd_accdb_v1_root_get( fd_accdb_admin_t const * admin_ ) {
54 0 : fd_accdb_admin_v1_t const * admin = (fd_accdb_admin_v1_t const *)admin_;
55 0 : return *fd_funk_last_publish( admin->funk );
56 0 : }
57 :
58 : /* Begin transaction-level operations. It is assumed that funk_txn data
59 : structures are not concurrently modified. This includes txn_pool and
60 : txn_map. */
61 :
62 : void
63 : fd_accdb_v1_attach_child( fd_accdb_admin_t * db_,
64 : fd_funk_txn_xid_t const * xid_parent,
65 54860 : fd_funk_txn_xid_t const * xid_new ) {
66 54860 : fd_accdb_admin_v1_t * db = downcast( db_ );
67 54860 : FD_LOG_INFO(( "accdb txn xid %lu:%lu: created with parent %lu:%lu",
68 54860 : xid_new ->ul[0], xid_new ->ul[1],
69 54860 : xid_parent->ul[0], xid_parent->ul[1] ));
70 54860 : fd_funk_txn_prepare( db->funk, xid_parent, xid_new );
71 54860 : }
72 :
73 : static void
74 : fd_accdb_txn_cancel_one( fd_accdb_admin_v1_t * admin,
75 0 : fd_funk_txn_t * txn ) {
76 0 : FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: cancel", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
77 :
78 0 : if( FD_UNLIKELY( txn->state!=FD_FUNK_TXN_STATE_ACTIVE ) ) {
79 0 : FD_LOG_CRIT(( "cannot cancel xid %lu:%lu: unxpected state %u-%s",
80 0 : txn->xid.ul[0], txn->xid.ul[1],
81 0 : txn->state, fd_funk_txn_state_str( txn->state ) ));
82 0 : }
83 0 : fd_funk_t * funk = admin->funk;
84 0 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( txn->child_head_cidx ) ||
85 0 : !fd_funk_txn_idx_is_null( txn->child_tail_cidx ) ) ) {
86 0 : FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: txn at %p with xid %lu:%lu has children (data corruption?)",
87 0 : (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
88 0 : }
89 :
90 : /* Phase 1: Drain users from transaction */
91 :
92 0 : ulong txn_idx = (ulong)( txn - funk->txn_pool->ele );
93 0 : fd_rwlock_write( &funk->txn_lock[ txn_idx ] );
94 0 : FD_COMPILER_MFENCE();
95 0 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_CANCEL;
96 :
97 : /* Phase 2: Detach all records */
98 :
99 0 : FD_COMPILER_MFENCE();
100 0 : uint const rec_head_idx = txn->rec_head_idx;
101 0 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
102 0 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
103 0 : FD_COMPILER_MFENCE();
104 :
105 : /* Phase 3: Remove records */
106 :
107 0 : ulong rec_cnt = 0UL;
108 0 : uint rec_idx = rec_head_idx;
109 0 : while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
110 0 : fd_funk_rec_t * rec = &funk->rec_pool->ele[ rec_idx ];
111 0 : fd_funk_xid_key_pair_t pair = FD_VOLATILE_CONST( rec->pair );
112 :
113 0 : uint next_idx = rec->next_idx;
114 0 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( pair.xid, &txn->xid ) ) ) {
115 0 : FD_LOG_CRIT(( "Record does not belong to txn being cancelled (data corruption?): rec_idx=%u", rec_idx ));
116 0 : }
117 :
118 : /* Phase 3.1: Hide record */
119 :
120 0 : fd_funk_rec_query_t query[1];
121 0 : int remove_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
122 0 : if( FD_UNLIKELY( remove_err ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
123 0 : if( FD_UNLIKELY( query->ele!=rec ) ) FD_LOG_CRIT(( "Found duplicate record in map idx[0]=%p idx[1]=%p", (void *)query->ele, (void *)rec ));
124 :
125 : /* Phase 3.2: Mark record as invalid */
126 :
127 0 : FD_COMPILER_MFENCE();
128 0 : memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
129 0 : FD_COMPILER_MFENCE();
130 :
131 : /* Phase 3.3: Free record */
132 :
133 0 : fd_funk_val_flush( rec, funk->alloc, funk->wksp );
134 0 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
135 0 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
136 0 : funk->rec_lock[ rec_idx ] = fd_funk_rec_ver_lock( fd_funk_rec_ver_inc( fd_funk_rec_ver_bits( funk->rec_lock[ rec_idx ] ) ), 0UL );
137 0 : fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
138 0 : rec_idx = next_idx;
139 0 : rec_cnt++;
140 0 : }
141 0 : admin->base.revert_cnt += rec_cnt;
142 0 : FD_LOG_INFO(( "accdb freed %lu records while cancelling txn %lu:%lu",
143 0 : rec_cnt, txn->xid.ul[0], txn->xid.ul[1] ));
144 :
145 : /* Phase 4: Remove transaction from fork graph */
146 :
147 0 : uint self_cidx = fd_funk_txn_cidx( (ulong)( txn-funk->txn_pool->ele ) );
148 0 : uint prev_cidx = txn->sibling_prev_cidx; ulong prev_idx = fd_funk_txn_idx( prev_cidx );
149 0 : uint next_cidx = txn->sibling_next_cidx; ulong next_idx = fd_funk_txn_idx( next_cidx );
150 0 : if( !fd_funk_txn_idx_is_null( next_idx ) ) {
151 0 : funk->txn_pool->ele[ next_idx ].sibling_prev_cidx = prev_cidx;
152 0 : }
153 0 : if( !fd_funk_txn_idx_is_null( prev_idx ) ) {
154 0 : funk->txn_pool->ele[ prev_idx ].sibling_next_cidx = next_cidx;
155 0 : }
156 0 : if( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) {
157 0 : fd_funk_txn_t * parent = &funk->txn_pool->ele[ fd_funk_txn_idx( txn->parent_cidx ) ];
158 0 : if( parent->child_head_cidx==self_cidx ) parent->child_head_cidx = next_cidx;
159 0 : if( parent->child_tail_cidx==self_cidx ) parent->child_tail_cidx = prev_cidx;
160 0 : } else {
161 0 : if( funk->shmem->child_head_cidx==self_cidx ) funk->shmem->child_head_cidx = next_cidx;
162 0 : if( funk->shmem->child_tail_cidx==self_cidx ) funk->shmem->child_tail_cidx = prev_cidx;
163 0 : }
164 :
165 : /* Phase 5: Remove transaction from index */
166 :
167 0 : fd_funk_txn_map_query_t query[1];
168 0 : int remove_err = fd_funk_txn_map_remove( funk->txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
169 0 : if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
170 0 : FD_LOG_CRIT(( "fd_accdb_txn_cancel failed: fd_funk_txn_map_remove(%lu:%lu) failed: %i-%s",
171 0 : txn->xid.ul[0], txn->xid.ul[1], remove_err, fd_map_strerror( remove_err ) ));
172 0 : }
173 :
174 : /* Phase 6: Free transaction object */
175 :
176 0 : txn->parent_cidx = UINT_MAX;
177 0 : txn->sibling_prev_cidx = UINT_MAX;
178 0 : txn->sibling_next_cidx = UINT_MAX;
179 0 : fd_rwlock_unwrite( &funk->txn_lock[ txn_idx ] );
180 0 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
181 0 : fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
182 0 : }
183 :
184 : /* Cancels txn and all children */
185 :
186 : static void
187 : fd_accdb_txn_cancel_tree( fd_accdb_admin_v1_t * accdb,
188 0 : fd_funk_txn_t * txn ) {
189 0 : for(;;) {
190 0 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
191 0 : if( fd_funk_txn_idx_is_null( child_idx ) ) break;
192 0 : fd_funk_txn_t * child = &accdb->funk->txn_pool->ele[ child_idx ];
193 0 : fd_accdb_txn_cancel_tree( accdb, child );
194 0 : }
195 0 : fd_accdb_txn_cancel_one( accdb, txn );
196 0 : }
197 :
198 : /* Cancels all left/right siblings */
199 :
200 : static void
201 : fd_accdb_txn_cancel_prev_list( fd_accdb_admin_v1_t * accdb,
202 0 : fd_funk_txn_t * txn ) {
203 0 : ulong self_idx = (ulong)( txn - accdb->funk->txn_pool->ele );
204 0 : for(;;) {
205 0 : ulong prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
206 0 : if( FD_UNLIKELY( prev_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
207 0 : if( fd_funk_txn_idx_is_null( prev_idx ) ) break;
208 0 : fd_funk_txn_t * sibling = &accdb->funk->txn_pool->ele[ prev_idx ];
209 0 : fd_accdb_txn_cancel_tree( accdb, sibling );
210 0 : }
211 0 : }
212 :
213 : static void
214 : fd_accdb_txn_cancel_next_list( fd_accdb_admin_v1_t * accdb,
215 0 : fd_funk_txn_t * txn ) {
216 0 : ulong self_idx = (ulong)( txn - accdb->funk->txn_pool->ele );
217 0 : for(;;) {
218 0 : ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
219 0 : if( FD_UNLIKELY( next_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
220 0 : if( fd_funk_txn_idx_is_null( next_idx ) ) break;
221 0 : fd_funk_txn_t * sibling = &accdb->funk->txn_pool->ele[ next_idx ];
222 0 : fd_accdb_txn_cancel_tree( accdb, sibling );
223 0 : }
224 0 : }
225 :
226 : void
227 : fd_accdb_txn_cancel_siblings( fd_accdb_admin_v1_t * accdb,
228 0 : fd_funk_txn_t * txn ) {
229 0 : fd_accdb_txn_cancel_prev_list( accdb, txn );
230 0 : fd_accdb_txn_cancel_next_list( accdb, txn );
231 0 : txn->sibling_prev_cidx = UINT_MAX;
232 0 : txn->sibling_next_cidx = UINT_MAX;
233 0 : }
234 :
235 : void
236 : fd_accdb_v1_cancel( fd_accdb_admin_t * accdb_,
237 0 : fd_funk_txn_xid_t const * xid ) {
238 0 : fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
239 0 : fd_funk_t * funk = accdb->funk;
240 :
241 : /* Assume no concurrent access to txn_map */
242 :
243 0 : fd_funk_txn_map_query_t query[1];
244 0 : int query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 );
245 0 : if( FD_UNLIKELY( query_err ) ) {
246 0 : FD_LOG_CRIT(( "fd_accdb_cancel failed: fd_funk_txn_map_query_try(xid=%lu:%lu) returned (%i-%s)",
247 0 : xid->ul[0], xid->ul[1], query_err, fd_map_strerror( query_err ) ));
248 0 : }
249 0 : fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( query );
250 :
251 0 : fd_accdb_txn_cancel_tree( accdb, txn );
252 0 : }
253 :
254 : /* fd_accdb_chain_reclaim "reclaims" a zero-lamport account by removing
255 : its underlying record. */
256 :
257 : static void
258 : fd_accdb_chain_reclaim( fd_accdb_admin_v1_t * accdb,
259 0 : fd_funk_rec_t * rec ) {
260 0 : fd_funk_t * funk = accdb->funk;
261 :
262 : /* Phase 1: Remove record from map */
263 :
264 0 : fd_funk_xid_key_pair_t pair = rec->pair;
265 0 : fd_funk_rec_query_t query[1];
266 0 : int rm_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
267 0 : 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 ) ));
268 0 : FD_COMPILER_MFENCE();
269 :
270 : /* Phase 2: Invalidate record */
271 :
272 0 : fd_funk_rec_t * old_rec = query->ele;
273 0 : memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
274 0 : FD_COMPILER_MFENCE();
275 :
276 : /* Phase 3: Free record */
277 :
278 0 : old_rec->map_next = FD_FUNK_REC_IDX_NULL;
279 0 : fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
280 0 : fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
281 0 : accdb->base.reclaim_cnt++;
282 0 : }
283 :
284 : /* fd_accdb_chain_gc_root cleans up a stale "rooted" version of a
285 : record. */
286 :
287 : static void
288 : fd_accdb_chain_gc_root( fd_accdb_admin_v1_t * accdb,
289 0 : fd_funk_xid_key_pair_t const * pair ) {
290 0 : fd_funk_t * funk = accdb->funk;
291 :
292 : /* Phase 1: Remove record from map if found */
293 :
294 0 : fd_funk_rec_query_t query[1];
295 0 : int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
296 0 : if( rm_err==FD_MAP_ERR_KEY ) return;
297 0 : 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 ) ));
298 0 : FD_COMPILER_MFENCE();
299 :
300 : /* Phase 2: Invalidate record */
301 :
302 0 : fd_funk_rec_t * old_rec = query->ele;
303 0 : memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
304 0 : FD_COMPILER_MFENCE();
305 :
306 : /* Phase 3: Free record */
307 :
308 0 : old_rec->map_next = FD_FUNK_REC_IDX_NULL;
309 0 : fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
310 0 : fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
311 0 : accdb->base.gc_root_cnt++;
312 0 : }
313 :
314 : /* fd_accdb_publish_recs moves all records in a transaction to the DB
315 : root. Currently, the DB root is stored by funk, which might change
316 : in the future.
317 :
318 : It is assumed at this point that the txn has no more concurrent
319 : users. */
320 :
321 : static void
322 : fd_accdb_publish_recs( fd_accdb_admin_v1_t * accdb,
323 0 : fd_funk_txn_t * txn ) {
324 : /* Iterate record list */
325 0 : uint head = txn->rec_head_idx;
326 0 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
327 0 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
328 0 : fd_wksp_t * funk_wksp = accdb->funk->wksp;
329 0 : while( !fd_funk_rec_idx_is_null( head ) ) {
330 0 : fd_funk_rec_t * rec = &accdb->funk->rec_pool->ele[ head ];
331 :
332 : /* Evict previous value from hash chain */
333 0 : fd_funk_xid_key_pair_t pair[1];
334 0 : fd_funk_rec_key_copy( pair->key, rec->pair.key );
335 0 : fd_funk_txn_xid_set_root( pair->xid );
336 0 : fd_accdb_chain_gc_root( accdb, pair );
337 :
338 : /* Root or reclaim record */
339 0 : uint next = rec->next_idx;
340 0 : fd_account_meta_t const * meta = fd_funk_val( rec, funk_wksp );
341 0 : FD_CRIT( meta && rec->val_sz>=sizeof(fd_account_meta_t), "invalid funk record value" );
342 0 : if( !meta->lamports ) {
343 : /* Remove record */
344 0 : fd_accdb_chain_reclaim( accdb, rec );
345 0 : } else {
346 : /* Migrate record to root */
347 0 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
348 0 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
349 0 : fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } };
350 0 : fd_funk_txn_xid_st_atomic( rec->pair.xid, &root );
351 0 : accdb->base.root_cnt++;
352 0 : accdb->base.root_tot_sz += rec->val_sz;
353 0 : }
354 :
355 0 : head = next; /* next record */
356 0 : }
357 0 : }
358 :
359 : /* fd_accdb_txn_publish_one merges an in-prep transaction whose
360 : parent is the last published, into the parent. */
361 :
362 : static void
363 : fd_accdb_txn_publish_one( fd_accdb_admin_v1_t * accdb,
364 0 : fd_funk_txn_t * txn ) {
365 0 : fd_funk_t * funk = accdb->funk;
366 :
367 : /* Phase 1: Mark transaction as "last published" */
368 :
369 0 : fd_funk_txn_xid_t xid[1]; fd_funk_txn_xid_copy( xid, fd_funk_txn_xid( txn ) );
370 0 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
371 0 : FD_LOG_CRIT(( "fd_accdb_publish failed: txn with xid %lu:%lu is not a child of the last published txn", xid->ul[0], xid->ul[1] ));
372 0 : }
373 0 : fd_funk_txn_xid_st_atomic( funk->shmem->last_publish, xid );
374 0 : FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: publish", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
375 :
376 : /* Phase 2: Drain users from transaction */
377 :
378 0 : ulong txn_idx = (ulong)( txn - funk->txn_pool->ele );
379 0 : fd_rwlock_write( &funk->txn_lock[ txn_idx ] );
380 0 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_PUBLISH;
381 :
382 : /* Phase 3: Migrate records */
383 :
384 0 : fd_accdb_publish_recs( accdb, txn );
385 :
386 : /* Phase 4: Remove transaction from fork graph
387 :
388 : Because the transaction has no more records, removing it from the
389 : fork graph has no visible side effects to concurrent query ops
390 : (always return "no found") or insert ops (refuse to write to a
391 : "publish" state txn). */
392 :
393 0 : { /* Adjust the parent pointers of the children to point to "last published" */
394 0 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
395 0 : while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
396 0 : funk->txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
397 0 : child_idx = fd_funk_txn_idx( funk->txn_pool->ele[ child_idx ].sibling_next_cidx );
398 0 : }
399 0 : }
400 :
401 : /* Phase 5: Remove transaction from index
402 :
403 : The transaction is now an orphan and won't get any new records. */
404 :
405 0 : fd_funk_txn_map_query_t query[1];
406 0 : int remove_err = fd_funk_txn_map_remove( funk->txn_map, xid, NULL, query, 0 );
407 0 : if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
408 0 : FD_LOG_CRIT(( "fd_accdb_publish failed: fd_funk_txn_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
409 0 : }
410 :
411 : /* Phase 6: Free transaction object */
412 :
413 0 : fd_rwlock_unwrite( &funk->txn_lock[ txn_idx ] );
414 0 : FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
415 0 : txn->parent_cidx = UINT_MAX;
416 0 : txn->sibling_prev_cidx = UINT_MAX;
417 0 : txn->sibling_next_cidx = UINT_MAX;
418 0 : txn->child_head_cidx = UINT_MAX;
419 0 : txn->child_tail_cidx = UINT_MAX;
420 0 : fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
421 0 : }
422 :
423 : void
424 : fd_accdb_v1_advance_root( fd_accdb_admin_t * accdb_,
425 0 : fd_funk_txn_xid_t const * xid ) {
426 0 : fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
427 0 : fd_funk_t * funk = accdb->funk;
428 :
429 : /* Assume no concurrent access to txn_map */
430 :
431 0 : long dt = -fd_tickcount();
432 0 : fd_funk_txn_map_query_t query[1];
433 0 : int query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 );
434 0 : if( FD_UNLIKELY( query_err ) ) {
435 0 : FD_LOG_CRIT(( "fd_accdb_advance_root failed: fd_funk_txn_map_query_try(xid=%lu:%lu) returned (%i-%s)",
436 0 : xid->ul[0], xid->ul[1], query_err, fd_map_strerror( query_err ) ));
437 0 : }
438 0 : fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( query );
439 :
440 0 : if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
441 0 : FD_LOG_CRIT(( "fd_accdb_txn_advance_root: parent of txn %lu:%lu is not root", xid->ul[0], xid->ul[1] ));
442 0 : }
443 :
444 0 : FD_LOG_INFO(( "accdb txn laddr=%p xid %lu:%lu: advancing root",
445 0 : (void *)txn,
446 0 : xid->ul[0], xid->ul[1] ));
447 :
448 0 : fd_accdb_txn_cancel_siblings( accdb, txn );
449 :
450 : /* Children of transaction are now children of root */
451 0 : funk->shmem->child_head_cidx = txn->child_head_cidx;
452 0 : funk->shmem->child_tail_cidx = txn->child_tail_cidx;
453 :
454 0 : fd_accdb_txn_publish_one( accdb, txn );
455 :
456 0 : dt += fd_tickcount();
457 0 : accdb->base.dt_gc += dt;
458 0 : }
459 :
460 : /* reset_rec_map frees all records in a funk instance. */
461 :
462 : static void
463 30112 : reset_rec_map( fd_funk_t * funk ) {
464 30112 : fd_wksp_t * wksp = funk->wksp;
465 30112 : fd_alloc_t * alloc = funk->alloc;
466 30112 : fd_funk_rec_map_t * rec_map = funk->rec_map;
467 30112 : fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
468 :
469 30112 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
470 15304807 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
471 15274695 : for(
472 15274695 : fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
473 15405118 : !fd_funk_rec_map_iter_done( iter );
474 15274695 : ) {
475 130423 : fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( iter );
476 130423 : ulong next = fd_funk_rec_map_private_idx( rec->map_next );;
477 :
478 : /* Remove rec object from map */
479 130423 : fd_funk_rec_map_query_t rec_query[1];
480 130423 : int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
481 130423 : fd_funk_rec_key_t key; fd_funk_rec_key_copy( &key, rec->pair.key );
482 130423 : if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
483 :
484 : /* Free rec resources */
485 130423 : rec->map_next = FD_FUNK_REC_IDX_NULL;
486 130423 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
487 130423 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
488 130423 : memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
489 130423 : fd_funk_val_flush( rec, alloc, wksp );
490 130423 : fd_funk_rec_pool_release( rec_pool, rec, 1 );
491 130423 : iter.ele_idx = next;
492 130423 : }
493 15274695 : }
494 30112 : }
495 :
496 : /* clear_txn_list does a depth-first traversal of the txn tree.
497 : Removes all txns. */
498 :
499 : static void
500 : clear_txn_list( fd_funk_t * funk,
501 85114 : ulong txn_head_idx ) {
502 85114 : fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
503 85114 : fd_funk_txn_map_t * txn_map = funk->txn_map;
504 85114 : for( ulong idx = txn_head_idx;
505 140118 : !fd_funk_txn_idx_is_null( idx );
506 85114 : ) {
507 55004 : fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
508 55004 : fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
509 55004 : ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
510 55004 : ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
511 55004 : txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
512 55004 : txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
513 55004 : txn->child_head_cidx = UINT_MAX;
514 55004 : txn->child_tail_cidx = UINT_MAX;
515 55004 : txn->parent_cidx = UINT_MAX;
516 55004 : txn->sibling_prev_cidx = UINT_MAX;
517 55004 : txn->sibling_next_cidx = UINT_MAX;
518 55004 : clear_txn_list( funk, child_idx );
519 55004 : fd_funk_txn_map_query_t query[1];
520 55004 : int rm_err = fd_funk_txn_map_remove( txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
521 55004 : 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 ) ));
522 55004 : txn->state = FD_FUNK_TXN_STATE_FREE;
523 55004 : int free_err = fd_funk_txn_pool_release( txn_pool, txn, 1 );
524 55004 : if( FD_UNLIKELY( free_err!=FD_POOL_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_pool_release failed (%i)", free_err ));
525 55004 : idx = next_idx;
526 55004 : }
527 85114 : }
528 :
529 : void
530 30135 : fd_accdb_v1_clear( fd_accdb_admin_t * accdb_ ) {
531 30135 : fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
532 30135 : fd_funk_t * funk = accdb->funk;
533 30135 : clear_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
534 30135 : funk->shmem->child_head_cidx = UINT_MAX;
535 30135 : funk->shmem->child_tail_cidx = UINT_MAX;
536 30135 : reset_rec_map( funk );
537 30135 : }
538 :
539 : void
540 0 : fd_accdb_v1_verify( fd_accdb_admin_t * accdb_ ) {
541 0 : fd_accdb_admin_v1_t * accdb = downcast( accdb_ );
542 0 : FD_TEST( fd_funk_verify( accdb->funk )==FD_FUNK_SUCCESS );
543 0 : }
544 :
545 : fd_accdb_admin_vt_t const fd_accdb_admin_v1_vt = {
546 : .fini = fd_accdb_admin_v1_fini,
547 : .root_get = fd_accdb_v1_root_get,
548 : .attach_child = fd_accdb_v1_attach_child,
549 : .advance_root = fd_accdb_v1_advance_root,
550 : .cancel = fd_accdb_v1_cancel
551 : };
|