Line data Source code
1 : #include "fd_inflight.h"
2 :
3 : void *
4 : fd_inflights_new( void * shmem,
5 48 : ulong seed ) {
6 48 : if( FD_UNLIKELY( !shmem ) ) {
7 0 : FD_LOG_WARNING(( "NULL mem" ));
8 0 : return NULL;
9 0 : }
10 :
11 48 : ulong footprint = fd_inflights_footprint();
12 48 : ulong chain_cnt = fd_inflight_map_chain_cnt_est( FD_INFLIGHT_REQ_MAX );
13 :
14 48 : FD_SCRATCH_ALLOC_INIT( l, shmem );
15 48 : fd_inflights_t * table = FD_SCRATCH_ALLOC_APPEND( l, fd_inflights_align(), sizeof(fd_inflights_t) );
16 48 : void * pool = FD_SCRATCH_ALLOC_APPEND( l, fd_inflight_pool_align(), fd_inflight_pool_footprint( FD_INFLIGHT_REQ_MAX ) );
17 48 : void * map = FD_SCRATCH_ALLOC_APPEND( l, fd_inflight_map_align(), fd_inflight_map_footprint ( chain_cnt ) );
18 48 : void * pmap = FD_SCRATCH_ALLOC_APPEND( l, fd_inflight_map_align(), fd_inflight_map_footprint ( chain_cnt ) );
19 48 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_inflights_align() ) == (ulong)shmem + footprint );
20 :
21 48 : table->pool = fd_inflight_pool_join( fd_inflight_pool_new( pool, FD_INFLIGHT_REQ_MAX ) );
22 48 : table->map = fd_inflight_map_join ( fd_inflight_map_new ( map, chain_cnt, seed ) );
23 48 : table->popped_map = fd_inflight_map_join ( fd_inflight_map_new ( pmap, chain_cnt, seed ) );
24 48 : table->popped_cnt = 0UL;
25 48 : FD_TEST( table->outstanding_dl==fd_inflight_dlist_join( fd_inflight_dlist_new( table->outstanding_dl ) ) );
26 48 : FD_TEST( table->popped_dl ==fd_inflight_dlist_join( fd_inflight_dlist_new( table->popped_dl ) ) );
27 :
28 48 : FD_TEST( table->pool );
29 48 : FD_TEST( table->map );
30 48 : FD_TEST( table->popped_map );
31 48 : return shmem;
32 48 : }
33 :
34 : fd_inflights_t *
35 48 : fd_inflights_join( void * shmem ) {
36 48 : fd_inflights_t * table = (fd_inflights_t *)shmem;
37 :
38 48 : if( FD_UNLIKELY( !table ) ) {
39 0 : FD_LOG_WARNING(( "NULL inflight table" ));
40 0 : return NULL;
41 0 : }
42 :
43 48 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)table, fd_inflights_align() ) ) ) {
44 0 : FD_LOG_WARNING(( "misaligned inflight table" ));
45 0 : return NULL;
46 0 : }
47 :
48 48 : return table;
49 48 : }
50 :
51 : /* inflight_acquire returns a FREE record, evicting the oldest POPPED
52 : record if none is free, and the oldest OUTSTANDING one as a last
53 : resort. The caller fills the key and payload, then commits it to
54 : the OUTSTANDING set with inflight_commit. */
55 :
56 : static fd_inflight_t *
57 2121 : inflight_acquire( fd_inflights_t * table ) {
58 2121 : if( FD_UNLIKELY( !fd_inflight_pool_free( table->pool ) ) ) {
59 0 : if( FD_LIKELY( !fd_inflight_dlist_is_empty( table->popped_dl, table->pool ) ) ) {
60 0 : fd_inflight_t * evict = fd_inflight_dlist_ele_pop_head( table->popped_dl, table->pool );
61 0 : table->popped_cnt--;
62 0 : fd_inflight_map_ele_remove_fast( table->popped_map, evict, table->pool );
63 0 : fd_inflight_pool_ele_release ( table->pool, evict );
64 0 : } else {
65 : /* (pool free cnt) + (popped_dl cnt) + (outstanding_dl cnt) ==
66 : FD_INFLIGHT_REQ_MAX, so they can't all be 0. Should be
67 : impossible in practice: callers gate new requests on
68 : fd_inflights_outstanding_free. */
69 0 : fd_inflight_t * evict = fd_inflight_dlist_ele_pop_head( table->outstanding_dl, table->pool );
70 0 : FD_LOG_WARNING(( "evicting outstanding request kind %u slot %lu idx %u nonce %u", evict->key.kind, evict->key.slot, evict->key.idx, evict->key.nonce ));
71 0 : fd_inflight_map_ele_remove_fast( table->map, evict, table->pool );
72 0 : fd_inflight_pool_ele_release ( table->pool, evict );
73 0 : }
74 0 : }
75 2121 : return fd_inflight_pool_ele_acquire( table->pool );
76 2121 : }
77 :
78 : static void
79 : inflight_commit( fd_inflights_t * table,
80 : fd_inflight_t * req,
81 2121 : long now ) {
82 2121 : req->timestamp_ns = now;
83 2121 : fd_inflight_map_ele_insert ( table->map, req, table->pool );
84 2121 : fd_inflight_dlist_ele_push_tail( table->outstanding_dl, req, table->pool );
85 2121 : }
86 :
87 : /* inflight_match removes every record with key from the outstanding
88 : then the popped set, copying the oldest to *out. Returns the number
89 : of records removed. */
90 :
91 : static ulong
92 : inflight_match( fd_inflights_t * table,
93 : fd_inflight_key_t const * key,
94 1908 : fd_inflight_t * out ) {
95 1908 : ulong cnt = 0UL;
96 1908 : long oldest = LONG_MAX;
97 5724 : for( int popped=0; popped<2; popped++ ) {
98 3816 : fd_inflight_map_t * map = popped ? table->popped_map : table->map;
99 3816 : fd_inflight_dlist_t * dlist = popped ? table->popped_dl : table->outstanding_dl;
100 5700 : for(;;) {
101 5700 : fd_inflight_t * req = fd_inflight_map_ele_remove( map, key, NULL, table->pool );
102 5700 : if( FD_LIKELY( !req ) ) break;
103 1884 : if( FD_LIKELY( req->timestamp_ns<oldest ) ) { oldest = req->timestamp_ns; *out = *req; }
104 1884 : fd_inflight_dlist_ele_remove( dlist, req, table->pool );
105 1884 : fd_inflight_pool_ele_release( table->pool, req );
106 1884 : table->popped_cnt -= (ulong)popped;
107 1884 : cnt++;
108 1884 : }
109 3816 : }
110 1908 : return cnt;
111 1908 : }
112 :
113 : void
114 : fd_inflights_shred_insert( fd_inflights_t * table,
115 : ulong nonce,
116 : fd_pubkey_t const * pubkey,
117 : ulong slot,
118 : ulong shred_idx,
119 : fd_hash_t const * block_id,
120 : fd_hash_t const * fec_root,
121 1941 : long now ) {
122 1941 : fd_inflight_t * req = inflight_acquire( table );
123 1941 : fd_inflight_key_init( &req->key, FD_REPAIR_KIND_SHRED, slot, shred_idx, nonce, fec_root );
124 1941 : req->pubkey = *pubkey;
125 1941 : if( FD_LIKELY( block_id ) ) req->block_id = *block_id;
126 9 : else fd_memset( &req->block_id, 0, sizeof(fd_hash_t) );
127 1941 : inflight_commit( table, req, now );
128 1941 : }
129 :
130 : long
131 : fd_inflights_shred_match( fd_inflights_t * table,
132 : ulong nonce,
133 : ulong slot,
134 : ulong shred_idx,
135 : fd_hash_t const * fec_root,
136 : fd_pubkey_t * peer_out,
137 : fd_hash_t * block_id_out,
138 1752 : long now ) {
139 1752 : fd_inflight_key_t key[1];
140 1752 : fd_inflight_key_init( key, FD_REPAIR_KIND_SHRED, slot, shred_idx, nonce, fec_root );
141 1752 : fd_inflight_t req[1];
142 1752 : if( FD_UNLIKELY( !inflight_match( table, key, req ) ) ) return 0L;
143 1734 : *peer_out = req->pubkey;
144 1734 : if( FD_LIKELY( block_id_out ) ) *block_id_out = req->block_id;
145 1734 : return fd_long_max( now-req->timestamp_ns, 1L ); /* >0 marks a match even if now has not advanced */
146 1752 : }
147 :
148 : void
149 : fd_inflights_meta_insert( fd_inflights_t * table,
150 : ulong nonce,
151 : uint kind,
152 : fd_pubkey_t const * pubkey,
153 : ulong slot,
154 : fd_hash_t const * block_id,
155 : uint fec_set_idx,
156 180 : long now ) {
157 180 : fd_inflight_t * req = inflight_acquire( table );
158 180 : fd_inflight_key_init( &req->key, kind, slot, fec_set_idx, nonce, NULL );
159 180 : req->pubkey = *pubkey;
160 180 : req->block_id = *block_id;
161 180 : inflight_commit( table, req, now );
162 180 : }
163 :
164 : int
165 : fd_inflights_meta_match( fd_inflights_t * table,
166 : ulong nonce,
167 156 : fd_inflight_t * out ) {
168 156 : fd_inflight_key_t key[1];
169 156 : fd_inflight_key_init( key, AG_REPAIR_KIND_PARENT_FEC_COUNT, 0UL, 0UL, nonce, NULL ); /* only the nonce is key material for metadata kinds */
170 156 : return !!inflight_match( table, key, out );
171 156 : }
172 :
173 : void
174 : fd_inflights_pop( fd_inflights_t * table,
175 33 : fd_inflight_t * out ) {
176 33 : fd_inflight_t * req = fd_inflight_dlist_ele_pop_head( table->outstanding_dl, table->pool );
177 33 : fd_inflight_map_ele_remove_fast( table->map, req, table->pool );
178 33 : *out = *req;
179 :
180 : /* A null record (nonce 0) was never sent, release it rather than
181 : parking it in the popped set. */
182 33 : if( FD_UNLIKELY( !req->key.nonce ) ) {
183 3 : fd_inflight_pool_ele_release( table->pool, req );
184 3 : return;
185 3 : }
186 :
187 30 : fd_inflight_map_ele_insert ( table->popped_map, req, table->pool );
188 30 : fd_inflight_dlist_ele_push_tail( table->popped_dl, req, table->pool );
189 30 : table->popped_cnt++;
190 30 : }
191 :
192 : #include <stdio.h>
193 :
194 : void
195 0 : fd_inflights_print( fd_inflight_dlist_t * dlist, fd_inflight_t * pool ) {
196 :
197 0 : printf("%-5s %-15s %-8s %-15s %-44s\n", "Kind", "Slot", "Idx", "Timestamp", "Peer");
198 0 : printf("%-5s %-15s %-8s %-15s %-44s\n",
199 0 : "-----", "---------------", "--------", "------------",
200 0 : "--------------------------------------------");
201 0 : for( fd_inflight_dlist_iter_t iter = fd_inflight_dlist_iter_fwd_init( dlist, pool );
202 0 : !fd_inflight_dlist_iter_done( iter, dlist, pool );
203 0 : iter = fd_inflight_dlist_iter_fwd_next( iter, dlist, pool ) ) {
204 0 : fd_inflight_t * req = fd_inflight_dlist_iter_ele( iter, dlist, pool );
205 0 : FD_BASE58_ENCODE_32_BYTES( req->pubkey.uc, peer );
206 0 : printf("%-5u %-15lu %-8u %-15lu %-44.44s\n", req->key.kind, req->key.slot, req->key.idx, (ulong)req->timestamp_ns / (ulong)1e6, peer );
207 0 : }
208 : printf("\n");
209 0 : }
|