From d59d22e20afb5e8734f1be24bdb804bac599b43b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 7 Jan 2007 00:18:48 +0100 Subject: [PATCH 1/2] [MINOR] imported the rbtree function from Linux kernel Those rbtree functions will be used by Sin Yu's new rbtree scheduler. --- include/common/rbtree.h | 150 +++++++++++++++ src/rbtree.c | 399 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 549 insertions(+) create mode 100644 include/common/rbtree.h create mode 100644 src/rbtree.c diff --git a/include/common/rbtree.h b/include/common/rbtree.h new file mode 100644 index 000000000..107e8d237 --- /dev/null +++ b/include/common/rbtree.h @@ -0,0 +1,150 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + Some example of insert and search follows here. The search is a plain + normal search over an ordered tree. The insert instead must be implemented + int two steps: as first thing the code must insert the element in + order as a red leaf in the tree, then the support library function + rb_insert_color() must be called. Such function will do the + not trivial work to rebalance the rbtree if necessary. + +----------------------------------------------------------------------- +static inline struct page * rb_search_page_cache(struct inode * inode, + unsigned long offset) +{ + struct rb_node * n = inode->i_rb_page_cache.rb_node; + struct page * page; + + while (n) + { + page = rb_entry(n, struct page, rb_page_cache); + + if (offset < page->offset) + n = n->rb_left; + else if (offset > page->offset) + n = n->rb_right; + else + return page; + } + return NULL; +} + +static inline struct page * __rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct rb_node ** p = &inode->i_rb_page_cache.rb_node; + struct rb_node * parent = NULL; + struct page * page; + + while (*p) + { + parent = *p; + page = rb_entry(parent, struct page, rb_page_cache); + + if (offset < page->offset) + p = &(*p)->rb_left; + else if (offset > page->offset) + p = &(*p)->rb_right; + else + return page; + } + + rb_link_node(node, parent, p); + + return NULL; +} + +static inline struct page * rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct page * ret; + if ((ret = __rb_insert_page_cache(inode, offset, node))) + goto out; + rb_insert_color(node, &inode->i_rb_page_cache); + out: + return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef _LINUX_RBTREE_H +#define _LINUX_RBTREE_H + +/* +#include +#include +*/ + +struct rb_node +{ + struct rb_node *rb_parent; + int rb_color; +#define RB_RED 0 +#define RB_BLACK 1 + struct rb_node *rb_right; + struct rb_node *rb_left; +}; + +struct rb_root +{ + struct rb_node *rb_node; +}; + +// Copy from linux kernel 2.6 source (kernel.h, stddef.h) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + + +#define RB_ROOT (struct rb_root) { NULL, } +#define rb_entry(ptr, type, member) container_of(ptr, type, member) + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(struct rb_node *); +extern struct rb_node *rb_prev(struct rb_node *); +extern struct rb_node *rb_first(struct rb_root *); +extern struct rb_node *rb_last(struct rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); + +static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, + struct rb_node ** rb_link) +{ + node->rb_parent = parent; + node->rb_color = RB_RED; + node->rb_left = node->rb_right = NULL; + + *rb_link = node; +} + +#endif /* _LINUX_RBTREE_H */ diff --git a/src/rbtree.c b/src/rbtree.c new file mode 100644 index 000000000..86b21badf --- /dev/null +++ b/src/rbtree.c @@ -0,0 +1,399 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/lib/rbtree.c +*/ + +/* +#include +#include +*/ + +#include +#include + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *right = node->rb_right; + + if ((node->rb_right = right->rb_left)) + right->rb_left->rb_parent = node; + right->rb_left = node; + + if ((right->rb_parent = node->rb_parent)) + { + if (node == node->rb_parent->rb_left) + node->rb_parent->rb_left = right; + else + node->rb_parent->rb_right = right; + } + else + root->rb_node = right; + node->rb_parent = right; +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *left = node->rb_left; + + if ((node->rb_left = left->rb_right)) + left->rb_right->rb_parent = node; + left->rb_right = node; + + if ((left->rb_parent = node->rb_parent)) + { + if (node == node->rb_parent->rb_right) + node->rb_parent->rb_right = left; + else + node->rb_parent->rb_left = left; + } + else + root->rb_node = left; + node->rb_parent = left; +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *parent, *gparent; + + while ((parent = node->rb_parent) && parent->rb_color == RB_RED) + { + gparent = parent->rb_parent; + + if (parent == gparent->rb_left) + { + { + register struct rb_node *uncle = gparent->rb_right; + if (uncle && uncle->rb_color == RB_RED) + { + uncle->rb_color = RB_BLACK; + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + node = gparent; + continue; + } + } + + if (parent->rb_right == node) + { + register struct rb_node *tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + __rb_rotate_right(gparent, root); + } else { + { + register struct rb_node *uncle = gparent->rb_left; + if (uncle && uncle->rb_color == RB_RED) + { + uncle->rb_color = RB_BLACK; + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + node = gparent; + continue; + } + } + + if (parent->rb_left == node) + { + register struct rb_node *tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + __rb_rotate_left(gparent, root); + } + } + + root->rb_node->rb_color = RB_BLACK; +} +// EXPORT_SYMBOL(rb_insert_color); + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, + struct rb_root *root) +{ + struct rb_node *other; + + while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) + { + if (parent->rb_left == node) + { + other = parent->rb_right; + if (other->rb_color == RB_RED) + { + other->rb_color = RB_BLACK; + parent->rb_color = RB_RED; + __rb_rotate_left(parent, root); + other = parent->rb_right; + } + if ((!other->rb_left || + other->rb_left->rb_color == RB_BLACK) + && (!other->rb_right || + other->rb_right->rb_color == RB_BLACK)) + { + other->rb_color = RB_RED; + node = parent; + parent = node->rb_parent; + } + else + { + if (!other->rb_right || + other->rb_right->rb_color == RB_BLACK) + { + register struct rb_node *o_left; + if ((o_left = other->rb_left)) + o_left->rb_color = RB_BLACK; + other->rb_color = RB_RED; + __rb_rotate_right(other, root); + other = parent->rb_right; + } + other->rb_color = parent->rb_color; + parent->rb_color = RB_BLACK; + if (other->rb_right) + other->rb_right->rb_color = RB_BLACK; + __rb_rotate_left(parent, root); + node = root->rb_node; + break; + } + } + else + { + other = parent->rb_left; + if (other->rb_color == RB_RED) + { + other->rb_color = RB_BLACK; + parent->rb_color = RB_RED; + __rb_rotate_right(parent, root); + other = parent->rb_left; + } + if ((!other->rb_left || + other->rb_left->rb_color == RB_BLACK) + && (!other->rb_right || + other->rb_right->rb_color == RB_BLACK)) + { + other->rb_color = RB_RED; + node = parent; + parent = node->rb_parent; + } + else + { + if (!other->rb_left || + other->rb_left->rb_color == RB_BLACK) + { + register struct rb_node *o_right; + if ((o_right = other->rb_right)) + o_right->rb_color = RB_BLACK; + other->rb_color = RB_RED; + __rb_rotate_left(other, root); + other = parent->rb_left; + } + other->rb_color = parent->rb_color; + parent->rb_color = RB_BLACK; + if (other->rb_left) + other->rb_left->rb_color = RB_BLACK; + __rb_rotate_right(parent, root); + node = root->rb_node; + break; + } + } + } + if (node) + node->rb_color = RB_BLACK; +} + +void rb_erase(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *child, *parent; + int color; + + if (!node->rb_left) + child = node->rb_right; + else if (!node->rb_right) + child = node->rb_left; + else + { + struct rb_node *old = node, *left; + + node = node->rb_right; + while ((left = node->rb_left) != NULL) + node = left; + child = node->rb_right; + parent = node->rb_parent; + color = node->rb_color; + + if (child) + child->rb_parent = parent; + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + if (node->rb_parent == old) + parent = node; + node->rb_parent = old->rb_parent; + node->rb_color = old->rb_color; + node->rb_right = old->rb_right; + node->rb_left = old->rb_left; + + if (old->rb_parent) + { + if (old->rb_parent->rb_left == old) + old->rb_parent->rb_left = node; + else + old->rb_parent->rb_right = node; + } else + root->rb_node = node; + + old->rb_left->rb_parent = node; + if (old->rb_right) + old->rb_right->rb_parent = node; + goto color; + } + + parent = node->rb_parent; + color = node->rb_color; + + if (child) + child->rb_parent = parent; + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} +// EXPORT_SYMBOL(rb_erase); + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_left) + n = n->rb_left; + return n; +} +// EXPORT_SYMBOL(rb_first); + +struct rb_node *rb_last(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_right) + n = n->rb_right; + return n; +} +// EXPORT_SYMBOL(rb_last); + +struct rb_node *rb_next(struct rb_node *node) +{ + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) + node=node->rb_left; + return node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while (node->rb_parent && node == node->rb_parent->rb_right) + node = node->rb_parent; + + return node->rb_parent; +} +// EXPORT_SYMBOL(rb_next); + +struct rb_node *rb_prev(struct rb_node *node) +{ + /* If we have a left-hand child, go down and then right as far + as we can. */ + if (node->rb_left) { + node = node->rb_left; + while (node->rb_right) + node=node->rb_right; + return node; + } + + /* No left-hand children. Go up till we find an ancestor which + is a right-hand child of its parent */ + while (node->rb_parent && node == node->rb_parent->rb_left) + node = node->rb_parent; + + return node->rb_parent; +} +// EXPORT_SYMBOL(rb_prev); + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root) +{ + struct rb_node *parent = victim->rb_parent; + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->rb_left) + parent->rb_left = new; + else + parent->rb_right = new; + } else { + root->rb_node = new; + } + if (victim->rb_left) + victim->rb_left->rb_parent = new; + if (victim->rb_right) + victim->rb_right->rb_parent = new; + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} +// EXPORT_SYMBOL(rb_replace_node); From 964c936b040dbd24a1b83309c54af25fe34cb95a Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 7 Jan 2007 00:38:00 +0100 Subject: [PATCH 2/2] [MAJOR] replace the wait-queue linked list with an rbtree. This patch from Sin Yu makes use of an rbtree for the wait queue, which will solve the slowdown problem encountered when timeouts are heterogenous in the configuration. The next step will be to turn maintain_proxies() into a per-proxy task so that we won't have to scan them all after each poll() loop. --- Makefile | 2 +- Makefile.bsd | 2 +- include/proto/task.h | 4 +- include/types/task.h | 7 +- src/appsession.c | 4 +- src/cfgparse.c | 8 +- src/client.c | 4 +- src/haproxy.c | 9 +- src/task.c | 192 ++++++++++++++++++------------------------- 9 files changed, 99 insertions(+), 133 deletions(-) diff --git a/Makefile b/Makefile index 195e6a9fc..5e23a0593 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \ src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \ src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \ src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \ - src/session.o src/hdr_idx.o + src/session.o src/hdr_idx.o src/rbtree.o haproxy: $(OBJS) $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) diff --git a/Makefile.bsd b/Makefile.bsd index 7cde1949c..40229fab1 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -87,7 +87,7 @@ OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \ src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \ src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \ src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \ - src/session.o src/hdr_idx.o + src/session.o src/hdr_idx.o src/rbtree.o all: haproxy diff --git a/include/proto/task.h b/include/proto/task.h index 70abb8296..8bd973a51 100644 --- a/include/proto/task.h +++ b/include/proto/task.h @@ -61,8 +61,8 @@ static inline struct task *task_sleep(struct task **q, struct task *t) */ static inline struct task *task_delete(struct task *t) { - t->prev->next = t->next; - t->next->prev = t->prev; + rb_erase(&t->rb_node, t->wq); + t->wq = NULL; return t; } diff --git a/include/types/task.h b/include/types/task.h index 6b1df226c..d09efae2c 100644 --- a/include/types/task.h +++ b/include/types/task.h @@ -25,6 +25,7 @@ #include #include +#include /* values for task->state */ #define TASK_IDLE 0 @@ -32,9 +33,9 @@ /* The base for all tasks */ struct task { - struct task *next, *prev; /* chaining ... */ + struct rb_node rb_node; + struct rb_root *wq; struct task *rqnext; /* chaining in run queue ... */ - struct task *wq; /* the wait queue this task is in */ int state; /* task state : IDLE or RUNNING */ struct timeval expire; /* next expiration time for this task, use only for fast sorting */ int (*process)(struct task *t); /* the function which processes the task */ @@ -44,7 +45,7 @@ struct task { #define sizeof_task sizeof(struct task) extern void **pool_task; -extern struct task wait_queue[2]; +extern struct rb_root wait_queue[2]; extern struct task *rq; diff --git a/src/appsession.c b/src/appsession.c index a63116d9c..62b096ff4 100644 --- a/src/appsession.c +++ b/src/appsession.c @@ -113,8 +113,8 @@ int appsession_task_init(void) if (!initialized) { if ((t = pool_alloc(task)) == NULL) return -1; - t->next = t->prev = t->rqnext = NULL; - t->wq = LIST_HEAD(wait_queue[0]); + t->wq = NULL; + t->rqnext = NULL; t->state = TASK_IDLE; t->context = NULL; tv_delayfrom(&t->expire, &now, TBLCHKINT); diff --git a/src/cfgparse.c b/src/cfgparse.c index 5017d50d0..7b517d771 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2293,8 +2293,8 @@ int readcfgfile(const char *file) return -1; } - t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */ - t->wq = LIST_HEAD(wait_queue[1]); /* already assigned to the eternity queue */ + t->rqnext = NULL; + t->wq = NULL; t->state = TASK_IDLE; t->process = process_srv_queue; t->context = newsrv; @@ -2340,8 +2340,8 @@ int readcfgfile(const char *file) return -1; } - t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */ - t->wq = LIST_HEAD(wait_queue[0]); /* but already has a wait queue assigned */ + t->wq = NULL; + t->rqnext = NULL; t->state = TASK_IDLE; t->process = process_chk; t->context = newsrv; diff --git a/src/client.c b/src/client.c index f0e698d4c..a8aad8e3c 100644 --- a/src/client.c +++ b/src/client.c @@ -150,8 +150,8 @@ int event_accept(int fd) { if (p->options & PR_O_TCP_CLI_KA) setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); - t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */ - t->wq = LIST_HEAD(wait_queue[0]); /* but already has a wait queue assigned */ + t->wq = NULL; + t->rqnext = NULL; t->state = TASK_IDLE; t->process = process_session; t->context = s; diff --git a/src/haproxy.c b/src/haproxy.c index 047136422..4a3035730 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -255,12 +255,13 @@ void sig_dump_state(int sig) void dump(int sig) { - struct task *t, *tnext; + struct task *t; struct session *s; + struct rb_node *node; - tnext = ((struct task *)LIST_HEAD(wait_queue[0]))->next; - while ((t = tnext) != LIST_HEAD(wait_queue[0])) { /* we haven't looped ? */ - tnext = t->next; + for(node = rb_first(&wait_queue[0]); + node != NULL; node = rb_next(node)) { + t = rb_entry(node, struct task, rb_node); s = t->context; qfprintf(stderr,"[dump] wq: task %p, still %ld ms, " "cli=%d, srv=%d, cr=%d, cw=%d, sr=%d, sw=%d, " diff --git a/src/task.c b/src/task.c index 1f567b924..beddb27e3 100644 --- a/src/task.c +++ b/src/task.c @@ -22,112 +22,86 @@ extern int maintain_proxies(void); void **pool_task= NULL; struct task *rq = NULL; /* global run queue */ -struct task wait_queue[2] = { /* global wait queue */ - { - prev:LIST_HEAD(wait_queue[0]), /* expirable tasks */ - next:LIST_HEAD(wait_queue[0]), - }, - { - prev:LIST_HEAD(wait_queue[1]), /* non-expirable tasks */ - next:LIST_HEAD(wait_queue[1]), - }, + +struct rb_root wait_queue[2] = { + RB_ROOT, + RB_ROOT, }; -/* inserts into its assigned wait queue, where it may already be. In this case, it - * may be only moved or left where it was, depending on its timing requirements. - * is returned. - */ +static inline void __rb_insert_task_queue(struct task *newtask) +{ + struct rb_node **p = &newtask->wq->rb_node; + struct rb_node *parent = NULL; + struct task * task; + + while (*p) + { + parent = *p; + task = rb_entry(parent, struct task, rb_node); + if (tv_cmp2(&task->expire, &newtask->expire) >= 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&newtask->rb_node, parent, p); +} + +static inline void rb_insert_task_queue(struct task *newtask) +{ + __rb_insert_task_queue(newtask); + rb_insert_color(&newtask->rb_node, newtask->wq); +} + + struct task *task_queue(struct task *task) { - struct task *list = task->wq; - struct task *start_from; + struct rb_node *node; + struct task *next, *prev; - /* This is a very dirty hack to queue non-expirable tasks in another queue - * in order to avoid pulluting the tail of the standard queue. This will go - * away with the new O(log(n)) scheduler anyway. - */ if (tv_iseternity(&task->expire)) { - /* if the task was queued in the standard wait queue, we must dequeue it */ - if (task->prev) { - if (task->wq == LIST_HEAD(wait_queue[1])) + if (task->wq) { + if (task->wq == &wait_queue[1]) return task; - else { + else task_delete(task); - task->prev = NULL; + } + task->wq = &wait_queue[1]; + rb_insert_task_queue(task); + return task; + } else { + if (task->wq != &wait_queue[0]) { + if (task->wq) + task_delete(task); + task->wq = &wait_queue[0]; + rb_insert_task_queue(task); + return task; + } + + // check whether task should be re insert + node = rb_prev(&task->rb_node); + if (node) { + prev = rb_entry(node, struct task, rb_node); + if (tv_cmp2(&prev->expire, &task->expire) >= 0) { + task_delete(task); + task->wq = &wait_queue[0]; + rb_insert_task_queue(task); + return task; } } - list = task->wq = LIST_HEAD(wait_queue[1]); - } else { - /* if the task was queued in the eternity queue, we must dequeue it */ - if (task->prev && (task->wq == LIST_HEAD(wait_queue[1]))) { - task_delete(task); - task->prev = NULL; - list = task->wq = LIST_HEAD(wait_queue[0]); - } - } - /* next, test if the task was already in a list */ - if (task->prev == NULL) { - // start_from = list; - start_from = list->prev; - /* insert the unlinked into the list, searching back from the last entry */ - while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) { - start_from = start_from->prev; + node = rb_next(&task->rb_node); + if (node) { + next = rb_entry(node, struct task, rb_node); + if (tv_cmp2(&task->expire, &next->expire) > 0) { + task_delete(task); + task->wq = &wait_queue[0]; + rb_insert_task_queue(task); + return task; + } } - - // while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) { - // start_from = start_from->next; - // stats_tsk_nsrch++; - // } - } - else if (task->prev == list || - tv_cmp2(&task->expire, &task->prev->expire) >= 0) { /* walk right */ - start_from = task->next; - if (start_from == list || tv_cmp2(&task->expire, &start_from->expire) <= 0) { - return task; /* it's already in the right place */ - } - - /* if the task is not at the right place, there's little chance that - * it has only shifted a bit, and it will nearly always be queued - * at the end of the list because of constant timeouts - * (observed in real case). - */ -#ifndef WE_REALLY_THINK_THAT_THIS_TASK_MAY_HAVE_SHIFTED - start_from = list->prev; /* assume we'll queue to the end of the list */ - while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) { - start_from = start_from->prev; - } -#else /* WE_REALLY_... */ - /* insert the unlinked into the list, searching after position */ - while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) { - start_from = start_from->next; - } -#endif /* WE_REALLY_... */ - - /* we need to unlink it now */ - task_delete(task); + return task; } - else { /* walk left. */ -#ifdef LEFT_TO_TOP /* not very good */ - start_from = list; - while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) { - start_from = start_from->next; - } -#else - start_from = task->prev->prev; /* valid because of the previous test above */ - while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) { - start_from = start_from->prev; - } -#endif - /* we need to unlink it now */ - task_delete(task); - } - task->prev = start_from; - task->next = start_from->next; - task->next->prev = task; - start_from->next = task; - return task; } /* @@ -136,37 +110,26 @@ struct task *task_queue(struct task *task) * - call all runnable tasks * - call maintain_proxies() to enable/disable the listeners * - return the delay till next event in ms, -1 = wait indefinitely - * Note: this part should be rewritten with the O(ln(n)) scheduler. * */ - int process_runnable_tasks() { int next_time; int time2; - struct task *t, *tnext; + struct task *t; + struct rb_node *node; - next_time = TIME_ETERNITY; /* set the timer to wait eternally first */ - - /* look for expired tasks and add them to the run queue. - */ - tnext = ((struct task *)LIST_HEAD(wait_queue[0]))->next; - while ((t = tnext) != LIST_HEAD(wait_queue[0])) { /* we haven't looped ? */ - tnext = t->next; + next_time = TIME_ETERNITY; + for (node = rb_first(&wait_queue[0]); + node != NULL; node = rb_next(node)) { + t = rb_entry(node, struct task, rb_node); if (t->state & TASK_RUNNING) continue; - if (tv_iseternity(&t->expire)) continue; - - /* wakeup expired entries. It doesn't matter if they are - * already running because of a previous event - */ if (tv_cmp_ms(&t->expire, &now) <= 0) { task_wakeup(&rq, t); - } - else { - /* first non-runnable task. Use its expiration date as an upper bound */ + } else { int temp_time = tv_remain(&now, &t->expire); if (temp_time) next_time = temp_time; @@ -177,7 +140,7 @@ int process_runnable_tasks() /* process each task in the run queue now. Each task may be deleted * since we only use the run queue's head. Note that any task can be * woken up by any other task and it will be processed immediately - * after as it will be queued on the run queue's head. + * after as it will be queued on the run queue's head ! */ while ((t = rq) != NULL) { int temp_time; @@ -186,13 +149,14 @@ int process_runnable_tasks() temp_time = t->process(t); next_time = MINTIME(temp_time, next_time); } - - /* maintain all proxies in a consistent state. This should quickly become a task */ + + /* maintain all proxies in a consistent state. This should quickly + * become a task because it becomes expensive when there are huge + * numbers of proxies. */ time2 = maintain_proxies(); return MINTIME(time2, next_time); } - /* * Local variables: * c-indent-level: 8