mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-03 04:22:47 -05:00
Replace PktIn reference count with a 'free queue'.
This is a new idea I've had to make memory-management of PktIn even easier. The idea is that a PktIn is essentially _always_ an element of some linked-list queue: if it's not one of the queues by which packets move through ssh.c, then it's a special 'free queue' which holds packets that are unowned and due to be freed. pq_pop() on a PktInQueue automatically relinks the packet to the free queue, and also triggers an idempotent callback which will empty the queue and really free all the packets on it. Hence, you can pop a packet off a real queue, parse it, handle it, and then just assume it'll get tidied up at some point - the only constraint being that you have to finish with it before returning to the application's main loop. The exception is that it's OK to pq_push() the packet back on to some other PktInQueue, because a side effect of that will be to _remove_ it from the free queue again. (And if _all_ the incoming packets get that treatment, then when the free-queue handler eventually runs, it may find it has nothing to do - which is harmless.)
This commit is contained in:
59
sshcommon.c
59
sshcommon.c
@ -15,10 +15,20 @@
|
||||
* Implementation of PacketQueue.
|
||||
*/
|
||||
|
||||
static void pq_ensure_unlinked(PacketQueueNode *node)
|
||||
{
|
||||
if (node->on_free_queue) {
|
||||
node->next->prev = node->prev;
|
||||
node->prev->next = node->next;
|
||||
} else {
|
||||
assert(!node->next);
|
||||
assert(!node->prev);
|
||||
}
|
||||
}
|
||||
|
||||
void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
|
||||
{
|
||||
assert(!node->next);
|
||||
assert(!node->prev);
|
||||
pq_ensure_unlinked(node);
|
||||
node->next = &pqb->end;
|
||||
node->prev = pqb->end.prev;
|
||||
node->next->prev = node;
|
||||
@ -27,14 +37,33 @@ void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
|
||||
|
||||
void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
|
||||
{
|
||||
assert(!node->next);
|
||||
assert(!node->prev);
|
||||
pq_ensure_unlinked(node);
|
||||
node->prev = &pqb->end;
|
||||
node->next = pqb->end.next;
|
||||
node->next->prev = node;
|
||||
node->prev->next = node;
|
||||
}
|
||||
|
||||
static PacketQueueNode pktin_freeq_head = {
|
||||
&pktin_freeq_head, &pktin_freeq_head, TRUE
|
||||
};
|
||||
|
||||
static void pktin_free_queue_callback(void *vctx)
|
||||
{
|
||||
while (pktin_freeq_head.next != &pktin_freeq_head) {
|
||||
PacketQueueNode *node = pktin_freeq_head.next;
|
||||
PktIn *pktin = FROMFIELD(node, PktIn, qnode);
|
||||
pktin_freeq_head.next = node->next;
|
||||
sfree(pktin);
|
||||
}
|
||||
|
||||
pktin_freeq_head.prev = &pktin_freeq_head;
|
||||
}
|
||||
|
||||
static IdempotentCallback ic_pktin_free = {
|
||||
pktin_free_queue_callback, NULL, FALSE
|
||||
};
|
||||
|
||||
static PktIn *pq_in_get(PacketQueueBase *pqb, int pop)
|
||||
{
|
||||
PacketQueueNode *node = pqb->end.next;
|
||||
@ -44,7 +73,13 @@ static PktIn *pq_in_get(PacketQueueBase *pqb, int pop)
|
||||
if (pop) {
|
||||
node->next->prev = node->prev;
|
||||
node->prev->next = node->next;
|
||||
node->prev = node->next = NULL;
|
||||
|
||||
node->prev = pktin_freeq_head.prev;
|
||||
node->next = &pktin_freeq_head;
|
||||
node->next->prev = node;
|
||||
node->prev->next = node;
|
||||
node->on_free_queue = TRUE;
|
||||
queue_idempotent_callback(&ic_pktin_free);
|
||||
}
|
||||
|
||||
return FROMFIELD(node, PktIn, qnode);
|
||||
@ -80,8 +115,11 @@ void pq_out_init(PktOutQueue *pq)
|
||||
void pq_in_clear(PktInQueue *pq)
|
||||
{
|
||||
PktIn *pkt;
|
||||
while ((pkt = pq_pop(pq)) != NULL)
|
||||
ssh_unref_packet(pkt);
|
||||
while ((pkt = pq_pop(pq)) != NULL) {
|
||||
/* No need to actually free these packets: pq_pop on a
|
||||
* PktInQueue will automatically move them to the free
|
||||
* queue. */
|
||||
}
|
||||
}
|
||||
|
||||
void pq_out_clear(PktOutQueue *pq)
|
||||
@ -170,6 +208,7 @@ PktOut *ssh_new_packet(void)
|
||||
pkt->downstream_id = 0;
|
||||
pkt->additional_log_text = NULL;
|
||||
pkt->qnode.next = pkt->qnode.prev = NULL;
|
||||
pkt->qnode.on_free_queue = FALSE;
|
||||
|
||||
return pkt;
|
||||
}
|
||||
@ -195,12 +234,6 @@ static void ssh_pkt_BinarySink_write(BinarySink *bs,
|
||||
ssh_pkt_adddata(pkt, data, len);
|
||||
}
|
||||
|
||||
void ssh_unref_packet(PktIn *pkt)
|
||||
{
|
||||
if (--pkt->refcount <= 0)
|
||||
sfree(pkt);
|
||||
}
|
||||
|
||||
void ssh_free_pktout(PktOut *pkt)
|
||||
{
|
||||
sfree(pkt->data);
|
||||
|
Reference in New Issue
Block a user