Line data Source code
1 : #include "fd_quic_svc_q.h"
2 : #include "fd_quic_private.h"
3 : #include "fd_quic_conn.h"
4 :
5 : /* PRIVATE ************************************************/
6 :
7 : #define PRQ_NAME fd_quic_svc_queue_prq
8 26347959 : #define PRQ_T fd_quic_svc_event_t
9 26047452 : #define PRQ_TMP_ST(p,t) do { \
10 26047452 : (p)[0] = (t); \
11 26047452 : t.conn->svc_meta.private.prq_idx = (ulong)((p)-heap); \
12 26047452 : } while( 0 )
13 26347419 : #define PRQ_TIMEOUT_T long
14 : #include "../../util/tmpl/fd_prq.c"
15 : typedef fd_quic_svc_event_t fd_quic_svc_queue_prq_t;
16 :
17 :
18 : /* SETUP FUNCTIONS *************************************************/
19 :
20 : ulong
21 11088 : fd_quic_svc_timers_footprint( ulong max_conn ) {
22 11088 : ulong l = FD_LAYOUT_INIT;
23 11088 : l = FD_LAYOUT_APPEND( l, alignof(fd_quic_svc_timers_t), sizeof(fd_quic_svc_timers_t) );
24 11088 : l = FD_LAYOUT_APPEND( l, fd_quic_svc_queue_prq_align(), fd_quic_svc_queue_prq_footprint( max_conn ) );
25 11088 : l = FD_LAYOUT_FINI( l, fd_quic_svc_timers_align() );
26 11088 : return l;
27 11088 : }
28 :
29 : ulong
30 36678 : fd_quic_svc_timers_align( void ) {
31 36678 : return fd_ulong_max( alignof( fd_quic_svc_timers_t ),
32 36678 : fd_quic_svc_queue_prq_align() );
33 36678 : }
34 :
35 : fd_quic_svc_timers_t *
36 : fd_quic_svc_timers_init( void * mem,
37 : ulong max_conn,
38 3414 : fd_quic_state_t * state ) {
39 3414 : if( FD_UNLIKELY( !mem ) ) {
40 0 : FD_LOG_ERR(( "fd_quic_svc_timers_init called with NULL mem" ));
41 0 : return NULL;
42 0 : }
43 :
44 3414 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_quic_svc_timers_align() ) ) ) {
45 0 : FD_LOG_ERR(( "fd_quic_svc_timers_init called with misaligned mem" ));
46 0 : return NULL;
47 0 : }
48 :
49 3414 : FD_SCRATCH_ALLOC_INIT( l, mem );
50 3414 : fd_quic_svc_timers_t * timers = FD_SCRATCH_ALLOC_APPEND( l,
51 3414 : alignof(fd_quic_svc_timers_t),
52 3414 : sizeof(fd_quic_svc_timers_t) );
53 3414 : uchar * prq_mem = FD_SCRATCH_ALLOC_APPEND( l,
54 3414 : fd_quic_svc_queue_prq_align(),
55 3414 : fd_quic_svc_queue_prq_footprint( max_conn ) );
56 :
57 :
58 0 : timers->prq = fd_quic_svc_queue_prq_join( fd_quic_svc_queue_prq_new( prq_mem, max_conn ) );
59 3414 : if( FD_UNLIKELY( !timers->prq ) ) FD_LOG_ERR(( "fd_quic_svc_timers_init failed to join prq" ));
60 :
61 3414 : timers->instant.cnt = 0U;
62 3414 : timers->instant.head = FD_QUIC_SVC_DLIST_IDX_INVAL;
63 3414 : timers->instant.tail = FD_QUIC_SVC_DLIST_IDX_INVAL;
64 :
65 3414 : timers->state = state;
66 :
67 3414 : return timers;
68 3414 : }
69 :
70 : void
71 26014294 : fd_quic_svc_timers_init_conn( fd_quic_conn_t * conn ) {
72 26014294 : conn->svc_meta.next_timeout = LONG_MAX;
73 26014294 : conn->svc_meta.private.prq_idx = FD_QUIC_SVC_PRQ_IDX_INVAL;
74 26014294 : conn->svc_meta.private.svc_type = FD_QUIC_SVC_CNT;
75 26014294 : }
76 :
77 : /* END SETUP FUNCTIONS *********************************************/
78 :
79 : /* DLIST HELPER FUNCTIONS *******************************************/
80 :
81 : static inline void
82 : fd_quic_svc_dlist_insert_tail( fd_quic_svc_queue_t * queue,
83 : fd_quic_state_t * state,
84 25366204 : fd_quic_conn_t * conn ) {
85 :
86 25366204 : uint conn_idx = conn->conn_idx;
87 25366204 : fd_quic_conn_t * tail_conn = fd_quic_conn_at_idx( state, queue->tail );
88 :
89 25366204 : *fd_ptr_if( !!queue->cnt, &tail_conn->svc_meta.private.dlist.next , &queue->head) = conn_idx ;
90 25366204 : conn->svc_meta.private.dlist.prev = queue->tail;
91 25366204 : conn->svc_meta.private.dlist.next = FD_QUIC_SVC_DLIST_IDX_INVAL;
92 25366204 : queue->tail = conn_idx;
93 25366204 : queue->cnt++;
94 25366204 : }
95 :
96 : static inline void
97 : fd_quic_svc_dlist_remove( fd_quic_svc_queue_t * queue,
98 : fd_quic_state_t * state,
99 25366189 : fd_quic_conn_t * conn ) {
100 25366189 : uint conn_idx = conn->conn_idx;
101 25366189 : uint qhead = queue->head;
102 25366189 : uint qtail = queue->tail;
103 :
104 25366189 : fd_quic_conn_t * prev_conn = fd_quic_conn_at_idx( state, conn->svc_meta.private.dlist.prev );
105 25366189 : fd_quic_conn_t * next_conn = fd_quic_conn_at_idx( state, conn->svc_meta.private.dlist.next );
106 :
107 25366189 : *fd_ptr_if( conn_idx == qhead, &queue->head , &prev_conn->svc_meta.private.dlist.next) = conn->svc_meta.private.dlist.next;
108 25366189 : *fd_ptr_if( conn_idx == qtail, &queue->tail , &next_conn->svc_meta.private.dlist.prev) = conn->svc_meta.private.dlist.prev;
109 :
110 25366189 : conn->svc_meta.private.dlist.next = FD_QUIC_SVC_DLIST_IDX_INVAL;
111 25366189 : conn->svc_meta.private.dlist.prev = FD_QUIC_SVC_DLIST_IDX_INVAL;
112 25366189 : queue->cnt--;
113 25366189 : }
114 :
115 : /* TASK FUNCTIONS *************************************************/
116 :
117 : void
118 : fd_quic_svc_timers_cancel( fd_quic_svc_timers_t * timers,
119 14346 : fd_quic_conn_t * conn ) {
120 :
121 14346 : uint svc_type = conn->svc_meta.private.svc_type;
122 14346 : fd_quic_state_t * state = timers->state;
123 :
124 14346 : if( svc_type == FD_QUIC_SVC_INSTANT ) {
125 45 : fd_quic_svc_dlist_remove( &timers->instant, state, conn );
126 14301 : } else if( svc_type == FD_QUIC_SVC_DYNAMIC ) {
127 87 : fd_quic_svc_queue_prq_remove( timers->prq, conn->svc_meta.private.prq_idx );
128 87 : }
129 14346 : fd_quic_svc_timers_init_conn( conn );
130 14346 : }
131 :
132 : void
133 : fd_quic_svc_timers_schedule( fd_quic_svc_timers_t * timers,
134 : fd_quic_conn_t * conn,
135 101418149 : long now ) {
136 :
137 : /* if conn null or invalid, do not schedule */
138 101418149 : if( FD_UNLIKELY( !conn || conn->state == FD_QUIC_CONN_STATE_INVALID ) ) {
139 : /* cleaner/safer to check in here for now. If function call overhead
140 : becomes a constraint, move check to caller */
141 13212 : return;
142 13212 : }
143 :
144 101404937 : fd_quic_state_t * state = timers->state;
145 101404937 : long const expiry = conn->svc_meta.next_timeout;
146 101404937 : uint const old_svc_type = conn->svc_meta.private.svc_type;
147 :
148 101404937 : uint const new_svc_type = expiry == now ? FD_QUIC_SVC_INSTANT : FD_QUIC_SVC_DYNAMIC;
149 101404937 : int const old_dynamic = old_svc_type==FD_QUIC_SVC_DYNAMIC;
150 101404937 : int const both_dynamic = old_dynamic & (new_svc_type==FD_QUIC_SVC_DYNAMIC);
151 :
152 : /* Speculative is_increase is invalid when !both_dynamic, but safe bc prq_idx==0 */
153 101404937 : ulong const prq_idx = fd_ulong_if( both_dynamic, conn->svc_meta.private.prq_idx, 0 );
154 101404937 : int const is_increase = timers->prq[prq_idx].timeout <= expiry;
155 :
156 : /* No-op if already INSTANT, or if trying to increase/preserve DYNAMIC expiry */
157 101404937 : int noop = (old_svc_type==FD_QUIC_SVC_INSTANT) | (both_dynamic & is_increase);
158 101404937 : if( noop ) return;
159 :
160 : /* Cancel existing DYNAMIC timer if it exists */
161 51413386 : if( old_dynamic ) {
162 25744590 : fd_quic_svc_queue_prq_remove( timers->prq, conn->svc_meta.private.prq_idx );
163 25744590 : }
164 :
165 : /* Schedule in appropriate queue */
166 51413386 : conn->svc_meta.private.svc_type = new_svc_type;
167 :
168 51413386 : if( new_svc_type==FD_QUIC_SVC_INSTANT ) {
169 25366204 : fd_quic_svc_dlist_insert_tail( &timers->instant, state, conn );
170 26047182 : } else {
171 : /* FD_QUIC_SVC_DYNAMIC - use heap */
172 26047182 : fd_quic_svc_event_t e = {
173 26047182 : .conn = conn,
174 26047182 : .timeout = expiry
175 26047182 : };
176 26047182 : fd_quic_svc_queue_prq_insert( timers->prq, &e );
177 26047182 : }
178 51413386 : }
179 :
180 : int
181 : fd_quic_svc_timers_validate( fd_quic_svc_timers_t * timers,
182 60 : fd_quic_t * quic ) {
183 60 : fd_quic_state_t * state = fd_quic_get_state( quic );
184 :
185 : /* Validate DYNAMIC queue (heap) */
186 60 : ulong prq_cnt = fd_quic_svc_queue_prq_cnt( timers->prq );
187 300111 : for( ulong i=0; i<prq_cnt; i++ ) {
188 300051 : fd_quic_svc_event_t * event = timers->prq + i;
189 300051 : fd_quic_conn_t * conn = event->conn;
190 :
191 : /* conn and idx match for dynamic queue */
192 300051 : if( FD_UNLIKELY( conn->svc_meta.private.prq_idx != i ) ) return 0;
193 300051 : if( FD_UNLIKELY( conn->svc_meta.private.svc_type != FD_QUIC_SVC_DYNAMIC ) ) return 0;
194 :
195 : /* conn in prq at most once */
196 300051 : if( FD_UNLIKELY( conn->visited ) ) return 0;
197 300051 : conn->visited = 1U;
198 300051 : }
199 :
200 : /* Validate dlist */
201 60 : ulong instant_cnt = 0U;
202 60 : uint curr = timers->instant.head;
203 63 : while( curr != FD_QUIC_SVC_DLIST_IDX_INVAL ) {
204 3 : fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, curr );
205 3 : if( FD_UNLIKELY( conn->svc_meta.private.svc_type != FD_QUIC_SVC_INSTANT ) ) return 0;
206 3 : if( FD_UNLIKELY( conn->visited ) ) return 0;
207 3 : conn->visited = 1U;
208 3 : curr = conn->svc_meta.private.dlist.next;
209 3 : instant_cnt++;
210 3 : }
211 60 : if( instant_cnt != timers->instant.cnt ) return 0;
212 :
213 : /* connections not in any queue should have INVALID idx */
214 60 : ulong const conn_cnt = quic->limits.conn_cnt;
215 300558 : for( ulong i=0; i<conn_cnt; i++ ) {
216 300498 : fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, i );
217 300498 : if( !conn->visited && conn->svc_meta.private.prq_idx != FD_QUIC_SVC_PRQ_IDX_INVAL ) return 0;
218 300498 : }
219 :
220 60 : return 1;
221 60 : }
222 :
223 : fd_quic_svc_event_t
224 : fd_quic_svc_timers_next( fd_quic_svc_timers_t * timers,
225 : long now,
226 145145494 : int pop ) {
227 145145494 : fd_quic_svc_event_t next = { .timeout = LONG_MAX, .conn = NULL };
228 :
229 : /* Priority: INSTANT > DYNAMIC */
230 :
231 : /* Check INSTANT queue first */
232 145145494 : if( FD_LIKELY( timers->instant.cnt ) ) {
233 25414582 : fd_quic_conn_t * conn = fd_quic_conn_at_idx( timers->state, timers->instant.head );
234 25414582 : next = (fd_quic_svc_event_t){.conn = conn, .timeout = fd_long_min( now, conn->svc_meta.next_timeout )};
235 :
236 25414582 : if( FD_LIKELY( pop ) ) {
237 25366144 : fd_quic_svc_dlist_remove( &timers->instant, timers->state, conn );
238 25366144 : fd_quic_svc_timers_init_conn( conn );
239 25366144 : }
240 :
241 25414582 : return next;
242 25414582 : }
243 :
244 : /* Check DYNAMIC queue (heap) */
245 119730912 : if( !fd_quic_svc_queue_prq_cnt( timers->prq ) ) return next;
246 119725557 : else if( pop && now < timers->prq[0].timeout ) return next;
247 105273 : else {
248 105273 : next = timers->prq[0];
249 105273 : if( FD_LIKELY( pop ) ) {
250 2460 : fd_quic_svc_queue_prq_remove_min( timers->prq );
251 2460 : fd_quic_svc_timers_init_conn( next.conn );
252 2460 : }
253 105273 : return next;
254 105273 : }
255 119730912 : }
256 :
257 : fd_quic_svc_event_t
258 : fd_quic_svc_timers_get_event( fd_quic_svc_timers_t * timers,
259 : fd_quic_conn_t * conn,
260 2091 : long now ) {
261 2091 : uint svc_type = conn->svc_meta.private.svc_type;
262 :
263 2091 : if( svc_type == FD_QUIC_SVC_INSTANT ) {
264 0 : return (fd_quic_svc_event_t){ .timeout = now, .conn = conn };
265 2091 : } else if (svc_type == FD_QUIC_SVC_DYNAMIC) {
266 2088 : ulong idx = conn->svc_meta.private.prq_idx;
267 2088 : return *(timers->prq + idx);
268 2088 : }
269 3 : return (fd_quic_svc_event_t){ .timeout = LONG_MAX, .conn = NULL };
270 2091 : }
271 :
272 : ulong
273 170404805 : fd_quic_svc_timers_cnt_events( fd_quic_svc_timers_t * timers ) {
274 170404805 : return timers->instant.cnt +
275 170404805 : fd_quic_svc_queue_prq_cnt( timers->prq );
276 170404805 : }
|