mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 07:07:04 +02:00
[MAJOR] replace ultree with ebtree in wait-queues
The ultree code has been removed in favor of a simpler and cleaner ebtree implementation. The eternity queue does not need to exist anymore, and the pool_tree64 has been removed. The ebtree node is stored in the task itself. The qlist list header is still used by the run-queue, but will be able to disappear once the run-queue uses ebtree too.
This commit is contained in:
parent
47d940485a
commit
9789f7bd68
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* bitops.h : macros and functions for bit operations.
|
||||
* (C) 2002 - Willy Tarreau - willy@ant-computing.com
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BITOPS_H__
|
||||
#define __BITOPS_H__
|
||||
|
||||
/* how many bits are needed to code the size of an int (eg: 32bits -> 5) */
|
||||
#define LONGSHIFT 5
|
||||
#define LLONGSHIFT 6
|
||||
#define LONGBITS 32
|
||||
#define LLONGBITS 64
|
||||
|
||||
/* very fast FFS function : returns the position of the lowest 1 */
|
||||
#define __ffs_fast32(___a) ({ \
|
||||
register int ___x, ___bits = 32; \
|
||||
if (___a) { \
|
||||
___x = (___a); \
|
||||
___bits--; \
|
||||
if (___x & 0x0000ffff) { ___x &= 0x0000ffff; ___bits -= 16;} \
|
||||
if (___x & 0x00ff00ff) { ___x &= 0x00ff00ff; ___bits -= 8;} \
|
||||
if (___x & 0x0f0f0f0f) { ___x &= 0x0f0f0f0f; ___bits -= 4;} \
|
||||
if (___x & 0x33333333) { ___x &= 0x33333333; ___bits -= 2;} \
|
||||
if (___x & 0x55555555) { ___x &= 0x55555555; ___bits -= 1;} \
|
||||
}\
|
||||
___bits; \
|
||||
})
|
||||
|
||||
/* very fast FLS function : returns the position of the highest 1 */
|
||||
#define __fls_fast32(___a) ({ \
|
||||
register int ___x, ___bits = 0; \
|
||||
if (___a) { \
|
||||
___x = (___a); \
|
||||
if (___x & 0xffff0000) { ___x &= 0xffff0000; ___bits += 16;} \
|
||||
if (___x & 0xff00ff00) { ___x &= 0xff00ff00; ___bits += 8;} \
|
||||
if (___x & 0xf0f0f0f0) { ___x &= 0xf0f0f0f0; ___bits += 4;} \
|
||||
if (___x & 0xcccccccc) { ___x &= 0xcccccccc; ___bits += 2;} \
|
||||
if (___x & 0xaaaaaaaa) { ___x &= 0xaaaaaaaa; ___bits += 1;} \
|
||||
} else { \
|
||||
___bits = 32; \
|
||||
} \
|
||||
___bits; \
|
||||
})
|
||||
|
||||
/* very fast FFS function working on 64 bits */
|
||||
#define __ffs_fast64(___a) ({ \
|
||||
register int ___bits = 64; \
|
||||
register unsigned long ___x = ((___a) >> 32); \
|
||||
if ((___a) & 0xffffffffUL) { \
|
||||
___x = (___a) & 0xffffffffUL; \
|
||||
___bits -= 32; \
|
||||
} \
|
||||
if (___x) { \
|
||||
___bits--; \
|
||||
if (___x & 0x0000ffff) { ___x &= 0x0000ffff; ___bits -= 16;} \
|
||||
if (___x & 0x00ff00ff) { ___x &= 0x00ff00ff; ___bits -= 8;} \
|
||||
if (___x & 0x0f0f0f0f) { ___x &= 0x0f0f0f0f; ___bits -= 4;} \
|
||||
if (___x & 0x33333333) { ___x &= 0x33333333; ___bits -= 2;} \
|
||||
if (___x & 0x55555555) { ___x &= 0x55555555; ___bits -= 1;} \
|
||||
}\
|
||||
___bits; \
|
||||
})
|
||||
|
||||
|
||||
/* very fast FLS function working on 64 bits */
|
||||
#define __fls_fast64(___a) ({ \
|
||||
register int ___bits = 0; \
|
||||
register unsigned long ___x = (___a); \
|
||||
if (((unsigned long long)(___a)) >> 32) { \
|
||||
___x = ((unsigned long long)(___a)) >> 32; \
|
||||
___bits += 32; \
|
||||
} \
|
||||
if (___x) { \
|
||||
if (___x & 0xffff0000) { ___x &= 0xffff0000; ___bits += 16;} \
|
||||
if (___x & 0xff00ff00) { ___x &= 0xff00ff00; ___bits += 8;} \
|
||||
if (___x & 0xf0f0f0f0) { ___x &= 0xf0f0f0f0; ___bits += 4;} \
|
||||
if (___x & 0xcccccccc) { ___x &= 0xcccccccc; ___bits += 2;} \
|
||||
if (___x & 0xaaaaaaaa) { ___x &= 0xaaaaaaaa; ___bits += 1;} \
|
||||
} else { \
|
||||
___bits += 32; \
|
||||
} \
|
||||
___bits; \
|
||||
})
|
||||
|
||||
static int ffs_fast32(register unsigned long a) {
|
||||
return __ffs_fast32(a);
|
||||
}
|
||||
|
||||
static int fls_fast32(unsigned long a) {
|
||||
return __fls_fast32(a);
|
||||
}
|
||||
|
||||
static int ffs_fast64(unsigned long long a) {
|
||||
return __ffs_fast64(a);
|
||||
}
|
||||
|
||||
static int fls_fast64(unsigned long long a) {
|
||||
return __fls_fast64(a);
|
||||
}
|
||||
|
||||
#endif /* __BITOPS_H__ */
|
@ -1,727 +0,0 @@
|
||||
/*
|
||||
* tree.h : tree manipulation macros and structures.
|
||||
* (C) 2002 - Willy Tarreau - willy@ant-computing.com
|
||||
*
|
||||
* 2007/05/13: adapted to mempools v2.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TREE_H__
|
||||
#define __TREE_H__
|
||||
|
||||
#include <import/bitops.h>
|
||||
#include <common/memory.h>
|
||||
|
||||
/* binary tree node : either 32 bits unsigned long int values, or
|
||||
* 64 bits in two 32 bits unsigned long int values
|
||||
*/
|
||||
struct ultree {
|
||||
unsigned long low; /* 32 bits low value of this node */
|
||||
unsigned long high; /* 32 bits high value of this node, not used in 32 bits */
|
||||
int level; /* bit level of this node */
|
||||
void *data; /* carried data */
|
||||
struct ultree *left, *right; /* children : left and right. NULL = leaf */
|
||||
struct ultree *up; /* parent node. NULL = root */
|
||||
};
|
||||
|
||||
/* binary tree node : 64 bits unsigned long long values */
|
||||
struct ulltree {
|
||||
unsigned long long value; /* 64 bits value of this node */
|
||||
int level; /* bit level of this node */
|
||||
void *data; /* carried data */
|
||||
struct ulltree *left, *right; /* children : left and right. NULL = leaf */
|
||||
struct ulltree *up; /* parent node. NULL = root */
|
||||
};
|
||||
|
||||
/* binary tree node : 64 bits in either one ull or two 32 bits unsigned long int values. This
|
||||
* is the common type for all the above trees, which should be cast into it. This makes
|
||||
* pool_free() far simpler since all types share a same pool.
|
||||
*/
|
||||
struct tree64 {
|
||||
union {
|
||||
struct {
|
||||
unsigned long low; /* 32 bits low value of this node */
|
||||
unsigned long high; /* 32 bits high value of this node */
|
||||
} ul;
|
||||
struct {
|
||||
unsigned long long value; /* 64 bits value of this node */
|
||||
} ull;
|
||||
} value;
|
||||
int level; /* bit level of this node */
|
||||
void *data; /* carried data */
|
||||
struct tree64 *left, *right; /* children : left and right. NULL = leaf */
|
||||
struct tree64 *up; /* parent node. NULL = root */
|
||||
};
|
||||
|
||||
extern struct pool_head *pool2_tree64;
|
||||
|
||||
#define ULTREE_HEAD(l) struct ultree (l) = { .left=NULL, .right=NULL, .up=NULL, .low=0, .level=LONGBITS, .data=NULL }
|
||||
#define ULTREE_INIT(l) { (l)->data = (l)->left = (l)->right = NULL; }
|
||||
#define ULTREE_INIT_ROOT(l) { (l)->left=(l)->right=(l)->up=(l)->data=NULL; (l)->low=0; (l)->level=LONGBITS; }
|
||||
|
||||
#define ULLTREE_HEAD(l) struct ulltree (l) = { .left=NULL, .right=NULL, .up=NULL, .value=0, .level=LLONGBITS, .data=NULL }
|
||||
#define ULLTREE_INIT(l) { (l)->data = (l)->left = (l)->right = NULL; }
|
||||
#define ULLTREE_INIT_ROOT(l) { (l)->left=(l)->right=(l)->up=(l)->data=NULL; (l)->value=0; (l)->level=LLONGBITS; }
|
||||
|
||||
#define UL2TREE_HEAD(l) struct ultree (l) = { .left=NULL, .right=NULL, .up=NULL, .high=0, .low=0, .level=LLONGBITS, .data=NULL }
|
||||
#define UL2TREE_INIT(l) { (l)->left = (l)->right = (l)->data = NULL; }
|
||||
#define UL2TREE_INIT_ROOT(l) { (l)->left=(l)->right=(l)->up=(l)->data=NULL; (l)->high=(l)->low=0; (l)->level=LLONGBITS; }
|
||||
|
||||
/*
|
||||
* inserts necessary nodes to reach <x> in tree starting at <root>. The node
|
||||
* is not created if it exists. It is returned.
|
||||
*/
|
||||
inline static struct ulltree *__ulltree_insert(struct ulltree *root, unsigned long long x) {
|
||||
int m;
|
||||
struct ulltree *next, *new, *node;
|
||||
struct ulltree **branch;
|
||||
int ffs;
|
||||
|
||||
next = root;
|
||||
ffs = ffs_fast64(x);
|
||||
|
||||
do {
|
||||
root = next;
|
||||
|
||||
if (x == next->value) {
|
||||
return next;
|
||||
}
|
||||
|
||||
if (x & (1ULL << (next->level - 1))) { /* right branch */
|
||||
branch = &next->right;
|
||||
next = *branch;
|
||||
} else {
|
||||
branch = &next->left;
|
||||
next = *branch;
|
||||
}
|
||||
|
||||
if (next == NULL) {
|
||||
/* we'll have to insert our node here */
|
||||
*branch = new = (struct ulltree *)pool_alloc2(pool2_tree64);
|
||||
ULLTREE_INIT(new);
|
||||
new->up = root;
|
||||
new->value = x;
|
||||
new->level = ffs;
|
||||
return new;
|
||||
}
|
||||
|
||||
/* we'll keep walking down as long as we have all bits in common */
|
||||
} while ((x & ~((1ULL << next->level) - 1)) == next->value);
|
||||
|
||||
|
||||
/* ok, now we know that we must insert between both. */
|
||||
|
||||
/* the new interconnect node */
|
||||
*branch = node = (struct ulltree *)pool_alloc2(pool2_tree64); /* was <next> */
|
||||
ULLTREE_INIT(node);
|
||||
node->up = root;
|
||||
next->up = node;
|
||||
|
||||
/* we need the common higher bits between x and next->value. */
|
||||
|
||||
/* what differences are there between x and the node here ?
|
||||
* NOTE that m is always < level(parent) because highest bit
|
||||
* of x and next-value are identical here (else they would be
|
||||
* on a different branch).
|
||||
*/
|
||||
m = fls_fast64(x ^ next->value) + 1; /* m = lowest identical bit */
|
||||
node->value = x & ~((1ULL << m) - 1); /* value of common bits */
|
||||
|
||||
if (node->value == x) { /* <x> is exactly on this node */
|
||||
/* we must set its real position (eg: 8,10 => m=1 => val=8, m=3)*/
|
||||
node->level = ffs;
|
||||
|
||||
if (next->value & (1ULL << (node->level - 1))) /* right branch */
|
||||
node->right = next;
|
||||
else
|
||||
node->left = next;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* the new leaf now */
|
||||
node->level = m; /* set the level to the lowest common bit */
|
||||
new = (struct ulltree *)pool_alloc2(pool2_tree64);
|
||||
ULLTREE_INIT(new);
|
||||
new->value = x;
|
||||
new->level = ffs;
|
||||
|
||||
if (x > next->value) {
|
||||
node->left = next;
|
||||
node->right = new;
|
||||
}
|
||||
else {
|
||||
node->left = new;
|
||||
node->right = next;
|
||||
}
|
||||
new->up = node;
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* inserts necessary nodes to reach <x> in tree starting at <root>. The node
|
||||
* is not created if it exists. It is returned.
|
||||
*/
|
||||
inline static struct ultree *__ultree_insert(struct ultree *root, unsigned long x) {
|
||||
int m;
|
||||
struct ultree *next, *new, *node;
|
||||
struct ultree **branch;
|
||||
int ffs;
|
||||
|
||||
next = root;
|
||||
ffs = ffs_fast32(x);
|
||||
|
||||
do {
|
||||
root = next;
|
||||
|
||||
if (x == next->low) {
|
||||
return next;
|
||||
}
|
||||
|
||||
if ((x >> (next->level - 1)) & 1) { /* right branch */
|
||||
branch = &next->right;
|
||||
next = *branch;
|
||||
} else {
|
||||
branch = &next->left;
|
||||
next = *branch;
|
||||
}
|
||||
|
||||
if (next == NULL) {
|
||||
/* we'll have to insert our node here */
|
||||
*branch = new = (struct ultree *)pool_alloc2(pool2_tree64);
|
||||
ULTREE_INIT(new);
|
||||
new->up = root;
|
||||
new->low = x;
|
||||
new->level = ffs;
|
||||
return new;
|
||||
}
|
||||
|
||||
/* we'll keep walking down as long as we have all bits in common */
|
||||
} while ((x & ~((1 << next->level) - 1)) == next->low);
|
||||
|
||||
/* ok, now we know that we must insert between both. */
|
||||
|
||||
/* the new interconnect node */
|
||||
*branch = node = (struct ultree *)pool_alloc2(pool2_tree64); /* was <next> */
|
||||
ULTREE_INIT(node);
|
||||
node->up = root;
|
||||
next->up = node;
|
||||
|
||||
/* we need the common higher bits between x and next->low. */
|
||||
|
||||
/* what differences are there between x and the node here ?
|
||||
* NOTE that m is always < level(parent) because highest bit
|
||||
* of x and next->low are identical here (else they would be
|
||||
* on a different branch).
|
||||
*/
|
||||
m = fls_fast32(x ^ next->low) + 1; /* m = lower identical bit */
|
||||
node->low = x & ~((1 << m) - 1); /* value of common bits */
|
||||
|
||||
if (node->low == x) { /* <x> is exactly on this node */
|
||||
/* we must set its real position (eg: 8,10 => m=1 => val=8, m=3)*/
|
||||
node->level = ffs;
|
||||
|
||||
if (next->low & (1 << (node->level - 1))) /* right branch */
|
||||
node->right = next;
|
||||
else
|
||||
node->left = next;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* the new leaf now */
|
||||
node->level = m; /* set the level to the lowest common bit */
|
||||
new = (struct ultree *)pool_alloc2(pool2_tree64);
|
||||
ULTREE_INIT(new);
|
||||
new->low = x;
|
||||
new->level = ffs;
|
||||
|
||||
if (x > next->low) {
|
||||
node->left = next;
|
||||
node->right = new;
|
||||
}
|
||||
else {
|
||||
node->left = new;
|
||||
node->right = next;
|
||||
}
|
||||
new->up = node;
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* inserts necessary nodes to reach <h:l> in tree starting at <root>. The node
|
||||
* is not created if it exists. It is returned.
|
||||
*/
|
||||
inline static struct ultree *__ul2tree_insert(struct ultree *root, unsigned long h, unsigned long l) {
|
||||
int m;
|
||||
struct ultree *next, *new, *node;
|
||||
struct ultree **branch;
|
||||
|
||||
next = root;
|
||||
|
||||
do {
|
||||
root = next;
|
||||
|
||||
if (h == next->high && l == next->low) {
|
||||
return next;
|
||||
}
|
||||
|
||||
branch = &next->left;
|
||||
if (next->level >= 33) {
|
||||
if ((h >> (next->level - 33)) & 1) { /* right branch */
|
||||
branch = &next->right;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((l >> (next->level - 1)) & 1) { /* right branch */
|
||||
branch = &next->right;
|
||||
}
|
||||
}
|
||||
next = *branch;
|
||||
|
||||
if (next == NULL) {
|
||||
/* we'll have to insert our node here */
|
||||
*branch = new =(struct ultree *)pool_alloc2(pool2_tree64);
|
||||
UL2TREE_INIT(new);
|
||||
new->up = root;
|
||||
new->high = h;
|
||||
new->low = l;
|
||||
if (l)
|
||||
new->level = __ffs_fast32(l);
|
||||
else
|
||||
new->level = __ffs_fast32(h) + 32;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/* we'll keep walking down as long as we have all bits in common */
|
||||
if (next->level >= 32) {
|
||||
if ((h & ~((1 << (next->level-32)) - 1)) != next->high)
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (h != next->high)
|
||||
break;
|
||||
if ((l & ~((1 << next->level) - 1)) != next->low)
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
/* ok, now we know that we must insert between both. */
|
||||
|
||||
/* the new interconnect node */
|
||||
*branch = node = (struct ultree *)pool_alloc2(pool2_tree64); /* was <next> */
|
||||
UL2TREE_INIT(node);
|
||||
node->up = root;
|
||||
next->up = node;
|
||||
|
||||
/* we need the common higher bits between x and next->high:low. */
|
||||
|
||||
/* what differences are there between x and the node here ?
|
||||
* NOTE that m is always < level(parent) because highest bit
|
||||
* of x and next->high:low are identical here (else they would be
|
||||
* on a different branch).
|
||||
*/
|
||||
if (h != next->high) {
|
||||
m = fls_fast32(h ^ next->high) + 1; /* m = lower identical bit */
|
||||
node->high = h & ~((1 << m) - 1); /* value of common bits */
|
||||
m += 32;
|
||||
node->low = 0;
|
||||
} else {
|
||||
node->high = h;
|
||||
m = fls_fast32(l ^ next->low) + 1; /* m = lower identical bit */
|
||||
node->low = l & ~((1 << m) - 1); /* value of common bits */
|
||||
}
|
||||
|
||||
if (node->high == h && node->low == l) { /* <h:l> is exactly on this node */
|
||||
/* we must set its real position (eg: 8,10 => m=1 => val=8, m=3)*/
|
||||
if (l) {
|
||||
node->level = ffs_fast32(l);
|
||||
if (next->low & (1 << (node->level - 1))) /* right branch */
|
||||
node->right = next;
|
||||
else
|
||||
node->left = next;
|
||||
}
|
||||
else {
|
||||
node->level = ffs_fast32(h) + 32;
|
||||
if (next->high & (1 << (node->level - 33))) /* right branch */
|
||||
node->right = next;
|
||||
else
|
||||
node->left = next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/* the new leaf now */
|
||||
node->level = m; /* set the level to the lowest common bit */
|
||||
new = (struct ultree *)pool_alloc2(pool2_tree64);
|
||||
UL2TREE_INIT(new);
|
||||
new->high = h;
|
||||
new->low = l;
|
||||
if (l)
|
||||
new->level = __ffs_fast32(l);
|
||||
else
|
||||
new->level = __ffs_fast32(h) + 32;
|
||||
|
||||
if (h > next->high || (h == next->high && l > next->low)) {
|
||||
node->left = next;
|
||||
node->right = new;
|
||||
}
|
||||
else {
|
||||
node->left = new;
|
||||
node->right = next;
|
||||
}
|
||||
new->up = node;
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* finds a value in the tree <root>. If it cannot be found, NULL is returned.
|
||||
*/
|
||||
inline static struct ultree *__ultree_find(struct ultree *root, unsigned long x) {
|
||||
do {
|
||||
if (x == root->low)
|
||||
return root;
|
||||
|
||||
if ((x >> (root->level - 1)) & 1)
|
||||
root = root->right;
|
||||
else
|
||||
root = root->left;
|
||||
|
||||
if (root == NULL)
|
||||
return NULL;
|
||||
|
||||
/* we'll keep walking down as long as we have all bits in common */
|
||||
} while ((x & ~((1 << root->level) - 1)) == root->low);
|
||||
|
||||
/* should be there, but nothing. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* finds a value in the tree <root>. If it cannot be found, NULL is returned.
|
||||
*/
|
||||
inline static struct ulltree *__ulltree_find(struct ulltree *root, unsigned long long x) {
|
||||
do {
|
||||
if (x == root->value)
|
||||
return root;
|
||||
|
||||
if ((x >> (root->level - 1)) & 1)
|
||||
root = root->right;
|
||||
else
|
||||
root = root->left;
|
||||
|
||||
if (root == NULL)
|
||||
return NULL;
|
||||
|
||||
/* we'll keep walking down as long as we have all bits in common */
|
||||
} while ((x & ~((1ULL << root->level) - 1)) == root->value);
|
||||
|
||||
/* should be there, but nothing. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* walks down the tree <__root> and assigns each of its data to <__data>.
|
||||
* <__stack> is an int array of at least N entries where N is the maximum number
|
||||
* of levels of the tree. <__slen> is an integer variable used as a stack index.
|
||||
* The instruction following the foreach statement is executed for each data,
|
||||
* after the data has been unlinked from the tree.
|
||||
* The nodes are deleted automatically, so it is illegal to manually delete a
|
||||
* node within this loop.
|
||||
*/
|
||||
#define tree64_foreach_destructive(__root, __data, __stack, __slen) \
|
||||
for (__slen = 0, __stack[0] = __root, __data = NULL; ({ \
|
||||
__label__ __left, __right, __again, __end; \
|
||||
typeof(__root) __ptr = __stack[__slen]; \
|
||||
__again: \
|
||||
__data = __ptr->data; \
|
||||
if (__data != NULL) { \
|
||||
__ptr->data = NULL; \
|
||||
goto __end; \
|
||||
} \
|
||||
else if (__ptr->left != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->left; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__left: \
|
||||
if (__ptr->right != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->right; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__right: \
|
||||
if (!__slen--) \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free2(pool2_tree64, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
/* unlink this node from its parent */ \
|
||||
__ptr->left = NULL; \
|
||||
goto __left; \
|
||||
} \
|
||||
else { \
|
||||
/* no need to unlink, the parent will also die */ \
|
||||
goto __right; \
|
||||
} \
|
||||
} \
|
||||
__end: \
|
||||
(__slen >= 0); /* nothing after loop */}); )
|
||||
|
||||
|
||||
/*
|
||||
* walks down the tree <__root> of type <__type> and assigns each of its data
|
||||
* to <__data>. <__stack> is an int array of at least N entries where N is the
|
||||
* maximum number of levels of the tree. <__slen> is an integer variable used
|
||||
* as a stack index. The instruction following the foreach statement is
|
||||
* executed for each data, after the data has been unlinked from the tree.
|
||||
*/
|
||||
#define tree_foreach_destructive(__type, __root, __data, __stack, __slen) \
|
||||
for (__slen = 0, __stack[0] = __root, __data = NULL; ({ \
|
||||
__label__ __left, __right, __again, __end; \
|
||||
typeof(__root) __ptr = __stack[__slen]; \
|
||||
__again: \
|
||||
__data = __ptr->data; \
|
||||
if (__data != NULL) { \
|
||||
__ptr->data = NULL; \
|
||||
goto __end; \
|
||||
} \
|
||||
else if (__ptr->left != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->left; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__left: \
|
||||
if (__ptr->right != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->right; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__right: \
|
||||
if (!__slen--) \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free2(pool##__type, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
/* unlink this node from its parent */ \
|
||||
__ptr->left = NULL; \
|
||||
goto __left; \
|
||||
} \
|
||||
else { \
|
||||
/* no need to unlink, the parent will also die */ \
|
||||
goto __right; \
|
||||
} \
|
||||
} \
|
||||
__end: \
|
||||
(__slen >= 0); /* nothing after loop */}); )
|
||||
|
||||
|
||||
/*
|
||||
* walks down the tree <__root> and assigns <__data> a pointer to each of its
|
||||
* data pointers. <__stack> is an int array of at least N entries where N is the
|
||||
* maximum number of levels of the tree. <__slen> is an integer variable used as
|
||||
* a stack index. The instruction following the foreach statement is executed
|
||||
* for each data.
|
||||
* The tree will walk down only when the data field is empty (NULL), so it
|
||||
* allows inner breaks, and will restart without losing items. The nodes data
|
||||
* will be set to NULL after the inner code, or when the inner code does
|
||||
* '__stack[__slen]->data = NULL';
|
||||
* The nodes are deleted automatically, so it is illegal to manually delete a
|
||||
* node within this loop.
|
||||
*/
|
||||
#define tree64_foreach(__root, __data, __stack, __slen) \
|
||||
for (__slen = 0, __stack[0] = __root, __data = NULL; ({ \
|
||||
__label__ __left, __right, __again, __end; \
|
||||
typeof(__root) __ptr = __stack[__slen]; \
|
||||
__again: \
|
||||
if (__ptr->data != NULL) { \
|
||||
__data = __ptr->data; \
|
||||
goto __end; \
|
||||
} \
|
||||
else if (__ptr->left != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->left; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__left: \
|
||||
if (__ptr->right != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->right; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__right: \
|
||||
if (!__slen--) \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free2(pool2_tree64, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
/* unlink this node from its parent */ \
|
||||
__ptr->left = NULL; \
|
||||
goto __left; \
|
||||
} \
|
||||
else { \
|
||||
/* no need to unlink, the parent will also die */ \
|
||||
goto __right; \
|
||||
} \
|
||||
} \
|
||||
__end: \
|
||||
(__slen >= 0); }); ((typeof(__root))__stack[__slen])->data = NULL)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* walks down the tree <__root> and assigns <__node> to each of its nodes.
|
||||
* <__stack> is an int array of at least N entries where N is the
|
||||
* maximum number of levels of the tree. <__slen> is an integer variable used as
|
||||
* a stack index. The instruction following the foreach statement is executed
|
||||
* for each node.
|
||||
* The tree will walk down only when the data field is empty (NULL), so it
|
||||
* allows inner breaks, and will restart without losing items. The nodes data
|
||||
* will be set to NULL after the inner code, or when the inner code does
|
||||
* '__node->data = NULL';
|
||||
* The nodes are deleted automatically, so it is illegal to manually delete a
|
||||
* node within this loop.
|
||||
*/
|
||||
#define tree64_foreach_node(__root, __node, __stack, __slen) \
|
||||
for (__slen = 0, __stack[0] = __root; ({ \
|
||||
__label__ __left, __right, __again, __end; \
|
||||
typeof(__root) __ptr = __stack[__slen]; \
|
||||
__again: \
|
||||
if (__ptr->data != NULL) { \
|
||||
__node = __ptr; \
|
||||
goto __end; \
|
||||
} \
|
||||
else if (__ptr->left != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->left; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__left: \
|
||||
if (__ptr->right != NULL) { \
|
||||
__stack[++__slen] = __ptr = __ptr->right; \
|
||||
goto __again; \
|
||||
} \
|
||||
else \
|
||||
__right: \
|
||||
if (!__slen--) \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free2(pool2_tree64, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
/* unlink this node from its parent */ \
|
||||
__ptr->left = NULL; \
|
||||
goto __left; \
|
||||
} \
|
||||
else { \
|
||||
/* no need to unlink, the parent will also die */ \
|
||||
goto __right; \
|
||||
} \
|
||||
} \
|
||||
__end: \
|
||||
(__slen >= 0); }); ((typeof(__root))__stack[__slen])->data = NULL)
|
||||
|
||||
|
||||
/*
|
||||
* removes the current node if possible, and its parent if it doesn't handle
|
||||
* data. A pointer to the parent or grandparent is returned (the parent of the
|
||||
* last one deleted in fact). This function should be compatible with any
|
||||
* tree struct because of the void types.
|
||||
* WARNING : never call it from within a tree_foreach() because this last one
|
||||
* uses a stack which will not be updated.
|
||||
*/
|
||||
|
||||
inline static void *__tree_delete_only_one(void *firstnode) {
|
||||
struct tree64 *down, **uplink;
|
||||
struct tree64 *node = firstnode;
|
||||
|
||||
/* don't kill the root or a populated link */
|
||||
if (node->data || node->up == NULL)
|
||||
return node;
|
||||
if (node->left && node->right)
|
||||
return node;
|
||||
/* since we know that at least left or right is null, we can do arithmetics on them */
|
||||
down = (void *)((long)node->left | (long)node->right);
|
||||
/* find where we are linked */
|
||||
if (node == node->up->left)
|
||||
uplink = &node->up->left;
|
||||
else
|
||||
uplink = &node->up->right;
|
||||
|
||||
*uplink = down; /* we relink the lower branch above us or simply cut it */
|
||||
if (down) {
|
||||
down->up = node->up;
|
||||
/* we know that we cannot do more because we kept one branch */
|
||||
}
|
||||
else {
|
||||
/* we'll redo this once for the node above us because there was no branch below us,
|
||||
* so maybe it doesn't need to exist for only one branch
|
||||
*/
|
||||
down = node;
|
||||
node = node->up;
|
||||
pool_free2(pool2_tree64, down);
|
||||
if (node->data || node->up == NULL)
|
||||
return node;
|
||||
/* now we're sure we were sharing this empty node with another branch, let's find it */
|
||||
down = (void *)((long)node->left | (long)node->right);
|
||||
if (node == node->up->left)
|
||||
uplink = &node->up->left;
|
||||
else
|
||||
uplink = &node->up->right;
|
||||
*uplink = down; /* we relink the lower branch above */
|
||||
down->up = node->up;
|
||||
}
|
||||
/* free the last node */
|
||||
pool_free2(pool2_tree64, node);
|
||||
return down->up;
|
||||
}
|
||||
|
||||
/*
|
||||
* removes the current node if possible, and all of its parents which do not
|
||||
* carry data. A pointer to the parent of the last one deleted is returned.
|
||||
* This function should be compatible with any tree struct because of the void
|
||||
* types.
|
||||
* WARNING : never call it from within a tree_foreach() because this last one
|
||||
* uses a stack which will not be updated.
|
||||
*/
|
||||
|
||||
inline static void *__tree_delete(void *firstnode) {
|
||||
struct tree64 *down, **uplink, *up;
|
||||
struct tree64 *node = firstnode;
|
||||
|
||||
while (1) {
|
||||
/* don't kill the root or a populated link */
|
||||
if (node->data || (up = node->up) == NULL)
|
||||
return node;
|
||||
if (node->left && node->right)
|
||||
return node;
|
||||
/* since we know that at least left or right is null, we can do arithmetics on them */
|
||||
down = (void *)((long)node->left | (long)node->right);
|
||||
/* find where we are linked */
|
||||
if (node == up->left)
|
||||
uplink = &up->left;
|
||||
else
|
||||
uplink = &up->right;
|
||||
|
||||
*uplink = down; /* we relink the lower branch above us or simply cut it */
|
||||
pool_free2(pool2_tree64, node);
|
||||
node = up;
|
||||
if (down)
|
||||
down->up = node;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __TREE_H__ */
|
@ -2,7 +2,7 @@
|
||||
include/proto/task.h
|
||||
Functions for task management.
|
||||
|
||||
Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
@ -35,12 +35,9 @@
|
||||
extern void *run_queue;
|
||||
extern struct pool_head *pool2_task;
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
/* perform minimal initializations, report 0 in case of error, 1 if OK. */
|
||||
int init_task();
|
||||
|
||||
/* needed later */
|
||||
void *tree_delete(void *node);
|
||||
|
||||
/* puts the task <t> in run queue <q>, and returns <t> */
|
||||
#define task_wakeup _task_wakeup
|
||||
struct task *_task_wakeup(struct task *t);
|
||||
@ -49,17 +46,12 @@ static inline struct task *__task_wakeup(struct task *t)
|
||||
if (t->state == TASK_RUNNING)
|
||||
return t;
|
||||
|
||||
if (t->qlist.p != NULL)
|
||||
DLIST_DEL(&t->qlist);
|
||||
if (likely(t->eb.node.leaf_p))
|
||||
eb32_delete(&t->eb);
|
||||
|
||||
DLIST_ADD(run_queue, &t->qlist);
|
||||
t->state = TASK_RUNNING;
|
||||
|
||||
if (likely(t->wq != NULL)) {
|
||||
tree_delete(t->wq);
|
||||
t->wq = NULL;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -79,7 +71,7 @@ static inline struct task *task_sleep(struct task *t)
|
||||
/*
|
||||
* unlinks the task from wherever it is queued :
|
||||
* - eternity_queue, run_queue
|
||||
* - wait queue : wq not null => remove carrier node too
|
||||
* - wait queue
|
||||
* A pointer to the task itself is returned.
|
||||
*/
|
||||
static inline struct task *task_delete(struct task *t)
|
||||
@ -89,10 +81,21 @@ static inline struct task *task_delete(struct task *t)
|
||||
t->qlist.p = NULL;
|
||||
}
|
||||
|
||||
if (t->wq) {
|
||||
tree_delete(t->wq);
|
||||
t->wq = NULL;
|
||||
}
|
||||
if (t->eb.node.leaf_p)
|
||||
eb32_delete(&t->eb);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a new task. The bare minimum is performed (queue pointers and state).
|
||||
* The task is returned.
|
||||
*/
|
||||
static inline struct task *task_init(struct task *t)
|
||||
{
|
||||
t->qlist.p = NULL;
|
||||
t->eb.node.leaf_p = NULL;
|
||||
t->state = TASK_IDLE;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
include/types/task.h
|
||||
Macros, variables and structures for task management.
|
||||
|
||||
Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
@ -25,8 +25,8 @@
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/eb32tree.h>
|
||||
#include <common/mini-clist.h>
|
||||
#include <import/tree.h>
|
||||
|
||||
/* values for task->state */
|
||||
#define TASK_IDLE 0
|
||||
@ -35,7 +35,7 @@
|
||||
/* The base for all tasks */
|
||||
struct task {
|
||||
struct list qlist; /* chaining in the same queue; bidirectionnal but not circular */
|
||||
struct ultree *wq; /* NULL if unqueued, or back ref to the carrier node in the WQ */
|
||||
struct eb32_node eb; /* ebtree node used to hold the task in the wait queue */
|
||||
int state; /* task state : IDLE or RUNNING */
|
||||
struct timeval expire; /* next expiration time for this task, use only for fast sorting */
|
||||
void (*process)(struct task *t, struct timeval *next); /* the function which processes the task */
|
||||
|
@ -91,9 +91,7 @@ int appsession_task_init(void)
|
||||
if ((appsess_refresh = pool_alloc2(pool2_task)) == NULL)
|
||||
return -1;
|
||||
|
||||
appsess_refresh->wq = NULL;
|
||||
appsess_refresh->qlist.p = NULL;
|
||||
appsess_refresh->state = TASK_IDLE;
|
||||
task_init(appsess_refresh);
|
||||
appsess_refresh->context = NULL;
|
||||
tv_ms_add(&appsess_refresh->expire, &now, TBLCHKINT);
|
||||
appsess_refresh->process = appsession_refresh;
|
||||
|
@ -850,9 +850,7 @@ int start_checks() {
|
||||
|
||||
s->check = t;
|
||||
|
||||
t->wq = NULL;
|
||||
t->qlist.p = NULL;
|
||||
t->state = TASK_IDLE;
|
||||
task_init(t);
|
||||
t->process = process_chk;
|
||||
t->context = s;
|
||||
|
||||
|
@ -155,9 +155,7 @@ int event_accept(int fd) {
|
||||
if (p->options & PR_O_TCP_NOLING)
|
||||
setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
|
||||
|
||||
t->wq = NULL;
|
||||
t->qlist.p = NULL;
|
||||
t->state = TASK_IDLE;
|
||||
task_init(t);
|
||||
t->process = process_session;
|
||||
t->context = s;
|
||||
|
||||
@ -410,8 +408,6 @@ int event_accept(int fd) {
|
||||
tv_bound(&t->expire, &s->txn.exp);
|
||||
}
|
||||
|
||||
task_queue(t);
|
||||
|
||||
if (p->mode != PR_MODE_HEALTH)
|
||||
task_wakeup(t);
|
||||
|
||||
|
@ -864,7 +864,6 @@ void deinit(void)
|
||||
pool_destroy2(pool2_buffer);
|
||||
pool_destroy2(pool2_requri);
|
||||
pool_destroy2(pool2_task);
|
||||
pool_destroy2(pool2_tree64);
|
||||
pool_destroy2(pool2_capture);
|
||||
pool_destroy2(pool2_appsess);
|
||||
pool_destroy2(pool2_pendconn);
|
||||
|
@ -444,9 +444,7 @@ int uxst_event_accept(int fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
t->wq = NULL;
|
||||
t->qlist.p = NULL;
|
||||
t->state = TASK_IDLE;
|
||||
task_init(t);
|
||||
t->process = l->handler;
|
||||
t->context = s;
|
||||
|
||||
@ -527,7 +525,6 @@ int uxst_event_accept(int fd) {
|
||||
t->expire = s->req->rex;
|
||||
}
|
||||
|
||||
task_queue(t);
|
||||
task_wakeup(t);
|
||||
|
||||
l->nbconn++; /* warning! right now, it's up to the handler to decrease this */
|
||||
|
195
src/task.c
195
src/task.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Task management functions.
|
||||
*
|
||||
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
|
||||
* Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/eb32tree.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/mini-clist.h>
|
||||
#include <common/standard.h>
|
||||
@ -20,33 +21,65 @@
|
||||
#include <proto/task.h>
|
||||
#include <types/task.h>
|
||||
|
||||
// FIXME: check 8bitops.c for faster FLS
|
||||
#include <import/bitops.h>
|
||||
#include <import/tree.h>
|
||||
struct pool_head *pool2_task;
|
||||
|
||||
static struct ultree *stack[LLONGBITS];
|
||||
|
||||
struct pool_head *pool2_task, *pool2_tree64;
|
||||
|
||||
UL2TREE_HEAD(timer_wq);
|
||||
void *eternity_queue = NULL;
|
||||
void *run_queue = NULL;
|
||||
|
||||
/* Principle of the wait queue : we have two trees ordered by time. On of them
|
||||
* contains all timers for current time-frame, and the other one for next
|
||||
* time-frame. Each time-frame is TIMER_KEY_BITS bits wide in number of
|
||||
* milliseconds, which is 49 days for 32 bits. Values are stored into and
|
||||
* retrieved from the tree using a key of TIMER_KEY_BITS bits. A pointer
|
||||
* always designates the current tree, which is the one we read from, until
|
||||
* it is exhausted and <now> has its high bit designate the new tree.
|
||||
* An improvement would consist in holding too large timers in a side tree
|
||||
* consulted only once a switch. It could also be a simple list BTW.
|
||||
*/
|
||||
#define TIMER_KEY_BITS 32
|
||||
#define TIMER_SUBSEC_BITS 10
|
||||
#define TIMER_SECOND_BITS (TIMER_KEY_BITS - TIMER_SUBSEC_BITS)
|
||||
|
||||
static struct {
|
||||
struct eb_root *curr; /* current time frame (t[0],t[1]) */
|
||||
struct eb_root t[2]; /* trees with MSB 0 and 1 */
|
||||
struct timeval first; /* first value in the tree when known */
|
||||
} timers;
|
||||
|
||||
/* returns an ordered key based on an expiration date. */
|
||||
static inline unsigned int timeval_to_key(const struct timeval *t)
|
||||
{
|
||||
unsigned int key;
|
||||
|
||||
/* We choose sec << 10 + usec / 1000 below to keep the precision at the
|
||||
* millisecond, but we might as well divide by 1024 and have a slightly
|
||||
* lower precision of 1.024 ms.
|
||||
*/
|
||||
|
||||
key = ((unsigned int)t->tv_sec << TIMER_SUBSEC_BITS) +
|
||||
((unsigned int)t->tv_usec / 1000);
|
||||
|
||||
#if TIMER_KEY_BITS != 32
|
||||
key &= (1 << TIMER_KEY_BITS) - 1;
|
||||
#endif
|
||||
return key;
|
||||
}
|
||||
|
||||
/* returns a tree number based on an expiration date. */
|
||||
static inline unsigned int timeval_to_tree(const struct timeval *t)
|
||||
{
|
||||
return (t->tv_sec >> TIMER_SECOND_BITS) & 1;
|
||||
}
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_task()
|
||||
{
|
||||
memset(&timers, 0, sizeof(timers));
|
||||
|
||||
/* note: we never queue in the past, so we start with <now> */
|
||||
timers.curr = &timers.t[timeval_to_tree(&now)];
|
||||
|
||||
pool2_task = create_pool("task", sizeof(struct task), MEM_F_SHARED);
|
||||
pool2_tree64 = create_pool("tree64", sizeof(struct tree64), MEM_F_SHARED);
|
||||
return pool2_task && pool2_tree64;
|
||||
}
|
||||
|
||||
struct ultree *ul2tree_insert(struct ultree *root, unsigned long h, unsigned long l)
|
||||
{
|
||||
return __ul2tree_insert(root, h, l);
|
||||
}
|
||||
|
||||
void *tree_delete(void *node) {
|
||||
return __tree_delete(node);
|
||||
return pool2_task != NULL;
|
||||
}
|
||||
|
||||
struct task *_task_wakeup(struct task *t)
|
||||
@ -58,84 +91,100 @@ struct task *_task_wakeup(struct task *t)
|
||||
* task_queue()
|
||||
*
|
||||
* Inserts a task into the wait queue at the position given by its expiration
|
||||
* date.
|
||||
*
|
||||
* date. Note that the task must *not* already be in the wait queue nor in the
|
||||
* run queue, otherwise unpredictable results may happen. Tasks queued with an
|
||||
* eternity expiration date are simply returned. Last, tasks must not be queued
|
||||
* further than the end of the next tree, which is between now and
|
||||
* now+1<<TIMER_KEY_BITS ms (now+49days in 32bit).
|
||||
*/
|
||||
struct task *task_queue(struct task *task)
|
||||
{
|
||||
if (unlikely(task->qlist.p != NULL)) {
|
||||
DLIST_DEL(&task->qlist);
|
||||
task->qlist.p = NULL;
|
||||
}
|
||||
struct eb_root *tmp;
|
||||
unsigned int key;
|
||||
|
||||
if (unlikely(task->wq != NULL)) {
|
||||
tree_delete(task->wq);
|
||||
task->wq = NULL;
|
||||
}
|
||||
|
||||
if (unlikely(tv_iseternity(&task->expire))) {
|
||||
task->wq = NULL;
|
||||
DLIST_ADD(eternity_queue, &task->qlist);
|
||||
if (unlikely(tv_iseternity(&task->expire)))
|
||||
return task;
|
||||
}
|
||||
|
||||
task->wq = ul2tree_insert(&timer_wq, task->expire.tv_sec, task->expire.tv_usec);
|
||||
DLIST_ADD(task->wq->data, &task->qlist);
|
||||
if (tv_islt(&task->expire, &timers.first))
|
||||
timers.first = task->expire;
|
||||
|
||||
key = timeval_to_key(&task->expire);
|
||||
tmp = &timers.t[timeval_to_tree(&task->expire)];
|
||||
eb32_insert(tmp, &task->eb);
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extract all expired timers from the wait queue, and wakes up all
|
||||
* Extract all expired timers from the timer queue, and wakes up all
|
||||
* associated tasks. Returns the date of next event (or eternity).
|
||||
*
|
||||
*/
|
||||
void wake_expired_tasks(struct timeval *next)
|
||||
{
|
||||
int slen;
|
||||
struct task *task;
|
||||
void *data;
|
||||
struct eb32_node *eb;
|
||||
unsigned int now_key;
|
||||
unsigned int now_tree;
|
||||
|
||||
#ifdef WAKE_HINT_CHECK_FIRST
|
||||
/*
|
||||
* Hint: tasks are *rarely* expired. So we can try to optimize
|
||||
* by not scanning the tree at all in most cases. However, this
|
||||
* code costs 160 more bytes which do not look much useful because
|
||||
* the performance win is not obvious.
|
||||
|
||||
now_tree = timeval_to_tree(&now);
|
||||
|
||||
/* This is a speedup: we immediately check for an expirable task in the
|
||||
* timer's index. Warning: if nothing is found, we still may have to
|
||||
* switch the trees.
|
||||
*/
|
||||
|
||||
if (likely(timer_wq.data != NULL)) {
|
||||
task = LIST_ELEM(timer_wq.data, struct task *, qlist);
|
||||
if (likely(tv_isgt(&task->expire, &now))) {
|
||||
*next = task->expire;
|
||||
return;
|
||||
}
|
||||
if (likely(tv_isgt(&timers.first, &now))) {
|
||||
*next = timers.first;
|
||||
if (timers.curr != &timers.t[now_tree])
|
||||
timers.curr = &timers.t[now_tree];
|
||||
return;
|
||||
}
|
||||
/* OK we lose. Let's scan the tree then. */
|
||||
#endif
|
||||
|
||||
tree64_foreach(&timer_wq, data, stack, slen) {
|
||||
task = LIST_ELEM(data, struct task *, qlist);
|
||||
now_key = timeval_to_key(&now);
|
||||
do {
|
||||
eb = eb32_first(timers.curr);
|
||||
while (eb) {
|
||||
struct eb32_node *next_eb;
|
||||
|
||||
if (tv_isgt(&task->expire, &now)) {
|
||||
*next = task->expire;
|
||||
return;
|
||||
}
|
||||
task = eb32_entry(eb, struct task, eb);
|
||||
if (eb->key > now_key) {
|
||||
*next = task->expire;
|
||||
timers.first = task->expire;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, all tasks linked to this node will be unlinked, as well
|
||||
* as the node itself, so we do not need to care about correct
|
||||
* unlinking.
|
||||
*/
|
||||
foreach_dlist_item(task, data, struct task *, qlist) {
|
||||
DLIST_DEL(&task->qlist);
|
||||
task->wq = NULL;
|
||||
/* detach the task from the queue */
|
||||
next_eb = eb32_next(eb);
|
||||
eb32_delete(eb);
|
||||
eb = next_eb;
|
||||
|
||||
/* and add the task to the run queue */
|
||||
DLIST_ADD(run_queue, &task->qlist);
|
||||
task->state = TASK_RUNNING;
|
||||
}
|
||||
}
|
||||
tv_eternity(next);
|
||||
return;
|
||||
|
||||
/* OK we have reached the end of the <curr> tree. It might mean
|
||||
* that we must now switch, which is indicated by the fact that
|
||||
* the current tree pointer does not match <now> anymore.
|
||||
*/
|
||||
if (timers.curr == &timers.t[now_tree]) {
|
||||
/* We cannot switch now, so we have to find the first
|
||||
* timer of the next tree.
|
||||
*/
|
||||
eb = eb32_first(&timers.t[now_tree ^ 1]);
|
||||
if (eb) {
|
||||
task = eb32_entry(eb, struct task, eb);
|
||||
*next = task->expire;
|
||||
timers.first = task->expire;
|
||||
} else {
|
||||
tv_eternity(next);
|
||||
tv_eternity(&timers.first);
|
||||
}
|
||||
return;
|
||||
}
|
||||
timers.curr = &timers.t[now_tree];
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user