Line data Source code
1 : #include "fd_funkier.h"
2 : #include <stdio.h>
3 :
4 : ulong
5 0 : fd_funkier_align( void ) {
6 0 : return FD_FUNKIER_ALIGN;
7 0 : }
8 :
9 : ulong
10 : fd_funkier_footprint( ulong txn_max,
11 0 : ulong rec_max ) {
12 :
13 0 : ulong l = FD_LAYOUT_INIT;
14 :
15 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_funkier_t), sizeof(fd_funkier_t) );
16 :
17 0 : ulong txn_chain_cnt = fd_funkier_txn_map_chain_cnt_est( txn_max );
18 0 : l = FD_LAYOUT_APPEND( l, fd_funkier_txn_map_align(), fd_funkier_txn_map_footprint( txn_chain_cnt ) );
19 0 : l = FD_LAYOUT_APPEND( l, fd_funkier_txn_pool_align(), fd_funkier_txn_pool_footprint() );
20 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_funkier_txn_t), sizeof(fd_funkier_txn_t) * txn_max );
21 :
22 0 : ulong rec_chain_cnt = fd_funkier_rec_map_chain_cnt_est( rec_max );
23 0 : l = FD_LAYOUT_APPEND( l, fd_funkier_rec_map_align(), fd_funkier_rec_map_footprint( rec_chain_cnt ) );
24 0 : l = FD_LAYOUT_APPEND( l, fd_funkier_rec_pool_align(), fd_funkier_rec_pool_footprint() );
25 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_funkier_rec_t), sizeof(fd_funkier_rec_t) * rec_max );
26 :
27 0 : l = FD_LAYOUT_APPEND( l, fd_alloc_align(), fd_alloc_footprint() );
28 :
29 0 : return l;
30 0 : }
31 :
32 : /* TODO: Consider letter user just passing a join of alloc to use,
33 : inferring the backing wksp and cgroup_hint from that and then
34 : allocating exclusively from that? */
35 :
36 : void *
37 : fd_funkier_new( void * shmem,
38 : ulong wksp_tag,
39 : ulong seed,
40 : ulong txn_max,
41 0 : ulong rec_max ) {
42 0 : fd_funkier_t * funk = (fd_funkier_t *)shmem;
43 0 : fd_wksp_t * wksp = fd_wksp_containing( funk );
44 :
45 : #ifdef FD_FUNKIER_HANDHOLDING
46 : if( FD_UNLIKELY( !funk ) ) {
47 : FD_LOG_WARNING(( "NULL funk" ));
48 : return NULL;
49 : }
50 :
51 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)funk, fd_funkier_align() ) ) ) {
52 : FD_LOG_WARNING(( "misaligned funk" ));
53 : return NULL;
54 : }
55 :
56 : if( FD_UNLIKELY( !wksp_tag ) ) {
57 : FD_LOG_WARNING(( "bad wksp_tag" ));
58 : return NULL;
59 : }
60 :
61 : if( FD_UNLIKELY( !wksp ) ) {
62 : FD_LOG_WARNING(( "shmem must be part of a workspace" ));
63 : return NULL;
64 : }
65 :
66 : if( txn_max>FD_FUNKIER_TXN_IDX_NULL ) { /* See note in fd_funkier.h about this limit */
67 : FD_LOG_WARNING(( "txn_max too large for index compression" ));
68 : return NULL;
69 : }
70 : #endif
71 :
72 0 : FD_SCRATCH_ALLOC_INIT( l, funk+1 );
73 :
74 0 : ulong txn_chain_cnt = fd_funkier_txn_map_chain_cnt_est( txn_max );
75 0 : void * txn_map = FD_SCRATCH_ALLOC_APPEND( l, fd_funkier_txn_map_align(), fd_funkier_txn_map_footprint( txn_chain_cnt ) );
76 0 : void * txn_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_funkier_txn_pool_align(), fd_funkier_txn_pool_footprint() );
77 0 : fd_funkier_txn_t * txn_ele = (fd_funkier_txn_t *)FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_funkier_txn_t), sizeof(fd_funkier_txn_t) * txn_max );
78 :
79 0 : ulong rec_chain_cnt = fd_funkier_rec_map_chain_cnt_est( rec_max );
80 0 : void * rec_map = FD_SCRATCH_ALLOC_APPEND( l, fd_funkier_rec_map_align(), fd_funkier_rec_map_footprint( rec_chain_cnt ) );
81 0 : void * rec_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_funkier_rec_pool_align(), fd_funkier_rec_pool_footprint() );
82 0 : fd_funkier_rec_t * rec_ele = (fd_funkier_rec_t *)FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_funkier_rec_t), sizeof(fd_funkier_rec_t) * rec_max );
83 :
84 0 : void * alloc = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(), fd_alloc_footprint() );
85 :
86 0 : FD_TEST( _l == (ulong)funk + fd_funkier_footprint( txn_max, rec_max ) );
87 :
88 0 : fd_memset( funk, 0, sizeof(fd_funkier_t) );
89 :
90 0 : funk->funk_gaddr = fd_wksp_gaddr_fast( wksp, funk );
91 0 : funk->wksp_tag = wksp_tag;
92 0 : funk->seed = seed;
93 0 : funk->cycle_tag = 3UL; /* various verify functions use tags 0-2 */
94 :
95 0 : funk->txn_map_gaddr = fd_wksp_gaddr_fast( wksp, fd_funkier_txn_map_new( txn_map, txn_chain_cnt, seed ) );
96 0 : void * txn_pool2 = fd_funkier_txn_pool_new( txn_pool );
97 0 : funk->txn_pool_gaddr = fd_wksp_gaddr_fast( wksp, txn_pool2 );
98 0 : fd_funkier_txn_pool_t txn_join[1];
99 0 : fd_funkier_txn_pool_join( txn_join, txn_pool2, txn_ele, txn_max );
100 0 : fd_funkier_txn_pool_reset( txn_join, 0UL );
101 0 : funk->txn_ele_gaddr = fd_wksp_gaddr_fast( wksp, txn_ele );
102 0 : funk->txn_max = txn_max;
103 0 : funk->child_head_cidx = fd_funkier_txn_cidx( FD_FUNKIER_TXN_IDX_NULL );
104 0 : funk->child_tail_cidx = fd_funkier_txn_cidx( FD_FUNKIER_TXN_IDX_NULL );
105 :
106 0 : fd_funkier_txn_xid_set_root( funk->root );
107 0 : fd_funkier_txn_xid_set_root( funk->last_publish );
108 :
109 0 : funk->rec_map_gaddr = fd_wksp_gaddr_fast( wksp, fd_funkier_rec_map_new( rec_map, rec_chain_cnt, seed ) );
110 0 : void * rec_pool2 = fd_funkier_rec_pool_new( rec_pool );
111 0 : funk->rec_pool_gaddr = fd_wksp_gaddr_fast( wksp, rec_pool2 );
112 0 : fd_funkier_rec_pool_t rec_join[1];
113 0 : fd_funkier_rec_pool_join( rec_join, rec_pool2, rec_ele, rec_max );
114 0 : fd_funkier_rec_pool_reset( rec_join, 0UL );
115 0 : funk->rec_ele_gaddr = fd_wksp_gaddr_fast( wksp, rec_ele );
116 0 : funk->rec_max = rec_max;
117 0 : funk->rec_head_idx = FD_FUNKIER_REC_IDX_NULL;
118 0 : funk->rec_tail_idx = FD_FUNKIER_REC_IDX_NULL;
119 :
120 0 : funk->alloc_gaddr = fd_wksp_gaddr_fast( wksp, fd_alloc_join( fd_alloc_new( alloc, wksp_tag ), 0UL ) );
121 :
122 0 : FD_COMPILER_MFENCE();
123 0 : FD_VOLATILE( funk->magic ) = FD_FUNKIER_MAGIC;
124 0 : FD_COMPILER_MFENCE();
125 :
126 0 : return (void *)funk;
127 0 : }
128 :
129 : fd_funkier_t *
130 0 : fd_funkier_join( void * shfunk ) {
131 0 : fd_funkier_t * funk = (fd_funkier_t *)shfunk;
132 :
133 : #ifdef FD_FUNKIER_HANDHOLDING
134 : if( FD_UNLIKELY( !funk ) ) {
135 : FD_LOG_WARNING(( "NULL shfunk" ));
136 : return NULL;
137 : }
138 :
139 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)funk, fd_funkier_align() ) ) ) {
140 : FD_LOG_WARNING(( "misaligned shfunk" ));
141 : return NULL;
142 : }
143 :
144 : fd_wksp_t * wksp = fd_wksp_containing( funk );
145 : if( FD_UNLIKELY( !wksp ) ) {
146 : FD_LOG_WARNING(( "shfunk must be part of a workspace" ));
147 : return NULL;
148 : }
149 :
150 : if( FD_UNLIKELY( funk->magic!=FD_FUNKIER_MAGIC ) ) {
151 : FD_LOG_WARNING(( "bad magic" ));
152 : return NULL;
153 : }
154 : #endif
155 :
156 : #ifdef FD_FUNKIER_WKSP_PROTECT
157 : #ifndef FD_FUNKIER_HANDHOLDING
158 : fd_wksp_t * wksp = fd_wksp_containing( funk );
159 : #endif
160 : fd_wksp_mprotect( wksp, 1 );
161 : #endif
162 :
163 0 : return funk;
164 0 : }
165 :
166 : void *
167 0 : fd_funkier_leave( fd_funkier_t * funk ) {
168 :
169 0 : if( FD_UNLIKELY( !funk ) ) {
170 0 : FD_LOG_WARNING(( "NULL funk" ));
171 0 : return NULL;
172 0 : }
173 :
174 0 : return (void *)funk;
175 0 : }
176 :
177 : void *
178 0 : fd_funkier_delete( void * shfunk ) {
179 0 : fd_funkier_t * funk = (fd_funkier_t *)shfunk;
180 :
181 : #ifdef FD_FUNKIER_HANDHOLDING
182 : if( FD_UNLIKELY( !funk ) ) {
183 : FD_LOG_WARNING(( "NULL shfunk" ));
184 : return NULL;
185 : }
186 :
187 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)funk, fd_funkier_align() ) ) ) {
188 : FD_LOG_WARNING(( "misaligned shfunk" ));
189 : return NULL;
190 : }
191 :
192 : fd_wksp_t * wksp = fd_wksp_containing( funk );
193 : if( FD_UNLIKELY( !wksp ) ) {
194 : FD_LOG_WARNING(( "shfunk must be part of a workspace" ));
195 : return NULL;
196 : }
197 :
198 : if( FD_UNLIKELY( funk->magic!=FD_FUNKIER_MAGIC ) ) {
199 : FD_LOG_WARNING(( "bad magic" ));
200 : return NULL;
201 : }
202 : #endif
203 :
204 0 : FD_COMPILER_MFENCE();
205 0 : FD_VOLATILE( funk->magic ) = 0UL;
206 0 : FD_COMPILER_MFENCE();
207 :
208 0 : return funk;
209 0 : }
210 :
211 : #ifdef FD_FUNKIER_HANDHOLDING
212 : int
213 : fd_funkier_verify( fd_funkier_t * funk ) {
214 :
215 : # define TEST(c) do { \
216 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNKIER_ERR_INVAL; } \
217 : } while(0)
218 :
219 : TEST( funk );
220 :
221 : /* Test metadata */
222 :
223 : TEST( funk->magic==FD_FUNKIER_MAGIC );
224 :
225 : ulong funk_gaddr = funk->funk_gaddr;
226 : TEST( funk_gaddr );
227 : fd_wksp_t * wksp = fd_funkier_wksp( funk );
228 : TEST( wksp );
229 : TEST( fd_wksp_laddr_fast( wksp, funk_gaddr )==(void *)funk );
230 : TEST( fd_wksp_gaddr_fast( wksp, funk )==funk_gaddr );
231 :
232 : ulong wksp_tag = fd_funkier_wksp_tag( funk );
233 : TEST( !!wksp_tag );
234 :
235 : ulong seed = funk->seed; /* seed can be anything */
236 :
237 : TEST( funk->cycle_tag>2UL );
238 :
239 : /* Test transaction map */
240 :
241 : ulong txn_max = funk->txn_max;
242 : TEST( txn_max<=FD_FUNKIER_TXN_IDX_NULL );
243 :
244 : ulong txn_map_gaddr = funk->txn_map_gaddr;
245 : TEST( txn_map_gaddr );
246 : fd_funkier_txn_map_t txn_map = fd_funkier_txn_map( funk, wksp );
247 : ulong txn_chain_cnt = fd_funkier_txn_map_chain_cnt_est( txn_max );
248 : TEST( txn_chain_cnt==fd_funkier_txn_map_chain_cnt( &txn_map ) );
249 : TEST( seed==fd_funkier_txn_map_seed( &txn_map ) );
250 :
251 : ulong child_head_idx = fd_funkier_txn_idx( funk->child_head_cidx );
252 : ulong child_tail_idx = fd_funkier_txn_idx( funk->child_tail_cidx );
253 :
254 : int null_child_head = fd_funkier_txn_idx_is_null( child_head_idx );
255 : int null_child_tail = fd_funkier_txn_idx_is_null( child_tail_idx );
256 :
257 : if( !txn_max ) TEST( null_child_head & null_child_tail );
258 : else {
259 : if( null_child_head ) TEST( null_child_tail );
260 : else TEST( child_head_idx<txn_max );
261 :
262 : if( null_child_tail ) TEST( null_child_head );
263 : else TEST( child_tail_idx<txn_max );
264 : }
265 :
266 : if( !txn_max ) TEST( fd_funkier_txn_idx_is_null( child_tail_idx ) );
267 :
268 : fd_funkier_txn_xid_t const * root = fd_funkier_root( funk );
269 : TEST( root ); /* Practically guaranteed */
270 : TEST( fd_funkier_txn_xid_eq_root( root ) );
271 :
272 : fd_funkier_txn_xid_t * last_publish = funk->last_publish;
273 : TEST( last_publish ); /* Practically guaranteed */
274 : /* (*last_publish) only be root at creation and anything but root post
275 : creation. But we don't know which situation applies here so this
276 : could be anything. */
277 :
278 : TEST( !fd_funkier_txn_verify( funk ) );
279 :
280 : /* Test record map */
281 :
282 : ulong rec_max = funk->rec_max;
283 : TEST( rec_max<=FD_FUNKIER_TXN_IDX_NULL );
284 :
285 : ulong rec_map_gaddr = funk->rec_map_gaddr;
286 : TEST( rec_map_gaddr );
287 : fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
288 : ulong rec_chain_cnt = fd_funkier_rec_map_chain_cnt_est( rec_max );
289 : TEST( rec_chain_cnt==fd_funkier_rec_map_chain_cnt( &rec_map ) );
290 : TEST( seed==fd_funkier_rec_map_seed( &rec_map ) );
291 :
292 : ulong rec_head_idx = funk->rec_head_idx;
293 : ulong rec_tail_idx = funk->rec_tail_idx;
294 :
295 : int null_rec_head = fd_funkier_rec_idx_is_null( rec_head_idx );
296 : int null_rec_tail = fd_funkier_rec_idx_is_null( rec_tail_idx );
297 :
298 : if( !rec_max ) TEST( null_rec_head & null_rec_tail );
299 : else {
300 : if( null_rec_head ) TEST( null_rec_tail );
301 : else TEST( rec_head_idx<rec_max );
302 :
303 : if( null_rec_tail ) TEST( null_rec_head );
304 : else TEST( rec_tail_idx<rec_max );
305 : }
306 :
307 : if( !rec_max ) TEST( fd_funkier_rec_idx_is_null( rec_tail_idx ) );
308 :
309 : TEST( !fd_funkier_rec_verify( funk ) );
310 :
311 : /* Test values */
312 :
313 : ulong alloc_gaddr = funk->alloc_gaddr;
314 : TEST( alloc_gaddr );
315 : fd_alloc_t * alloc = fd_funkier_alloc( funk, wksp );
316 : TEST( alloc );
317 :
318 : TEST( !fd_funkier_val_verify( funk ) );
319 :
320 : # undef TEST
321 :
322 : return FD_FUNKIER_SUCCESS;
323 : }
324 : #endif
|