1 /* * Copyright (c) 2012-2015, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
5 * \file circuitmux_ewma.c
6 * \brief EWMA circuit selection as a circuitmux_t policy
9 #define TOR_CIRCUITMUX_EWMA_C_
14 #include "circuitmux.h"
15 #include "circuitmux_ewma.h"
16 #include "networkstatus.h"
18 /*** EWMA parameter #defines ***/
20 /** How long does a tick last (seconds)? */
21 #define EWMA_TICK_LEN 10
23 /** The default per-tick scale factor, if it hasn't been overridden by a
24 * consensus or a configuration setting. zero means "disabled". */
25 #define EWMA_DEFAULT_HALFLIFE 0.0
27 /*** Some useful constant #defines ***/
30 #define EPSILON 0.00001
32 #define LOG_ONEHALF -0.69314718055994529
34 /*** EWMA structures ***/
36 typedef struct cell_ewma_s cell_ewma_t
;
37 typedef struct ewma_policy_data_s ewma_policy_data_t
;
38 typedef struct ewma_policy_circ_data_s ewma_policy_circ_data_t
;
41 * The cell_ewma_t structure keeps track of how many cells a circuit has
42 * transferred recently. It keeps an EWMA (exponentially weighted moving
43 * average) of the number of cells flushed from the circuit queue onto a
44 * connection in channel_flush_from_first_active_circuit().
48 /** The last 'tick' at which we recalibrated cell_count.
50 * A cell sent at exactly the start of this tick has weight 1.0. Cells sent
51 * since the start of this tick have weight greater than 1.0; ones sent
52 * earlier have less weight. */
53 unsigned int last_adjusted_tick
;
54 /** The EWMA of the cell count. */
56 /** True iff this is the cell count for a circuit's previous
58 unsigned int is_for_p_chan
: 1;
59 /** The position of the circuit within the OR connection's priority
64 struct ewma_policy_data_s
{
65 circuitmux_policy_data_t base_
;
68 * Priority queue of cell_ewma_t for circuits with queued cells waiting
69 * for room to free up on the channel that owns this circuitmux. Kept
70 * in heap order according to EWMA. This was formerly in channel_t, and
71 * in or_connection_t before that.
73 smartlist_t
*active_circuit_pqueue
;
76 * The tick on which the cell_ewma_ts in active_circuit_pqueue last had
77 * their ewma values rescaled. This was formerly in channel_t, and in
78 * or_connection_t before that.
80 unsigned int active_circuit_pqueue_last_recalibrated
;
83 struct ewma_policy_circ_data_s
{
84 circuitmux_policy_circ_data_t base_
;
87 * The EWMA count for the number of cells flushed from this circuit
88 * onto this circuitmux. Used to determine which circuit to flush
89 * from next. This was formerly in circuit_t and or_circuit_t.
91 cell_ewma_t cell_ewma
;
94 * Pointer back to the circuit_t this is for; since we're separating
95 * out circuit selection policy like this, we can't attach cell_ewma_t
96 * to the circuit_t any more, so we can't use SUBTYPE_P directly to a
97 * circuit_t like before; instead get it here.
102 #define EWMA_POL_DATA_MAGIC 0x2fd8b16aU
103 #define EWMA_POL_CIRC_DATA_MAGIC 0x761e7747U
105 /*** Downcasts for the above types ***/
107 static ewma_policy_data_t
*
108 TO_EWMA_POL_DATA(circuitmux_policy_data_t
*);
110 static ewma_policy_circ_data_t
*
111 TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t
*);
114 * Downcast a circuitmux_policy_data_t to an ewma_policy_data_t and assert
115 * if the cast is impossible.
118 static INLINE ewma_policy_data_t
*
119 TO_EWMA_POL_DATA(circuitmux_policy_data_t
*pol
)
121 if (!pol
) return NULL
;
123 tor_assert(pol
->magic
== EWMA_POL_DATA_MAGIC
);
124 return DOWNCAST(ewma_policy_data_t
, pol
);
129 * Downcast a circuitmux_policy_circ_data_t to an ewma_policy_circ_data_t
130 * and assert if the cast is impossible.
133 static INLINE ewma_policy_circ_data_t
*
134 TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t
*pol
)
136 if (!pol
) return NULL
;
138 tor_assert(pol
->magic
== EWMA_POL_CIRC_DATA_MAGIC
);
139 return DOWNCAST(ewma_policy_circ_data_t
, pol
);
143 /*** Static declarations for circuitmux_ewma.c ***/
145 static void add_cell_ewma(ewma_policy_data_t
*pol
, cell_ewma_t
*ewma
);
146 static int compare_cell_ewma_counts(const void *p1
, const void *p2
);
147 static unsigned cell_ewma_tick_from_timeval(const struct timeval
*now
,
148 double *remainder_out
);
149 static circuit_t
* cell_ewma_to_circuit(cell_ewma_t
*ewma
);
150 static INLINE
double get_scale_factor(unsigned from_tick
, unsigned to_tick
);
151 static cell_ewma_t
* pop_first_cell_ewma(ewma_policy_data_t
*pol
);
152 static void remove_cell_ewma(ewma_policy_data_t
*pol
, cell_ewma_t
*ewma
);
153 static void scale_single_cell_ewma(cell_ewma_t
*ewma
, unsigned cur_tick
);
154 static void scale_active_circuits(ewma_policy_data_t
*pol
,
157 /*** Circuitmux policy methods ***/
159 static circuitmux_policy_data_t
* ewma_alloc_cmux_data(circuitmux_t
*cmux
);
160 static void ewma_free_cmux_data(circuitmux_t
*cmux
,
161 circuitmux_policy_data_t
*pol_data
);
162 static circuitmux_policy_circ_data_t
*
163 ewma_alloc_circ_data(circuitmux_t
*cmux
, circuitmux_policy_data_t
*pol_data
,
164 circuit_t
*circ
, cell_direction_t direction
,
165 unsigned int cell_count
);
167 ewma_free_circ_data(circuitmux_t
*cmux
,
168 circuitmux_policy_data_t
*pol_data
,
170 circuitmux_policy_circ_data_t
*pol_circ_data
);
172 ewma_notify_circ_active(circuitmux_t
*cmux
,
173 circuitmux_policy_data_t
*pol_data
,
175 circuitmux_policy_circ_data_t
*pol_circ_data
);
177 ewma_notify_circ_inactive(circuitmux_t
*cmux
,
178 circuitmux_policy_data_t
*pol_data
,
180 circuitmux_policy_circ_data_t
*pol_circ_data
);
182 ewma_notify_xmit_cells(circuitmux_t
*cmux
,
183 circuitmux_policy_data_t
*pol_data
,
185 circuitmux_policy_circ_data_t
*pol_circ_data
,
186 unsigned int n_cells
);
188 ewma_pick_active_circuit(circuitmux_t
*cmux
,
189 circuitmux_policy_data_t
*pol_data
);
191 ewma_cmp_cmux(circuitmux_t
*cmux_1
, circuitmux_policy_data_t
*pol_data_1
,
192 circuitmux_t
*cmux_2
, circuitmux_policy_data_t
*pol_data_2
);
194 /*** EWMA global variables ***/
196 /** The per-tick scale factor to be used when computing cell-count EWMA
197 * values. (A cell sent N ticks before the start of the current tick
198 * has value ewma_scale_factor ** N.)
200 static double ewma_scale_factor
= 0.1;
201 /* DOCDOC ewma_enabled */
202 static int ewma_enabled
= 0;
204 /*** EWMA circuitmux_policy_t method table ***/
206 circuitmux_policy_t ewma_policy
= {
207 /*.alloc_cmux_data =*/ ewma_alloc_cmux_data
,
208 /*.free_cmux_data =*/ ewma_free_cmux_data
,
209 /*.alloc_circ_data =*/ ewma_alloc_circ_data
,
210 /*.free_circ_data =*/ ewma_free_circ_data
,
211 /*.notify_circ_active =*/ ewma_notify_circ_active
,
212 /*.notify_circ_inactive =*/ ewma_notify_circ_inactive
,
213 /*.notify_set_n_cells =*/ NULL
, /* EWMA doesn't need this */
214 /*.notify_xmit_cells =*/ ewma_notify_xmit_cells
,
215 /*.pick_active_circuit =*/ ewma_pick_active_circuit
,
216 /*.cmp_cmux =*/ ewma_cmp_cmux
219 /*** EWMA method implementations using the below EWMA helper functions ***/
222 * Allocate an ewma_policy_data_t and upcast it to a circuitmux_policy_data_t;
223 * this is called when setting the policy on a circuitmux_t to ewma_policy.
226 static circuitmux_policy_data_t
*
227 ewma_alloc_cmux_data(circuitmux_t
*cmux
)
229 ewma_policy_data_t
*pol
= NULL
;
233 pol
= tor_malloc_zero(sizeof(*pol
));
234 pol
->base_
.magic
= EWMA_POL_DATA_MAGIC
;
235 pol
->active_circuit_pqueue
= smartlist_new();
236 pol
->active_circuit_pqueue_last_recalibrated
= cell_ewma_get_tick();
238 return TO_CMUX_POL_DATA(pol
);
242 * Free an ewma_policy_data_t allocated with ewma_alloc_cmux_data()
246 ewma_free_cmux_data(circuitmux_t
*cmux
,
247 circuitmux_policy_data_t
*pol_data
)
249 ewma_policy_data_t
*pol
= NULL
;
252 if (!pol_data
) return;
254 pol
= TO_EWMA_POL_DATA(pol_data
);
256 smartlist_free(pol
->active_circuit_pqueue
);
261 * Allocate an ewma_policy_circ_data_t and upcast it to a
262 * circuitmux_policy_data_t; this is called when attaching a circuit to a
263 * circuitmux_t with ewma_policy.
266 static circuitmux_policy_circ_data_t
*
267 ewma_alloc_circ_data(circuitmux_t
*cmux
,
268 circuitmux_policy_data_t
*pol_data
,
270 cell_direction_t direction
,
271 unsigned int cell_count
)
273 ewma_policy_circ_data_t
*cdata
= NULL
;
276 tor_assert(pol_data
);
278 tor_assert(direction
== CELL_DIRECTION_OUT
||
279 direction
== CELL_DIRECTION_IN
);
280 /* Shut the compiler up without triggering -Wtautological-compare */
283 cdata
= tor_malloc_zero(sizeof(*cdata
));
284 cdata
->base_
.magic
= EWMA_POL_CIRC_DATA_MAGIC
;
288 * Initialize the cell_ewma_t structure (formerly in
289 * init_circuit_base())
291 cdata
->cell_ewma
.last_adjusted_tick
= cell_ewma_get_tick();
292 cdata
->cell_ewma
.cell_count
= 0.0;
293 cdata
->cell_ewma
.heap_index
= -1;
294 if (direction
== CELL_DIRECTION_IN
) {
295 cdata
->cell_ewma
.is_for_p_chan
= 1;
297 cdata
->cell_ewma
.is_for_p_chan
= 0;
300 return TO_CMUX_POL_CIRC_DATA(cdata
);
304 * Free an ewma_policy_circ_data_t allocated with ewma_alloc_circ_data()
308 ewma_free_circ_data(circuitmux_t
*cmux
,
309 circuitmux_policy_data_t
*pol_data
,
311 circuitmux_policy_circ_data_t
*pol_circ_data
)
314 ewma_policy_circ_data_t
*cdata
= NULL
;
318 tor_assert(pol_data
);
320 if (!pol_circ_data
) return;
322 cdata
= TO_EWMA_POL_CIRC_DATA(pol_circ_data
);
328 * Handle circuit activation; this inserts the circuit's cell_ewma into
329 * the active_circuits_pqueue.
333 ewma_notify_circ_active(circuitmux_t
*cmux
,
334 circuitmux_policy_data_t
*pol_data
,
336 circuitmux_policy_circ_data_t
*pol_circ_data
)
338 ewma_policy_data_t
*pol
= NULL
;
339 ewma_policy_circ_data_t
*cdata
= NULL
;
342 tor_assert(pol_data
);
344 tor_assert(pol_circ_data
);
346 pol
= TO_EWMA_POL_DATA(pol_data
);
347 cdata
= TO_EWMA_POL_CIRC_DATA(pol_circ_data
);
349 add_cell_ewma(pol
, &(cdata
->cell_ewma
));
353 * Handle circuit deactivation; this removes the circuit's cell_ewma from
354 * the active_circuits_pqueue.
358 ewma_notify_circ_inactive(circuitmux_t
*cmux
,
359 circuitmux_policy_data_t
*pol_data
,
361 circuitmux_policy_circ_data_t
*pol_circ_data
)
363 ewma_policy_data_t
*pol
= NULL
;
364 ewma_policy_circ_data_t
*cdata
= NULL
;
367 tor_assert(pol_data
);
369 tor_assert(pol_circ_data
);
371 pol
= TO_EWMA_POL_DATA(pol_data
);
372 cdata
= TO_EWMA_POL_CIRC_DATA(pol_circ_data
);
374 remove_cell_ewma(pol
, &(cdata
->cell_ewma
));
378 * Update cell_ewma for this circuit after we've sent some cells, and
379 * remove/reinsert it in the queue. This used to be done (brokenly,
380 * see bug 6816) in channel_flush_from_first_active_circuit().
384 ewma_notify_xmit_cells(circuitmux_t
*cmux
,
385 circuitmux_policy_data_t
*pol_data
,
387 circuitmux_policy_circ_data_t
*pol_circ_data
,
388 unsigned int n_cells
)
390 ewma_policy_data_t
*pol
= NULL
;
391 ewma_policy_circ_data_t
*cdata
= NULL
;
393 double fractional_tick
, ewma_increment
;
394 /* The current (hi-res) time */
395 struct timeval now_hires
;
396 cell_ewma_t
*cell_ewma
, *tmp
;
399 tor_assert(pol_data
);
401 tor_assert(pol_circ_data
);
402 tor_assert(n_cells
> 0);
404 pol
= TO_EWMA_POL_DATA(pol_data
);
405 cdata
= TO_EWMA_POL_CIRC_DATA(pol_circ_data
);
407 /* Rescale the EWMAs if needed */
408 tor_gettimeofday_cached(&now_hires
);
409 tick
= cell_ewma_tick_from_timeval(&now_hires
, &fractional_tick
);
411 if (tick
!= pol
->active_circuit_pqueue_last_recalibrated
) {
412 scale_active_circuits(pol
, tick
);
415 /* How much do we adjust the cell count in cell_ewma by? */
417 ((double)(n_cells
)) * pow(ewma_scale_factor
, -fractional_tick
);
419 /* Do the adjustment */
420 cell_ewma
= &(cdata
->cell_ewma
);
421 cell_ewma
->cell_count
+= ewma_increment
;
424 * Since we just sent on this circuit, it should be at the head of
425 * the queue. Pop the head, assert that it matches, then re-add.
427 tmp
= pop_first_cell_ewma(pol
);
428 tor_assert(tmp
== cell_ewma
);
429 add_cell_ewma(pol
, cell_ewma
);
433 * Pick the preferred circuit to send from; this will be the one with
434 * the lowest EWMA value in the priority queue. This used to be done
435 * in channel_flush_from_first_active_circuit().
439 ewma_pick_active_circuit(circuitmux_t
*cmux
,
440 circuitmux_policy_data_t
*pol_data
)
442 ewma_policy_data_t
*pol
= NULL
;
443 circuit_t
*circ
= NULL
;
444 cell_ewma_t
*cell_ewma
= NULL
;
447 tor_assert(pol_data
);
449 pol
= TO_EWMA_POL_DATA(pol_data
);
451 if (smartlist_len(pol
->active_circuit_pqueue
) > 0) {
452 /* Get the head of the queue */
453 cell_ewma
= smartlist_get(pol
->active_circuit_pqueue
, 0);
454 circ
= cell_ewma_to_circuit(cell_ewma
);
461 * Compare two EWMA cmuxes, and return -1, 0 or 1 to indicate which should
462 * be more preferred - see circuitmux_compare_muxes() of circuitmux.c.
466 ewma_cmp_cmux(circuitmux_t
*cmux_1
, circuitmux_policy_data_t
*pol_data_1
,
467 circuitmux_t
*cmux_2
, circuitmux_policy_data_t
*pol_data_2
)
469 ewma_policy_data_t
*p1
= NULL
, *p2
= NULL
;
470 cell_ewma_t
*ce1
= NULL
, *ce2
= NULL
;
473 tor_assert(pol_data_1
);
475 tor_assert(pol_data_2
);
477 p1
= TO_EWMA_POL_DATA(pol_data_1
);
478 p2
= TO_EWMA_POL_DATA(pol_data_1
);
481 /* Get the head cell_ewma_t from each queue */
482 if (smartlist_len(p1
->active_circuit_pqueue
) > 0) {
483 ce1
= smartlist_get(p1
->active_circuit_pqueue
, 0);
486 if (smartlist_len(p2
->active_circuit_pqueue
) > 0) {
487 ce2
= smartlist_get(p2
->active_circuit_pqueue
, 0);
490 /* Got both of them? */
491 if (ce1
!= NULL
&& ce2
!= NULL
) {
492 /* Pick whichever one has the better best circuit */
493 return compare_cell_ewma_counts(ce1
, ce2
);
496 /* We only have a circuit on cmux_1, so prefer it */
498 } else if (ce2
!= NULL
) {
499 /* We only have a circuit on cmux_2, so prefer it */
502 /* No circuits at all; no preference */
507 /* We got identical params */
512 /** Helper for sorting cell_ewma_t values in their priority queue. */
514 compare_cell_ewma_counts(const void *p1
, const void *p2
)
516 const cell_ewma_t
*e1
= p1
, *e2
= p2
;
518 if (e1
->cell_count
< e2
->cell_count
)
520 else if (e1
->cell_count
> e2
->cell_count
)
526 /** Given a cell_ewma_t, return a pointer to the circuit containing it. */
528 cell_ewma_to_circuit(cell_ewma_t
*ewma
)
530 ewma_policy_circ_data_t
*cdata
= NULL
;
533 cdata
= SUBTYPE_P(ewma
, ewma_policy_circ_data_t
, cell_ewma
);
539 /* ==== Functions for scaling cell_ewma_t ====
541 When choosing which cells to relay first, we favor circuits that have been
542 quiet recently. This gives better latency on connections that aren't
543 pushing lots of data, and makes the network feel more interactive.
545 Conceptually, we take an exponentially weighted mean average of the number
546 of cells a circuit has sent, and allow active circuits (those with cells to
547 relay) to send cells in reverse order of their exponentially-weighted mean
548 average (EWMA) cell count. [That is, a cell sent N seconds ago 'counts'
549 F^N times as much as a cell sent now, for 0<F<1.0, and we favor the
550 circuit that has sent the fewest cells]
552 If 'double' had infinite precision, we could do this simply by counting a
553 cell sent at startup as having weight 1.0, and a cell sent N seconds later
554 as having weight F^-N. This way, we would never need to re-scale
555 any already-sent cells.
557 To prevent double from overflowing, we could count a cell sent now as
558 having weight 1.0 and a cell sent N seconds ago as having weight F^N.
559 This, however, would mean we'd need to re-scale *ALL* old circuits every
560 time we wanted to send a cell.
562 So as a compromise, we divide time into 'ticks' (currently, 10-second
563 increments) and say that a cell sent at the start of a current tick is
564 worth 1.0, a cell sent N seconds before the start of the current tick is
565 worth F^N, and a cell sent N seconds after the start of the current tick is
566 worth F^-N. This way we don't overflow, and we don't need to constantly
570 /** Given a timeval <b>now</b>, compute the cell_ewma tick in which it occurs
571 * and the fraction of the tick that has elapsed between the start of the tick
572 * and <b>now</b>. Return the former and store the latter in
573 * *<b>remainder_out</b>.
575 * These tick values are not meant to be shared between Tor instances, or used
576 * for other purposes. */
579 cell_ewma_tick_from_timeval(const struct timeval
*now
,
580 double *remainder_out
)
582 unsigned res
= (unsigned) (now
->tv_sec
/ EWMA_TICK_LEN
);
584 double rem
= (now
->tv_sec
% EWMA_TICK_LEN
) +
585 ((double)(now
->tv_usec
)) / 1.0e6
;
586 *remainder_out
= rem
/ EWMA_TICK_LEN
;
590 /** Tell the caller whether ewma_enabled is set */
592 cell_ewma_enabled(void)
597 /** Compute and return the current cell_ewma tick. */
599 cell_ewma_get_tick(void)
601 return ((unsigned)approx_time() / EWMA_TICK_LEN
);
604 /** Adjust the global cell scale factor based on <b>options</b> */
606 cell_ewma_set_scale_factor(const or_options_t
*options
,
607 const networkstatus_t
*consensus
)
612 if (options
&& options
->CircuitPriorityHalflife
>= -EPSILON
) {
613 halflife
= options
->CircuitPriorityHalflife
;
614 source
= "CircuitPriorityHalflife in configuration";
615 } else if (consensus
&& (halflife_ms
= networkstatus_get_param(
616 consensus
, "CircuitPriorityHalflifeMsec",
617 -1, -1, INT32_MAX
)) >= 0) {
618 halflife
= ((double)halflife_ms
)/1000.0;
619 source
= "CircuitPriorityHalflifeMsec in consensus";
621 halflife
= EWMA_DEFAULT_HALFLIFE
;
622 source
= "Default value";
625 if (halflife
<= EPSILON
) {
626 /* The cell EWMA algorithm is disabled. */
627 ewma_scale_factor
= 0.1;
630 "Disabled cell_ewma algorithm because of value in %s",
633 /* convert halflife into halflife-per-tick. */
634 halflife
/= EWMA_TICK_LEN
;
635 /* compute per-tick scale factor. */
636 ewma_scale_factor
= exp( LOG_ONEHALF
/ halflife
);
639 "Enabled cell_ewma algorithm because of value in %s; "
640 "scale factor is %f per %d seconds",
641 source
, ewma_scale_factor
, EWMA_TICK_LEN
);
645 /** Return the multiplier necessary to convert the value of a cell sent in
646 * 'from_tick' to one sent in 'to_tick'. */
648 get_scale_factor(unsigned from_tick
, unsigned to_tick
)
650 /* This math can wrap around, but that's okay: unsigned overflow is
652 int diff
= (int)(to_tick
- from_tick
);
653 return pow(ewma_scale_factor
, diff
);
656 /** Adjust the cell count of <b>ewma</b> so that it is scaled with respect to
659 scale_single_cell_ewma(cell_ewma_t
*ewma
, unsigned cur_tick
)
661 double factor
= get_scale_factor(ewma
->last_adjusted_tick
, cur_tick
);
662 ewma
->cell_count
*= factor
;
663 ewma
->last_adjusted_tick
= cur_tick
;
666 /** Adjust the cell count of every active circuit on <b>chan</b> so
667 * that they are scaled with respect to <b>cur_tick</b> */
669 scale_active_circuits(ewma_policy_data_t
*pol
, unsigned cur_tick
)
674 tor_assert(pol
->active_circuit_pqueue
);
678 pol
->active_circuit_pqueue_last_recalibrated
,
680 /** Ordinarily it isn't okay to change the value of an element in a heap,
681 * but it's okay here, since we are preserving the order. */
682 SMARTLIST_FOREACH_BEGIN(
683 pol
->active_circuit_pqueue
,
685 tor_assert(e
->last_adjusted_tick
==
686 pol
->active_circuit_pqueue_last_recalibrated
);
687 e
->cell_count
*= factor
;
688 e
->last_adjusted_tick
= cur_tick
;
689 } SMARTLIST_FOREACH_END(e
);
690 pol
->active_circuit_pqueue_last_recalibrated
= cur_tick
;
693 /** Rescale <b>ewma</b> to the same scale as <b>pol</b>, and add it to
694 * <b>pol</b>'s priority queue of active circuits */
696 add_cell_ewma(ewma_policy_data_t
*pol
, cell_ewma_t
*ewma
)
699 tor_assert(pol
->active_circuit_pqueue
);
701 tor_assert(ewma
->heap_index
== -1);
703 scale_single_cell_ewma(
705 pol
->active_circuit_pqueue_last_recalibrated
);
707 smartlist_pqueue_add(pol
->active_circuit_pqueue
,
708 compare_cell_ewma_counts
,
709 STRUCT_OFFSET(cell_ewma_t
, heap_index
),
713 /** Remove <b>ewma</b> from <b>pol</b>'s priority queue of active circuits */
715 remove_cell_ewma(ewma_policy_data_t
*pol
, cell_ewma_t
*ewma
)
718 tor_assert(pol
->active_circuit_pqueue
);
720 tor_assert(ewma
->heap_index
!= -1);
722 smartlist_pqueue_remove(pol
->active_circuit_pqueue
,
723 compare_cell_ewma_counts
,
724 STRUCT_OFFSET(cell_ewma_t
, heap_index
),
728 /** Remove and return the first cell_ewma_t from pol's priority queue of
729 * active circuits. Requires that the priority queue is nonempty. */
731 pop_first_cell_ewma(ewma_policy_data_t
*pol
)
734 tor_assert(pol
->active_circuit_pqueue
);
736 return smartlist_pqueue_pop(pol
->active_circuit_pqueue
,
737 compare_cell_ewma_counts
,
738 STRUCT_OFFSET(cell_ewma_t
, heap_index
));