Use a TAILQ, not a singly-linked queue, for the onion queue.

This makes removing items from the middle of the queue into an O(1)
operation, which could prove important as we let onionqueues grow
longer.

Doing this actually makes the code slightly smaller, too.
This commit is contained in:
Nick Mathewson 2012-12-26 22:08:12 -05:00
parent b0b3c14c11
commit b9fb01721a
2 changed files with 56 additions and 67 deletions

View File

@ -21,29 +21,29 @@
#include "relay.h"
#include "rephist.h"
#include "router.h"
#include "tor_queue.h"
/** Type for a linked list of circuits that are waiting for a free CPU worker
* to process a waiting onion handshake. */
typedef struct onion_queue_t {
TAILQ_ENTRY(onion_queue_t) next;
or_circuit_t *circ;
create_cell_t *onionskin;
time_t when_added;
struct onion_queue_t *next;
} onion_queue_t;
/** 5 seconds on the onion queue til we just send back a destroy */
#define ONIONQUEUE_WAIT_CUTOFF 5
/** First and last elements in the linked list of circuits waiting for CPU
* workers, or NULL if the list is empty.
* @{ */
static onion_queue_t *ol_list=NULL;
static onion_queue_t *ol_tail=NULL;
/**@}*/
/** Queue of circuits waiting for CPU workers, or NULL if the list is empty.*/
TAILQ_HEAD(onion_queue_head_t, onion_queue_t) ol_list =
TAILQ_HEAD_INITIALIZER(ol_list);
/** Number of entries of each type currently in ol_list. */
static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
static void onion_queue_entry_remove(onion_queue_t *victim);
/* XXXX024 Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
*
* (By which I think I meant, "make sure that no
@ -101,17 +101,6 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
tmp->onionskin = onionskin;
tmp->when_added = now;
if (!ol_tail) {
tor_assert(!ol_list);
ol_list = tmp;
ol_tail = tmp;
++ol_entries[onionskin->handshake_type];
return 0;
}
tor_assert(ol_list);
tor_assert(!ol_tail->next);
if (!have_room_for_onionskin(onionskin->handshake_type)) {
#define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60)
static ratelim_t last_warned =
@ -130,12 +119,18 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
}
++ol_entries[onionskin->handshake_type];
ol_tail->next = tmp;
ol_tail = tmp;
while ((int)(now - ol_list->when_added) >= ONIONQUEUE_WAIT_CUTOFF) {
/* cull elderly requests. */
circ = ol_list->circ;
onion_pending_remove(ol_list->circ);
circ->onionqueue_entry = tmp;
TAILQ_INSERT_TAIL(&ol_list, tmp, next);
/* cull elderly requests. */
while (1) {
onion_queue_t *head = TAILQ_FIRST(&ol_list);
if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF)
break;
circ = head->circ;
circ->onionqueue_entry = NULL;
onion_queue_entry_remove(head);
log_info(LD_CIRC,
"Circuit create request is too old; canceling due to overload.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
@ -150,19 +145,21 @@ or_circuit_t *
onion_next_task(create_cell_t **onionskin_out)
{
or_circuit_t *circ;
onion_queue_t *head = TAILQ_FIRST(&ol_list);
if (!ol_list)
if (!head)
return NULL; /* no onions pending, we're done */
tor_assert(ol_list->circ);
tor_assert(ol_list->circ->p_chan); /* make sure it's still valid */
circ = ol_list->circ;
if (ol_list->onionskin &&
ol_list->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
--ol_entries[ol_list->onionskin->handshake_type];
*onionskin_out = ol_list->onionskin;
ol_list->onionskin = NULL; /* prevent free. */
onion_pending_remove(ol_list->circ);
tor_assert(head->circ);
tor_assert(head->circ->p_chan); /* make sure it's still valid */
circ = head->circ;
if (head->onionskin &&
head->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
--ol_entries[head->onionskin->handshake_type];
*onionskin_out = head->onionskin;
head->onionskin = NULL; /* prevent free. */
circ->onionqueue_entry = NULL;
onion_queue_entry_remove(head);
return circ;
}
@ -172,40 +169,30 @@ onion_next_task(create_cell_t **onionskin_out)
void
onion_pending_remove(or_circuit_t *circ)
{
onion_queue_t *tmpo, *victim;
onion_queue_t *victim;
if (!ol_list)
return; /* nothing here. */
if (!circ)
return;
/* first check to see if it's the first entry */
tmpo = ol_list;
if (tmpo->circ == circ) {
/* it's the first one. remove it from the list. */
ol_list = tmpo->next;
if (!ol_list)
ol_tail = NULL;
victim = tmpo;
} else { /* we need to hunt through the rest of the list */
for ( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ;
if (!tmpo->next) {
log_debug(LD_GENERAL,
"circ (p_circ_id %d) not in list, probably at cpuworker.",
circ->p_circ_id);
return;
}
/* now we know tmpo->next->circ == circ */
victim = tmpo->next;
tmpo->next = victim->next;
if (ol_tail == victim)
ol_tail = tmpo;
}
victim = circ->onionqueue_entry;
if (victim)
onion_queue_entry_remove(victim);
}
/** Remove a queue entry <b>victim</b> from the queue, unlinking it from
* its circuit and freeing it and any structures it owns.*/
static void
onion_queue_entry_remove(onion_queue_t *victim)
{
TAILQ_REMOVE(&ol_list, victim, next);
if (victim->circ)
victim->circ->onionqueue_entry = NULL;
if (victim->onionskin &&
victim->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
--ol_entries[victim->onionskin->handshake_type];
/* now victim points to the element that needs to be removed */
tor_free(victim->onionskin);
tor_free(victim);
}
@ -214,13 +201,10 @@ onion_pending_remove(or_circuit_t *circ)
void
clear_pending_onions(void)
{
while (ol_list) {
onion_queue_t *victim = ol_list;
ol_list = victim->next;
tor_free(victim->onionskin);
tor_free(victim);
onion_queue_t *victim;
while ((victim = TAILQ_FIRST(&ol_list))) {
onion_queue_entry_remove(victim);
}
ol_list = ol_tail = NULL;
memset(ol_entries, 0, sizeof(ol_entries));
}

View File

@ -2988,6 +2988,8 @@ typedef struct origin_circuit_t {
} origin_circuit_t;
struct onion_queue_t;
/** An or_circuit_t holds information needed to implement a circuit at an
* OR. */
typedef struct or_circuit_t {
@ -3001,6 +3003,9 @@ typedef struct or_circuit_t {
* cells to p_chan. NULL if we have no cells pending, or if we're not
* linked to an OR connection. */
struct circuit_t *prev_active_on_p_chan;
/** Pointer to an entry on the onion queue, if this circuit is waiting for a
* chance to give an onionskin to a cpuworker. Used only in onion.c */
struct onion_queue_t *onionqueue_entry;
/** The circuit_id used in the previous (backward) hop of this circuit. */
circid_t p_circ_id;