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