Line data Source code
1 : #include "fd_funk.h"
2 : #include "fd_funk_base.h"
3 : #include <stdio.h>
4 :
5 : ulong
6 147 : fd_funk_align( void ) {
7 147 : return FD_FUNK_ALIGN;
8 147 : }
9 :
10 : ulong
11 : fd_funk_footprint( ulong txn_max,
12 57 : ulong rec_max ) {
13 57 : if( FD_UNLIKELY( rec_max>UINT_MAX ) ) return 0UL;
14 :
15 57 : ulong l = FD_LAYOUT_INIT;
16 :
17 57 : l = FD_LAYOUT_APPEND( l, alignof(fd_funk_shmem_t), sizeof(fd_funk_shmem_t) );
18 :
19 57 : ulong txn_chain_cnt = fd_funk_txn_map_chain_cnt_est( txn_max );
20 57 : l = FD_LAYOUT_APPEND( l, fd_funk_txn_map_align(), fd_funk_txn_map_footprint( txn_chain_cnt ) );
21 57 : l = FD_LAYOUT_APPEND( l, fd_funk_txn_pool_align(), fd_funk_txn_pool_footprint() );
22 57 : l = FD_LAYOUT_APPEND( l, alignof(fd_funk_txn_t), sizeof(fd_funk_txn_t) * txn_max );
23 :
24 57 : ulong rec_chain_cnt = fd_funk_rec_map_chain_cnt_est( rec_max );
25 57 : l = FD_LAYOUT_APPEND( l, fd_funk_rec_map_align(), fd_funk_rec_map_footprint( rec_chain_cnt ) );
26 57 : l = FD_LAYOUT_APPEND( l, fd_funk_rec_pool_align(), fd_funk_rec_pool_footprint() );
27 57 : l = FD_LAYOUT_APPEND( l, alignof(fd_funk_rec_t), sizeof(fd_funk_rec_t) * rec_max );
28 :
29 57 : l = FD_LAYOUT_APPEND( l, fd_alloc_align(), fd_alloc_footprint() );
30 :
31 57 : return l;
32 57 : }
33 :
34 : /* TODO: Consider letter user just passing a join of alloc to use,
35 : inferring the backing wksp and cgroup_hint from that and then
36 : allocating exclusively from that? */
37 :
38 : void *
39 : fd_funk_new( void * shmem,
40 : ulong wksp_tag,
41 : ulong seed,
42 : ulong txn_max,
43 48 : ulong rec_max ) {
44 48 : fd_funk_shmem_t * funk = shmem;
45 48 : fd_wksp_t * wksp = fd_wksp_containing( funk );
46 :
47 48 : if( FD_UNLIKELY( !funk ) ) {
48 3 : FD_LOG_WARNING(( "NULL funk" ));
49 3 : return NULL;
50 3 : }
51 :
52 45 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)funk, fd_funk_align() ) ) ) {
53 3 : FD_LOG_WARNING(( "misaligned funk" ));
54 3 : return NULL;
55 3 : }
56 :
57 42 : if( FD_UNLIKELY( !wksp_tag ) ) {
58 3 : FD_LOG_WARNING(( "bad wksp_tag" ));
59 3 : return NULL;
60 3 : }
61 :
62 39 : if( FD_UNLIKELY( !wksp ) ) {
63 3 : FD_LOG_WARNING(( "shmem must be part of a workspace" ));
64 3 : return NULL;
65 3 : }
66 :
67 36 : if( FD_UNLIKELY( txn_max>FD_FUNK_TXN_IDX_NULL ) ) { /* See note in fd_funk.h about this limit */
68 3 : FD_LOG_WARNING(( "txn_max too large for index compression" ));
69 3 : return NULL;
70 3 : }
71 :
72 33 : if( FD_UNLIKELY( rec_max>UINT_MAX ) ) {
73 0 : FD_LOG_WARNING(( "invalid rec_max" ));
74 0 : return NULL;
75 0 : }
76 :
77 33 : FD_SCRATCH_ALLOC_INIT( l, funk+1 );
78 :
79 33 : ulong txn_chain_cnt = fd_funk_txn_map_chain_cnt_est( txn_max );
80 33 : void * txn_map = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_txn_map_align(), fd_funk_txn_map_footprint( txn_chain_cnt ) );
81 33 : void * txn_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_txn_pool_align(), fd_funk_txn_pool_footprint() );
82 33 : fd_funk_txn_t * txn_ele = (fd_funk_txn_t *)FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_funk_txn_t), sizeof(fd_funk_txn_t) * txn_max );
83 :
84 0 : ulong rec_chain_cnt = fd_funk_rec_map_chain_cnt_est( rec_max );
85 33 : void * rec_map = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_rec_map_align(), fd_funk_rec_map_footprint( rec_chain_cnt ) );
86 33 : void * rec_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_funk_rec_pool_align(), fd_funk_rec_pool_footprint() );
87 33 : fd_funk_rec_t * rec_ele = (fd_funk_rec_t *)FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_funk_rec_t), sizeof(fd_funk_rec_t) * rec_max );
88 :
89 33 : void * alloc = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(), fd_alloc_footprint() );
90 :
91 33 : FD_TEST( _l == (ulong)funk + fd_funk_footprint( txn_max, rec_max ) );
92 :
93 33 : fd_memset( funk, 0, sizeof(fd_funk_shmem_t) );
94 :
95 33 : funk->funk_gaddr = fd_wksp_gaddr_fast( wksp, funk );
96 33 : funk->wksp_tag = wksp_tag;
97 33 : funk->seed = seed;
98 33 : funk->cycle_tag = 3UL; /* various verify functions use tags 0-2 */
99 :
100 33 : funk->txn_map_gaddr = fd_wksp_gaddr_fast( wksp, fd_funk_txn_map_new( txn_map, txn_chain_cnt, seed ) );
101 33 : void * txn_pool2 = fd_funk_txn_pool_new( txn_pool );
102 33 : funk->txn_pool_gaddr = fd_wksp_gaddr_fast( wksp, txn_pool2 );
103 33 : fd_funk_txn_pool_t txn_join[1];
104 33 : fd_funk_txn_pool_join( txn_join, txn_pool2, txn_ele, txn_max );
105 33 : fd_funk_txn_pool_reset( txn_join, 0UL );
106 33 : funk->txn_ele_gaddr = fd_wksp_gaddr_fast( wksp, txn_ele );
107 33 : funk->txn_max = txn_max;
108 33 : funk->child_head_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
109 33 : funk->child_tail_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
110 :
111 33 : fd_funk_txn_xid_set_root( funk->root );
112 33 : fd_funk_txn_xid_set_root( funk->last_publish );
113 :
114 33 : funk->rec_map_gaddr = fd_wksp_gaddr_fast( wksp, fd_funk_rec_map_new( rec_map, rec_chain_cnt, seed ) );
115 33 : void * rec_pool2 = fd_funk_rec_pool_new( rec_pool );
116 33 : funk->rec_pool_gaddr = fd_wksp_gaddr_fast( wksp, rec_pool2 );
117 33 : fd_funk_rec_pool_t rec_join[1];
118 33 : fd_funk_rec_pool_join( rec_join, rec_pool2, rec_ele, rec_max );
119 33 : fd_funk_rec_pool_reset( rec_join, 0UL );
120 33 : funk->rec_ele_gaddr = fd_wksp_gaddr_fast( wksp, rec_ele );
121 33 : funk->rec_max = (uint)rec_max;
122 33 : funk->rec_head_idx = FD_FUNK_REC_IDX_NULL;
123 33 : funk->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
124 :
125 33 : funk->alloc_gaddr = fd_wksp_gaddr_fast( wksp, fd_alloc_join( fd_alloc_new( alloc, wksp_tag ), 0UL ) );
126 :
127 33 : funk->lock = 0;
128 :
129 33 : FD_COMPILER_MFENCE();
130 33 : FD_VOLATILE( funk->magic ) = FD_FUNK_MAGIC;
131 33 : FD_COMPILER_MFENCE();
132 :
133 33 : return (void *)funk;
134 33 : }
135 :
136 : fd_funk_t *
137 : fd_funk_join( fd_funk_t * ljoin,
138 45 : void * shfunk ) {
139 45 : if( FD_UNLIKELY( !shfunk ) ) {
140 3 : FD_LOG_WARNING(( "NULL shfunk" ));
141 3 : return NULL;
142 3 : }
143 :
144 42 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shfunk, fd_funk_align() ) ) ) {
145 3 : FD_LOG_WARNING(( "misaligned shfunk" ));
146 3 : return NULL;
147 3 : }
148 :
149 39 : fd_wksp_t * wksp = fd_wksp_containing( shfunk );
150 39 : if( FD_UNLIKELY( !wksp ) ) {
151 3 : FD_LOG_WARNING(( "shfunk must be part of a workspace" ));
152 3 : return NULL;
153 3 : }
154 :
155 36 : fd_funk_shmem_t * shmem = shfunk;
156 36 : if( FD_UNLIKELY( shmem->magic!=FD_FUNK_MAGIC ) ) {
157 3 : if (shmem->magic == FD_FUNK_MAGIC+1) {
158 0 : FD_LOG_WARNING(( "attempted to join a funk that crashed in a critical section" ));
159 3 : } else {
160 3 : FD_LOG_WARNING(( "bad magic" ));
161 3 : }
162 3 : return NULL;
163 3 : }
164 :
165 33 : if( FD_UNLIKELY( !ljoin ) ) {
166 0 : FD_LOG_WARNING(( "NULL ljoin" ));
167 0 : return NULL;
168 0 : }
169 :
170 : #ifdef FD_FUNK_WKSP_PROTECT
171 : fd_wksp_mprotect( wksp, 1 );
172 : #endif
173 :
174 33 : fd_funk_t * funk = ljoin;
175 33 : memset( funk, 0, sizeof(fd_funk_t) );
176 :
177 33 : funk->shmem = shfunk;
178 33 : funk->wksp = wksp;
179 :
180 33 : if( FD_UNLIKELY( !fd_funk_txn_pool_join( funk->txn_pool, fd_wksp_laddr( wksp, shmem->txn_pool_gaddr ), fd_wksp_laddr( wksp, shmem->txn_ele_gaddr ), shmem->txn_max ) ) ) {
181 0 : FD_LOG_WARNING(( "failed to join txn_pool" ));
182 0 : return NULL;
183 0 : }
184 33 : if( FD_UNLIKELY( !fd_funk_txn_map_join( funk->txn_map, fd_wksp_laddr( wksp, shmem->txn_map_gaddr ), fd_wksp_laddr_fast( wksp, shmem->txn_ele_gaddr ), shmem->txn_max ) ) ) {
185 0 : FD_LOG_WARNING(( "failed to join txn_map" ));
186 0 : return NULL;
187 0 : }
188 33 : if( FD_UNLIKELY( !fd_funk_rec_map_join( funk->rec_map, fd_wksp_laddr( wksp, shmem->rec_map_gaddr ), fd_wksp_laddr( wksp, shmem->rec_ele_gaddr ), shmem->rec_max ) ) ) {
189 0 : FD_LOG_WARNING(( "failed to join rec_map" ));
190 0 : return NULL;
191 0 : }
192 33 : if( FD_UNLIKELY( !fd_funk_rec_pool_join( funk->rec_pool, fd_wksp_laddr( wksp, shmem->rec_pool_gaddr ), fd_wksp_laddr_fast( wksp, shmem->rec_ele_gaddr ), shmem->rec_max ) ) ) {
193 0 : FD_LOG_WARNING(( "failed to join rec_pool" ));
194 0 : return NULL;
195 0 : }
196 33 : funk->alloc = fd_wksp_laddr( wksp, shmem->alloc_gaddr );
197 33 : if( FD_UNLIKELY( !fd_alloc_join( funk->alloc, fd_tile_idx() ) ) ) {
198 0 : FD_LOG_WARNING(( "failed to join funk alloc" ));
199 0 : return NULL;
200 0 : }
201 :
202 33 : return funk;
203 33 : }
204 :
205 : int
206 0 : fd_funk_purify( void * shfunk ) {
207 : /* Join should work even if there was a crash */
208 0 : fd_funk_t ljoin[1];
209 0 : fd_funk_t * funk = fd_funk_join( ljoin, shfunk );
210 0 : if( funk == NULL ) return FD_FUNK_ERR_PURIFY;
211 :
212 : /* Reset the txn map. We discard any pending transactions. */
213 0 : fd_funk_txn_map_reset( funk->txn_map );
214 0 : fd_funk_txn_pool_reset( funk->txn_pool, 0 );
215 0 : funk->shmem->child_head_cidx = funk->shmem->child_tail_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
216 :
217 0 : return fd_funk_rec_purify( funk );
218 0 : }
219 :
220 : void *
221 : fd_funk_leave( fd_funk_t * funk,
222 30 : void ** opt_shfunk ) {
223 :
224 30 : if( FD_UNLIKELY( !funk ) ) {
225 3 : FD_LOG_WARNING(( "NULL funk" ));
226 3 : if( opt_shfunk ) *opt_shfunk = NULL;
227 3 : return NULL;
228 3 : }
229 27 : void * shfunk = funk->shmem;
230 :
231 27 : memset( funk, 0, sizeof(fd_funk_t) );
232 :
233 27 : if( opt_shfunk ) *opt_shfunk = shfunk;
234 27 : return (void *)funk;
235 30 : }
236 :
237 : void *
238 39 : fd_funk_delete( void * shfunk ) {
239 :
240 39 : if( FD_UNLIKELY( !shfunk ) ) {
241 3 : FD_LOG_WARNING(( "NULL shfunk" ));
242 3 : return NULL;
243 3 : }
244 :
245 36 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shfunk, fd_funk_align() ) ) ) {
246 3 : FD_LOG_WARNING(( "misaligned shfunk" ));
247 3 : return NULL;
248 3 : }
249 :
250 33 : fd_wksp_t * wksp = fd_wksp_containing( shfunk );
251 33 : if( FD_UNLIKELY( !wksp ) ) {
252 3 : FD_LOG_WARNING(( "shfunk must be part of a workspace" ));
253 3 : return NULL;
254 3 : }
255 :
256 30 : fd_funk_shmem_t * shmem = shfunk;
257 30 : if( FD_UNLIKELY( shmem->magic!=FD_FUNK_MAGIC ) ) {
258 6 : FD_LOG_WARNING(( "bad magic" ));
259 6 : return NULL;
260 6 : }
261 :
262 : /* Free all fd_alloc allocations made, individually
263 : (FIXME consider walking the element pool instead of the map?) */
264 :
265 24 : fd_alloc_t * alloc = fd_alloc_join( fd_wksp_laddr_fast( wksp, shmem->alloc_gaddr ), fd_tile_idx() );
266 :
267 24 : void * shmap = fd_wksp_laddr_fast( wksp, shmem->rec_map_gaddr );
268 24 : void * shele = fd_wksp_laddr_fast( wksp, shmem->rec_ele_gaddr );
269 24 : fd_funk_rec_map_t rec_map[1];
270 24 : if( FD_UNLIKELY( !fd_funk_rec_map_join( rec_map, shmap, shele, 0UL ) ) ) {
271 0 : FD_LOG_ERR(( "failed to join rec_map (corrupt funk?)" ));
272 0 : return NULL;
273 0 : }
274 24 : ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
275 2360334 : for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
276 2360310 : for(
277 2360310 : fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
278 5506425 : !fd_funk_rec_map_iter_done( iter );
279 3146115 : iter = fd_funk_rec_map_iter_next( iter )
280 3146115 : ) {
281 3146115 : fd_funk_val_flush( fd_funk_rec_map_iter_ele( iter ), alloc, wksp );
282 3146115 : }
283 2360310 : }
284 :
285 24 : fd_funk_rec_map_leave( rec_map );
286 :
287 : /* Free the fd_alloc instance */
288 :
289 24 : fd_wksp_free_laddr( fd_alloc_delete( fd_alloc_leave( alloc ) ) );
290 :
291 24 : FD_COMPILER_MFENCE();
292 24 : FD_VOLATILE( shmem->magic ) = 0UL;
293 24 : FD_COMPILER_MFENCE();
294 :
295 24 : return shmem;
296 24 : }
297 :
298 : void
299 0 : fd_funk_delete_fast( void * shfunk ) {
300 :
301 0 : if( FD_UNLIKELY( !shfunk ) ) {
302 0 : FD_LOG_WARNING(( "NULL shfunk" ));
303 0 : }
304 :
305 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shfunk, fd_funk_align() ) ) ) {
306 0 : FD_LOG_WARNING(( "misaligned shfunk" ));
307 0 : }
308 :
309 0 : fd_funk_shmem_t * shmem = shfunk;
310 0 : if( FD_UNLIKELY( shmem->magic!=FD_FUNK_MAGIC ) ) {
311 0 : FD_LOG_WARNING(( "bad magic" ));
312 0 : }
313 :
314 0 : fd_wksp_t * wksp = fd_wksp_containing( shmem );
315 0 : if( FD_UNLIKELY( !wksp ) ) {
316 0 : FD_LOG_WARNING(( "shfunk must be part of a workspace" ));
317 0 : }
318 :
319 0 : ulong const tags[1] = { shmem->wksp_tag };
320 0 : fd_wksp_tag_free( wksp, tags, 1UL );
321 :
322 0 : }
323 :
324 : int
325 12 : fd_funk_verify( fd_funk_t * join ) {
326 12 : fd_funk_shmem_t * funk = join->shmem;
327 :
328 336 : # define TEST(c) do { \
329 336 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
330 336 : } while(0)
331 :
332 12 : TEST( funk );
333 :
334 : /* Test metadata */
335 :
336 12 : TEST( funk->magic==FD_FUNK_MAGIC );
337 :
338 12 : ulong funk_gaddr = funk->funk_gaddr;
339 12 : TEST( funk_gaddr );
340 12 : fd_wksp_t * wksp = fd_funk_wksp( join );
341 12 : TEST( wksp );
342 12 : TEST( fd_wksp_laddr_fast( wksp, funk_gaddr )==(void *)funk );
343 12 : TEST( fd_wksp_gaddr_fast( wksp, funk )==funk_gaddr );
344 :
345 12 : ulong wksp_tag = fd_funk_wksp_tag( join );
346 12 : TEST( !!wksp_tag );
347 :
348 12 : ulong seed = funk->seed; /* seed can be anything */
349 :
350 12 : TEST( funk->cycle_tag>2UL );
351 :
352 : /* Test transaction map */
353 :
354 12 : ulong txn_max = fd_funk_txn_pool_ele_max( join->txn_pool );
355 12 : TEST( txn_max<=FD_FUNK_TXN_IDX_NULL );
356 :
357 12 : ulong txn_map_gaddr = funk->txn_map_gaddr;
358 12 : TEST( txn_map_gaddr );
359 12 : fd_funk_txn_map_t * txn_map = fd_funk_txn_map( join );
360 12 : ulong txn_chain_cnt = fd_funk_txn_map_chain_cnt_est( txn_max );
361 12 : TEST( txn_chain_cnt==fd_funk_txn_map_chain_cnt( txn_map ) );
362 12 : TEST( seed==fd_funk_txn_map_seed( txn_map ) );
363 :
364 12 : ulong child_head_idx = fd_funk_txn_idx( funk->child_head_cidx );
365 12 : ulong child_tail_idx = fd_funk_txn_idx( funk->child_tail_cidx );
366 :
367 12 : int null_child_head = fd_funk_txn_idx_is_null( child_head_idx );
368 12 : int null_child_tail = fd_funk_txn_idx_is_null( child_tail_idx );
369 :
370 12 : if( !txn_max ) TEST( null_child_head & null_child_tail );
371 6 : else {
372 6 : if( null_child_head ) TEST( null_child_tail );
373 0 : else TEST( child_head_idx<txn_max );
374 :
375 6 : if( null_child_tail ) TEST( null_child_head );
376 0 : else TEST( child_tail_idx<txn_max );
377 6 : }
378 :
379 12 : if( !txn_max ) TEST( fd_funk_txn_idx_is_null( child_tail_idx ) );
380 :
381 12 : fd_funk_txn_xid_t const * root = fd_funk_root( join );
382 12 : TEST( root ); /* Practically guaranteed */
383 12 : TEST( fd_funk_txn_xid_eq_root( root ) );
384 :
385 12 : fd_funk_txn_xid_t * last_publish = funk->last_publish;
386 12 : TEST( last_publish ); /* Practically guaranteed */
387 : /* (*last_publish) only be root at creation and anything but root post
388 : creation. But we don't know which situation applies here so this
389 : could be anything. */
390 :
391 12 : TEST( !fd_funk_txn_verify( join ) );
392 :
393 : /* Test record map */
394 :
395 12 : ulong rec_max = fd_funk_rec_pool_ele_max( join->rec_pool );
396 12 : TEST( rec_max<=FD_FUNK_TXN_IDX_NULL );
397 :
398 12 : ulong rec_map_gaddr = funk->rec_map_gaddr;
399 12 : TEST( rec_map_gaddr );
400 12 : fd_funk_rec_map_t * rec_map = fd_funk_rec_map( join );
401 12 : ulong rec_chain_cnt = fd_funk_rec_map_chain_cnt_est( rec_max );
402 12 : TEST( rec_chain_cnt==fd_funk_rec_map_chain_cnt( rec_map ) );
403 12 : TEST( seed==fd_funk_rec_map_seed( rec_map ) );
404 :
405 12 : uint rec_head_idx = funk->rec_head_idx;
406 12 : uint rec_tail_idx = funk->rec_tail_idx;
407 :
408 12 : int null_rec_head = fd_funk_rec_idx_is_null( rec_head_idx );
409 12 : int null_rec_tail = fd_funk_rec_idx_is_null( rec_tail_idx );
410 :
411 12 : if( !rec_max ) TEST( null_rec_head & null_rec_tail );
412 6 : else {
413 6 : if( null_rec_head ) TEST( null_rec_tail );
414 0 : else TEST( rec_head_idx<rec_max );
415 :
416 6 : if( null_rec_tail ) TEST( null_rec_head );
417 0 : else TEST( rec_tail_idx<rec_max );
418 6 : }
419 :
420 12 : if( !rec_max ) TEST( fd_funk_rec_idx_is_null( rec_tail_idx ) );
421 :
422 12 : TEST( !fd_funk_rec_verify( join ) );
423 :
424 : /* Test values */
425 :
426 12 : ulong alloc_gaddr = funk->alloc_gaddr;
427 12 : TEST( alloc_gaddr );
428 12 : fd_alloc_t * alloc = fd_funk_alloc( join );
429 12 : TEST( alloc );
430 :
431 12 : TEST( !fd_funk_val_verify( join ) );
432 :
433 12 : # undef TEST
434 :
435 12 : return FD_FUNK_SUCCESS;
436 12 : }
|