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