aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2010-05-06 14:02:22 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2010-05-06 14:02:22 +0000
commit0a876ee595e6526627352f8c2d3252838d69af43 (patch)
treeb6247799f5d2105b1cf76381afefff975027e0bb
parent72baa3968d8f9619a78cbea0c9de4b612adc2957 (diff)
Merged revisions 261496 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ........ r261496 | russell | 2010-05-06 08:58:07 -0500 (Thu, 06 May 2010) | 40 lines Fix handling of removing nodes from the middle of a heap. This bug surfaced in 1.6.2 and does not affect code in any other released version of Asterisk. It manifested itself as SIP qualify not happening when it should, causing peers to go unreachable. This was debugged down to scheduler entries sometimes not getting executed when they were supposed to, which was in turn caused by an error in the heap code. The problem only sometimes occurs, and it is due to the logic for removing an entry in the heap from an arbitrary location (not just popping off the top). The scheduler performs this operation frequently when entries are removed before they run (when ast_sched_del() is used). In a normal pop off of the top of the heap, a node is taken off the bottom, placed at the top, and then bubbled down until the max heap property is restored (see max_heapify()). This same logic was used for removing an arbitrary node from the middle of the heap. Unfortunately, that logic is full of fail. This patch fixes that by fully restoring the max heap property when a node is thrown into the middle of the heap. Instead of just pushing it down as appropriate, it first pushes it up as high as it will go, and _then_ pushes it down. Lastly, fix a minor problem in ast_heap_verify(), which is only used for debugging. If a parent and child node have the same value, that is not an error. The only error is if a parent's value is less than its children. A huge thanks goes out to cappucinoking for debugging this down to the scheduler, and then producing an ast_heap test case that demonstrated the breakage. That made it very easy for me to focus on the heap logic and produce a fix. Open source projects are awesome. (closes issue #16936) Reported by: ib2 Tested by: cappucinoking, crjw (closes issue #17277) Reported by: cappucinoking Patches: heap-fix.rev2.diff uploaded by russell (license 2) Tested by: cappucinoking, russell ........ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.1@261497 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--main/heap.c24
1 files changed, 14 insertions, 10 deletions
diff --git a/main/heap.c b/main/heap.c
index ee99f9919..a7a0ee643 100644
--- a/main/heap.c
+++ b/main/heap.c
@@ -92,13 +92,13 @@ int ast_heap_verify(struct ast_heap *h)
int r = right_node(i);
if (l <= h->cur_len) {
- if (h->cmp_fn(heap_get(h, i), heap_get(h, l)) <= 0) {
+ if (h->cmp_fn(heap_get(h, i), heap_get(h, l)) < 0) {
return -1;
}
}
if (r <= h->cur_len) {
- if (h->cmp_fn(heap_get(h, i), heap_get(h, r)) <= 0) {
+ if (h->cmp_fn(heap_get(h, i), heap_get(h, r)) < 0) {
return -1;
}
}
@@ -202,21 +202,25 @@ static inline void max_heapify(struct ast_heap *h, int i)
}
}
-int ast_heap_push(struct ast_heap *h, void *elm)
+static int bubble_up(struct ast_heap *h, int i)
{
- int i;
+ while (i > 1 && h->cmp_fn(heap_get(h, parent_node(i)), heap_get(h, i)) < 0) {
+ heap_swap(h, i, parent_node(i));
+ i = parent_node(i);
+ }
+
+ return i;
+}
+int ast_heap_push(struct ast_heap *h, void *elm)
+{
if (h->cur_len == h->avail_len && grow_heap(h)) {
return -1;
}
heap_set(h, ++(h->cur_len), elm);
- for (i = h->cur_len;
- i > 1 && h->cmp_fn(heap_get(h, parent_node(i)), heap_get(h, i)) < 0;
- i = parent_node(i)) {
- heap_swap(h, i, parent_node(i));
- }
+ bubble_up(h, h->cur_len);
return 0;
}
@@ -231,7 +235,7 @@ static void *_ast_heap_remove(struct ast_heap *h, unsigned int index)
ret = heap_get(h, index);
heap_set(h, index, heap_get(h, (h->cur_len)--));
-
+ index = bubble_up(h, index);
max_heapify(h, index);
return ret;