Skip to content

Commit 013d149

Browse files
committed
Fix LRU single-entry eviction
Evicting the only node in an LRU queue cleared the front pointer but left the end pointer referencing the removed node. A later empty eviction could see stale queue state and risk acting on an already removed entry. The removal path now clears queue->end when removing the last node, keeping the empty queue state consistent. Added an LRU unit test that evicts a single entry, verifies both queue ends are NULL, and confirms a second empty eviction does not run cleanup again.
1 parent f9bd494 commit 013d149

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

src/library/lru.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ static void remove_node(Queue *queue, const QNode *node)
265265
queue->front = node->next;
266266
if (queue->front)
267267
queue->front->prev = NULL;
268+
else
269+
queue->end = NULL;
268270
goto out;
269271
} else {
270272
if (node->prev->next != node) {

src/tests/lru_test.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,32 @@ static void test_reuse_after_evict(void)
5252
destroy_lru(queue);
5353
}
5454

55+
static void test_empty_after_single_evict(void)
56+
{
57+
Queue *queue;
58+
QNode *first;
59+
60+
cleaned = 0;
61+
queue = init_lru(1, cleanup_item, "empty", NULL);
62+
if (queue == NULL)
63+
error(1, 0, "init_lru failed");
64+
65+
first = check_lru_cache(queue, 0);
66+
if (first == NULL)
67+
error(1, 0, "check_lru_cache returned NULL");
68+
attach_item(first, 5);
69+
70+
lru_evict(queue, 0);
71+
if (queue->front != NULL || queue->end != NULL || queue->count != 0)
72+
error(1, 0, "single eviction left non-empty queue state");
73+
74+
lru_evict(queue, 0);
75+
if (cleaned != 1)
76+
error(1, 0, "empty eviction ran cleanup again");
77+
78+
destroy_lru(queue);
79+
}
80+
5581
static void test_pool_exhaustion(void)
5682
{
5783
Queue *queue;
@@ -173,6 +199,7 @@ static void test_null_queue_metrics(void)
173199
int main(void)
174200
{
175201
test_reuse_after_evict();
202+
test_empty_after_single_evict();
176203
test_pool_exhaustion();
177204
test_metrics_reset();
178205
test_collision_metrics();

0 commit comments

Comments
 (0)