This is the perhaps the biggest, boldest, most daring change I've ever committed to trunk. Forgive me in advance any disruption this may cause, and please, report any problems via the bugtracker. The upside is that this can speed up large dialplans by 20 times (or more). Context, extension, and priority matching are all fairly constant-time searches. I introduce here my hashtables (hashtabs), and a regression for them. I would have used the ast_obj2 tables, but mine are resizeable, and don't need the object destruction capability. The hashtab stuff is well tested and stable. I introduce a data structure, a trie, for extension pattern matching, in which knowledge of all patterns is accumulated, and all matches can be found via a single traversal of the tree. This is per-context. The trie is formed on the first lookup attempt, and stored in the context for future lookups. Destruction routines are in place for hashtabs and the pattern match trie. You can see the contents of the pattern match trie by using the 'dialplan show' cli command when 'core set debug' has been done to put it in debug mode. The pattern tree traversal only traverses those parts of the tree that are interesting. It uses a scoreboard sort of approach to find the best match. The speed of the traversal is more a function of the length of the pattern than the number of patterns in the tree. The tree also contains the CID matching patterns. See the source code comments for details on how everything works. I believe the approach general enough that any issues that might come up involving fine points in the pattern matching algorithm, can be solved by just tweaking things. We shall see. The current pattern matcher is fairly involved, and replicating every nuance of it is difficult. If you find and report problems, I will try to resolve than as quickly as I can. The trie and hashtabs are added to the existing context and exten structs, and none of the old machinery has been removed for the sake of the multitude of functions that use them. In the future, we can (maybe) weed out the linked lists and save some space.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@89129 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.0
Steve Murphy 18 years ago
parent ec8497c58f
commit a897556f7f

@ -0,0 +1,257 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2006, Digium, Inc.
*
* Steve Murphy <murf@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef _ASTERISK_HASHTAB_H_
#define _ASTERISK_HASHTAB_H_
#define __USE_UNIX98 1 /* to get the MUTEX_RECURSIVE stuff */
/* generic (perhaps overly so) hashtable implementation */
/* notes:
A hash table is a structure that allows for an exact-match search
in O(1) (or close to that) time.
The method: given: a set of {key,val} pairs. (at a minimum).
given: a hash function, which, given a key,
will return an integer. Ideally, each key in the
set will have its own unique associated hash value.
This hash number will index into an array. "buckets"
are what the elements of this array are called. To
handle possible collisions in hash values, buckets can form a list.
The key for a value must be contained in the value, or we won't
be able to find it in the bucket list.
This implementation is pretty generic, because:
1. The value and key are expected to be in a structure
(along with other data, perhaps) and it's address is a "void *".
2. The pointer to a compare function must be passed in at the
time of creation, and is stored in the hashtable.
3. The pointer to a resize function, which returns 1 if the
hash table is to be grown. A default routine is provided
if the pointer is NULL, and uses the java hashtable metric
of a 75% load factor.
4. The pointer to a "new size" function, which returns a preferable
new size for the hash table bucket array. By default, a function
is supplied which roughly doubles the size of the array, is provided.
This size should ideally be a prime number.
5. The hashing function pointer must also be supplied. This function
must be written by the user to access the keys in the objects being
stored. Some helper functions that use a simple "mult by prime, add
the next char", sort of string hash, or a simple modulus of the hash
table size for ints, is provided; the user can use these simple
algorithms to generate a hash, or implement any other algorithms they
wish.
6. Recently updated the hash routines to use Doubly-linked lists for buckets,
and added a doubly-linked list that threads thru every bucket in the table.
The list of all buckets is on the HashTab struct. The Traversal was modified
to go thru this list instead of searching the bucket array for buckets.
This also should make it safe to remove a bucket during the traversal.
Removal and destruction routines will work faster.
*/
struct ast_hashtab_bucket
{
const void *object; /* whatever it is we are storing in this table */
struct ast_hashtab_bucket *next; /* a DLL of buckets in hash collision */
struct ast_hashtab_bucket *prev; /* a DLL of buckets in hash collision */
struct ast_hashtab_bucket *tnext; /* a DLL of all the hash buckets for traversal */
struct ast_hashtab_bucket *tprev; /* a DLL of all the hash buckets for traversal */
};
struct ast_hashtab
{
struct ast_hashtab_bucket **array;
struct ast_hashtab_bucket *tlist; /* the head of a DLList of all the hashbuckets in the table (for traversal). */
int (*compare) (const void *a, const void *b); /* a ptr to func that returns int, and take two void* ptrs, compares them,
rets -1 if a < b; rets 0 if a==b; rets 1 if a>b */
int (*newsize) (struct ast_hashtab *tab); /* a ptr to func that returns int, a new size for hash tab, based on curr_size */
int (*resize) (struct ast_hashtab *tab); /* a function to decide whether this hashtable should be resized now */
unsigned int (*hash) (const void *obj); /* a hash func ptr for this table. Given a raw ptr to an obj,
it calcs a hash.*/
int hash_tab_size; /* the size of the bucket array */
int hash_tab_elements; /* the number of objects currently stored in the table */
int largest_bucket_size; /* a stat on the health of the table */
int resize_count; /* a count of the number of times this table has been
resized */
int do_locking; /* if 1, use locks to guarantee safety of insertions/deletions */
/* this spot reserved for the proper lock storage */
ast_rwlock_t lock; /* is this as good as it sounds? */
};
struct ast_hashtab_iter /* an iterator for traversing the buckets */
{
struct ast_hashtab *tab;
struct ast_hashtab_bucket *next;
};
/* some standard, default routines for general use */
int isPrime(int num); /* this one is handy for sizing the hash table, tells if num is prime or not */
int ast_hashtab_compare_strings(const void *a, const void *b); /* assumes a and b are char * and returns 0 if they match */
int ast_hashtab_compare_strings_nocase(const void *a, const void *b); /* assumes a & b are strings, returns 0 if they match (strcasecmp) */
int ast_hashtab_compare_ints(const void *a, const void *b); /* assumes a & b are int *, returns a != b */
int ast_hashtab_compare_shorts(const void *a, const void *b); /* assumes a & b are short *, returns a != b */
int ast_hashtab_resize_java(struct ast_hashtab *tab); /* returns 1 if the table is 75% full or more */
int ast_hashtab_resize_tight(struct ast_hashtab *tab); /* not yet specified; probably will return 1 if table is 100% full */
int ast_hashtab_resize_none(struct ast_hashtab *tab); /* no resizing; always return 0 */
int ast_hashtab_newsize_java(struct ast_hashtab *tab); /* returns a prime number roughly 2x the current table size */
int ast_hashtab_newsize_tight(struct ast_hashtab *tab); /* not yet specified, probably will return 1.5x the current table size */
int ast_hashtab_newsize_none(struct ast_hashtab *tab); /* always return current size -- no resizing */
unsigned int ast_hashtab_hash_string(const void *obj); /* hashes a string to a number, mod is applied so it in the range 0 to mod-1 */
unsigned int ast_hashtab_hash_string_nocase(const void *obj); /* upcases each char before using them for a hash */
unsigned int ast_hashtab_hash_string_sax(const void *obj); /* from Josh */
unsigned int ast_hashtab_hash_int(const int num); /* right now, both these funcs are just result = num%modulus; */
unsigned int ast_hashtab_hash_short(const short num);
struct ast_hashtab * ast_hashtab_create(int initial_buckets,
int (*compare)(const void *a, const void *b), /* a func to compare two elements in the hash -- cannot be null */
int (*resize)(struct ast_hashtab *), /* a func to decide if the table needs to be resized,
a NULL ptr here will cause a default to be used */
int (*newsize)(struct ast_hashtab *tab), /* a ptr to func that returns a new size of the array.
A NULL will cause a default to be used */
unsigned int (*hash)(const void *obj), /* a func to do the hashing */
int do_locking ); /* use locks to guarantee safety of iterators/insertion/deletion */
/* this func will free the hash table and all its memory. It
doesn't touch the objects stored in it */
void ast_hashtab_destroy( struct ast_hashtab *tab, void (*objdestroyfunc)(void *obj));
/* normally, you'd insert "safely" by checking to see if the element is
already there; in this case, you must already have checked. If an element
is already in the hashtable, that matches this one, most likely this one
will be found first. */
/* will force a resize if the resize func returns 1 */
/* returns 1 on success, 0 if there's a problem */
int ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj);
/* same as the above, but h is the hash index; won't hash to find the index */
int ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, int h);
/* check to see if the element is already there; insert only if
it is not there.*/
/* will force a resize if the resize func returns 1 */
/* returns 1 on success, 0 if there's a problem, or it's already there. */
int ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj);
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
void * ast_hashtab_lookup(struct ast_hashtab *tab, const void *obj);
/* if you know the hash val for the object, then use this and avoid the recalc
of the hash (the modulus (table_size) is not applied) */
void * ast_hashtab_lookup_with_hash(struct ast_hashtab *tab, const void *obj, unsigned int hashval);
/* same as the above lookup, but sets h to the key hash value if the lookup fails -- this has the modulus
applied, and will not be useful for long term storage if the table is resizable */
void * ast_hashtab_lookup_bucket(struct ast_hashtab *tab, const void *obj, int *h);
/* returns key stats for the table */
void ast_hashtab_get_stats( struct ast_hashtab *tab, int *biggest_bucket_size, int *resize_count, int *num_objects, int *num_buckets);
/* this function returns the number of elements stored in the hashtab */
int ast_hashtab_size( struct ast_hashtab *tab);
/* this function returns the size of the bucket array in the hashtab */
int ast_hashtab_capacity( struct ast_hashtab *tab);
/* this function will return a copy of the table */
struct ast_hashtab *ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj));
/* returns an iterator */
struct ast_hashtab_iter *ast_hashtab_start_traversal(struct ast_hashtab *tab);
/* end the traversal, free the iterator, unlock if necc. */
void ast_hashtab_end_traversal(struct ast_hashtab_iter *it);
/* returns the next object in the list, advances iter one step, returns null on end of traversal */
void *ast_hashtab_next(struct ast_hashtab_iter *it);
/* looks up the object; removes the corresponding bucket */
void *ast_hashtab_remove_object_via_lookup(struct ast_hashtab *tab, void *obj);
/* looks up the object by hash and then comparing pts in bucket list instead of
calling the compare routine; removes the bucket */
void *ast_hashtab_remove_this_object(struct ast_hashtab *tab, void *obj);
/* ------------------ */
/* for lock-enabled traversals with ability to remove an object during the traversal*/
/* ------------------ */
/* returns an iterator */
struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *tab);
/* looks up the object; removes the corresponding bucket */
void *ast_hashtab_remove_object_via_lookup_nolock(struct ast_hashtab *tab, void *obj);
/* looks up the object by hash and then comparing pts in bucket list instead of
calling the compare routine; removes the bucket */
void *ast_hashtab_remove_this_object_nolock(struct ast_hashtab *tab, void *obj);
/* ------------------ */
/* ------------------ */
/* user-controlled hashtab locking. Create a hashtab without locking, then call the
following locking routines yourself to lock the table between threads. */
void ast_hashtab_initlock(struct ast_hashtab *tab); /* call this after you create the table to init the lock */
void ast_hashtab_wrlock(struct ast_hashtab *tab); /* request a write-lock on the table. */
void ast_hashtab_rdlock(struct ast_hashtab *tab); /* request a read-lock on the table-- don't change anything! */
void ast_hashtab_unlock(struct ast_hashtab *tab); /* release a read- or write- lock. */
void ast_hashtab_destroylock(struct ast_hashtab *tab); /* call this before you destroy the table. */
#endif

@ -27,7 +27,7 @@ OBJS= io.o sched.o logger.o frame.o loader.o config.o channel.o \
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
astobj2.o
astobj2.o hashtab.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static

@ -220,7 +220,7 @@ struct ast_category_template_instance {
struct ast_category {
char name[80];
int ignored; /*!< do not let user of the config see this category */
int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
int include_level;
char *file; /*!< the file name from whence this declaration was read */
int lineno;

@ -0,0 +1,835 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Steve Murphy <murf@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief code to implement generic hash tables
*
* \author Steve Murphy <murf@digium.com>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision")
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include "asterisk/lock.h"
#include "asterisk/frame.h"
#include "asterisk/logger.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/cli.h"
#include "asterisk/term.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
#include "asterisk/linkedlists.h"
#include "asterisk/hashtab.h"
static void ast_hashtab_resize( struct ast_hashtab *tab);
/* some standard, default routines for general use */
int ast_hashtab_compare_strings(const void *a, const void *b)
{
return strcmp((char*)a,(char*)b);
}
int ast_hashtab_compare_strings_nocase(const void *a, const void *b)
{
return strcasecmp((const char*)a,(const char*)b);
}
int ast_hashtab_compare_ints(const void *a, const void *b)
{
int ai = *((int*)a);
int bi = *((int*)b);
if (ai < bi)
return -1;
else if (ai==bi)
return 0;
else
return 1;
}
int ast_hashtab_compare_shorts(const void *a, const void *b)
{
short as = *((short*)a);
short bs = *((short*)b);
if (as < bs)
return -1;
else if (as==bs)
return 0;
else
return 1;
}
int ast_hashtab_resize_java(struct ast_hashtab *tab)
{
double loadfactor = (double)tab->hash_tab_elements / (double)tab->hash_tab_size;
if (loadfactor > 0.75)
return 1;
return 0;
}
int ast_hashtab_resize_tight(struct ast_hashtab *tab)
{
if (tab->hash_tab_elements > tab->hash_tab_size) /* this is quicker than division */
return 1;
return 0;
}
int ast_hashtab_resize_none(struct ast_hashtab *tab) /* always return 0 -- no resizing */
{
return 0;
}
int isPrime(int num)
{
int tnum,limit;
if ((num & 0x1) == 0) /* even number -- not prime */
return 0;
/* Loop through ODD numbers starting with 3 */
tnum = 3;
limit = num;
while (tnum < limit)
{
if ((num%tnum) == 0) {
return 0;
}
/* really, we only need to check sqrt(num) numbers */
limit = num / tnum;
/* we only check odd numbers */
tnum = tnum+2;
}
/* if we made it thru the loop, the number is a prime */
return 1;
}
int ast_hashtab_newsize_java(struct ast_hashtab *tab)
{
int i = (tab->hash_tab_size<<1); /* multiply by two */
while (!isPrime(i))
i++;
return i;
}
int ast_hashtab_newsize_tight(struct ast_hashtab *tab)
{
int x = (tab->hash_tab_size<<1);
int i = (tab->hash_tab_size+x);
while (!isPrime(i))
i++;
return i;
}
int ast_hashtab_newsize_none(struct ast_hashtab *tab) /* always return current size -- no resizing */
{
return tab->hash_tab_size;
}
unsigned int ast_hashtab_hash_string(const void *obj)
{
unsigned char *str = (unsigned char*)obj;
unsigned int total;
for (total=0; *str; str++)
{
unsigned int tmp = total;
total <<= 1; /* multiply by 2 */
total += tmp; /* multiply by 3 */
total <<= 2; /* multiply by 12 */
total += tmp; /* multiply by 13 */
total += ((unsigned int)(*str));
}
return total;
}
unsigned int ast_hashtab_hash_string_sax(const void *obj) /* from Josh */
{
unsigned char *str = (unsigned char*)obj;
unsigned int total = 0, c = 0;
while ((c = *str++))
total ^= ( total << 5 ) + ( total >> 2 ) + ( total << 10) + c;
return total;
}
unsigned int ast_hashtab_hash_string_nocase(const void *obj)
{
unsigned char *str = (unsigned char*)obj;
unsigned int total;
for (total=0; *str; str++)
{
unsigned int tmp = total;
unsigned int charval = toupper(*str);
/* hopefully, the following is faster than multiplication by 7 */
/* why do I go to this bother? A good compiler will do this
anyway, if I say total *= 13 */
/* BTW, tried *= 7, and it doesn't do as well in spreading things around! */
total <<= 1; /* multiply by 2 */
total += tmp; /* multiply by 3 */
total <<= 2; /* multiply by 12 */
total += tmp; /* multiply by 13 */
total += (charval);
}
return total;
}
unsigned int ast_hashtab_hash_int(const int x)
{
return x;
}
unsigned int ast_hashtab_hash_short(const short x)
{
/* hmmmm.... modulus is best < 65535 !! */
return x;
}
struct ast_hashtab * ast_hashtab_create(int initial_buckets,
int (*compare)(const void *a, const void *b), /* a func to compare two elements in the hash -- cannot be null */
int (*resize)(struct ast_hashtab *), /* a func to decide if the table needs to be resized, a NULL ptr here will cause a default to be used */
int (*newsize)(struct ast_hashtab *tab), /* a ptr to func that returns a new size of the array. A NULL will cause a default to be used */
unsigned int (*hash)(const void *obj), /* a func to do the hashing */
int do_locking ) /* use locks to guarantee safety of iterators/insertion/deletion -- real simpleminded right now */
{
struct ast_hashtab *ht = ast_calloc(1,sizeof(struct ast_hashtab));
while (!isPrime(initial_buckets)) /* make sure this is prime */
initial_buckets++;
ht->array = ast_calloc(initial_buckets,sizeof(struct ast_hashtab_bucket*));
ht->hash_tab_size = initial_buckets;
ht->compare = compare;
ht->resize = resize;
ht->newsize = newsize;
ht->hash = hash;
ht->do_locking = do_locking;
if (do_locking)
ast_rwlock_init(&ht->lock);
if (!ht->resize)
ht->resize = ast_hashtab_resize_java;
if (!ht->newsize)
ht->newsize = ast_hashtab_newsize_java;
return ht;
}
struct ast_hashtab *ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj))
{
struct ast_hashtab *ht = ast_calloc(1,sizeof(struct ast_hashtab));
int i;
ht->array = ast_calloc(tab->hash_tab_size,sizeof(struct ast_hashtab_bucket*));
ht->hash_tab_size = tab->hash_tab_size;
ht->compare = tab->compare;
ht->resize = tab->resize;
ht->newsize = tab->newsize;
ht->hash = tab->hash;
ht->do_locking = tab->do_locking;
if (ht->do_locking)
ast_rwlock_init(&ht->lock);
/* now, dup the objects in the buckets and get them into the table */
/* the fast way is to use the existing array index, and not have to hash
the objects again */
for (i=0;i<ht->hash_tab_size;i++)
{
struct ast_hashtab_bucket *b = tab->array[i];
while( b )
{
void *newobj = (*obj_dup_func)(b->object);
if (newobj) {
ast_hashtab_insert_immediate_bucket(ht, newobj, i);
}
b = b->next;
}
}
return ht;
}
static void tlist_del_item(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
{
/* item had better be in the list! or suffer the weirdness that occurs, later! */
if (*head == item) { /* first item in the list */
*head = item->tnext;
if (item->tnext)
item->tnext->tprev = NULL;
} else {
/* short circuit stuff */
item->tprev->tnext = item->tnext;
if (item->tnext)
item->tnext->tprev = item->tprev;
}
}
static void tlist_add_head(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
{
if (*head) {
item->tnext = *head;
item->tprev = NULL;
(*head)->tprev = item;
*head = item;
} else {
/* the list is empty */
*head = item;
item->tprev = NULL;
item->tnext = NULL;
}
}
/* user-controlled hashtab locking. Create a hashtab without locking, then call the
following locking routines yourself to lock the table between threads. */
void ast_hashtab_wrlock(struct ast_hashtab *tab)
{
ast_rwlock_wrlock(&tab->lock);
}
void ast_hashtab_rdlock(struct ast_hashtab *tab)
{
ast_rwlock_rdlock(&tab->lock);
}
void ast_hashtab_initlock(struct ast_hashtab *tab)
{
ast_rwlock_init(&tab->lock);
}
void ast_hashtab_destroylock(struct ast_hashtab *tab)
{
ast_rwlock_destroy(&tab->lock);
}
void ast_hashtab_unlock(struct ast_hashtab *tab)
{
ast_rwlock_unlock(&tab->lock);
}
void ast_hashtab_destroy( struct ast_hashtab *tab, void (*objdestroyfunc)(void *obj))
{
/* this func will free the hash table and all its memory. It
doesn't touch the objects stored in it */
if (tab) {
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
if (tab->array) {
/* go thru and destroy the buckets */
struct ast_hashtab_bucket *t;
int i;
while (tab->tlist) {
t = tab->tlist;
if (t->object && objdestroyfunc) {
(*objdestroyfunc)((void*)t->object); /* I cast this because I'm not going to MOD it, I'm going to DESTROY it */
}
tlist_del_item(&(tab->tlist), tab->tlist);
free(t);
}
for (i=0;i<tab->hash_tab_size;i++) {
tab->array[i] = NULL; /* not totally necc., but best to destroy old ptrs */
}
free(tab->array);
}
if (tab->do_locking) {
ast_rwlock_unlock(&tab->lock);
ast_rwlock_destroy(&tab->lock);
}
free(tab);
}
}
int ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj)
{
/* normally, you'd insert "safely" by checking to see if the element is
already there; in this case, you must already have checked. If an element
is already in the hashtable, that matches this one, most likely this one
will be found first, but.... */
/* will force a resize if the resize func returns 1 */
/* returns 1 on success, 0 if there's a problem */
unsigned int h;
int c;
struct ast_hashtab_bucket *b;
if (!tab) {
return 0;
}
if (!obj) {
return 0;
}
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (c=0,b=tab->array[h];b;b=b->next) {
c++;
}
if (c+1 > tab->largest_bucket_size)
tab->largest_bucket_size = c+1;
b = ast_malloc(sizeof(struct ast_hashtab_bucket));
b->object = obj;
b->next = tab->array[h];
b->prev = NULL;
if (b->next)
b->next->prev = b;
tlist_add_head(&(tab->tlist),b);
tab->array[h] = b;
tab->hash_tab_elements++;
if ((*tab->resize)(tab))
ast_hashtab_resize(tab);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 1;
}
int ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, int h)
{
/* normally, you'd insert "safely" by checking to see if the element is
already there; in this case, you must already have checked. If an element
is already in the hashtable, that matches this one, most likely this one
will be found first, but.... */
/* will force a resize if the resize func returns 1 */
/* returns 1 on success, 0 if there's a problem */
int c;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
for (c=0,b=tab->array[h];b;b=b->next) {
c++;
}
if (c+1 > tab->largest_bucket_size)
tab->largest_bucket_size = c+1;
b = ast_malloc(sizeof(struct ast_hashtab_bucket));
b->object = obj;
b->next = tab->array[h];
b->prev = NULL;
tab->array[h] = b;
if (b->next)
b->next->prev = b;
tlist_add_head(&(tab->tlist), b);
tab->hash_tab_elements++;
if ((*tab->resize)(tab))
ast_hashtab_resize(tab);
return 1;
}
int ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj)
{
/* check to see if the element is already there; insert only if
it is not there. */
/* will force a resize if the resize func returns 1 */
/* returns 1 on success, 0 if there's a problem, or it's already there. */
int bucket = 0;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
if (ast_hashtab_lookup_bucket(tab,obj,&bucket) == 0)
{
int ret2 = ast_hashtab_insert_immediate_bucket(tab,obj,bucket);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return ret2;
}
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 0;
}
void * ast_hashtab_lookup(struct ast_hashtab *tab, const void *obj)
{
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
unsigned int h;
const void *ret;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next) {
if ((*tab->compare)(obj,b->object) == 0) {
ret = b->object;
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void*)ret; /* I can't touch obj in this func, but the outside world is welcome to */
}
}
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 0;
}
void * ast_hashtab_lookup_with_hash(struct ast_hashtab *tab, const void *obj, unsigned int hashval)
{
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
unsigned int h;
const void *ret;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
h = hashval % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next) {
if ((*tab->compare)(obj,b->object) == 0) {
ret = b->object;
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void*)ret; /* I can't touch obj in this func, but the outside world is welcome to */
}
}
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 0;
}
void * ast_hashtab_lookup_bucket(struct ast_hashtab *tab, const void *obj, int *bucket)
{
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next) {
if ((*tab->compare)(obj,b->object) == 0) {
return (void*)b->object; /* I can't touch obj in this func, but the outside world is welcome to */
}
}
*bucket = h;
return 0;
}
void ast_hashtab_get_stats( struct ast_hashtab *tab, int *biggest_bucket_size, int *resize_count, int *num_objects, int *num_buckets)
{
/* returns key stats for the table */
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
*biggest_bucket_size = tab->largest_bucket_size;
*resize_count = tab->resize_count;
*num_objects = tab->hash_tab_elements;
*num_buckets = tab->hash_tab_size;
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
}
/* this function returns the number of elements stored in the hashtab */
int ast_hashtab_size( struct ast_hashtab *tab)
{
return tab->hash_tab_elements;
}
/* this function returns the size of the bucket array in the hashtab */
int ast_hashtab_capacity( struct ast_hashtab *tab)
{
return tab->hash_tab_size;
}
/* the insert operation calls this, and is wrlock'd when it does. */
/* if you want to call it, you should set the wrlock yourself */
static void ast_hashtab_resize( struct ast_hashtab *tab)
{
/* this function is called either internally, when the resize func returns 1, or
externally by the user to force a resize of the hash table */
int newsize = (*tab->newsize)(tab), i, c;
unsigned int h;
struct ast_hashtab_bucket *b,*bn;
/* Since we keep a DLL of all the buckets in tlist,
all we have to do is free the array, malloc a new one,
and then go thru the tlist array and reassign them into
the bucket arrayj.
*/
for (i=0;i<tab->hash_tab_size;i++) { /* don't absolutely have to do this, but
why leave ptrs laying around */
tab->array[i] = 0; /* erase old ptrs */
}
free(tab->array);
tab->array = ast_calloc(newsize,sizeof(struct ast_hashtab_bucket *));
/* now sort the buckets into their rightful new slots */
tab->resize_count++;
tab->hash_tab_size = newsize;
tab->largest_bucket_size = 0;
for (b=tab->tlist;b;b=bn)
{
b->prev = 0;
bn = b->tnext;
h = (*tab->hash)(b->object) % tab->hash_tab_size;
b->next = tab->array[h];
if (b->next)
b->next->prev = b;
tab->array[h] = b;
}
/* recalc the largest bucket size */
for (i=0;i<tab->hash_tab_size;i++) {
c=0;
for (b=tab->array[i]; b; b=b->next) {
c++;
}
if (c > tab->largest_bucket_size)
tab->largest_bucket_size = c;
}
}
struct ast_hashtab_iter *ast_hashtab_start_traversal(struct ast_hashtab *tab)
{
/* returns an iterator */
struct ast_hashtab_iter *it = ast_malloc(sizeof(struct ast_hashtab_iter));
it->next = tab->tlist;
it->tab = tab;
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
return it;
}
/* use this function to get a write lock */
struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *tab)
{
/* returns an iterator */
struct ast_hashtab_iter *it = ast_malloc(sizeof(struct ast_hashtab_iter));
it->next = tab->tlist;
it->tab = tab;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
return it;
}
void ast_hashtab_end_traversal(struct ast_hashtab_iter *it)
{
if (it->tab->do_locking)
ast_rwlock_unlock(&it->tab->lock);
free(it);
}
void *ast_hashtab_next(struct ast_hashtab_iter *it)
{
/* returns the next object in the list, advances iter one step */
struct ast_hashtab_bucket *retval;
if (it && it->next) { /* there's a next in the bucket list */
retval = it->next;
it->next = retval->tnext;
return (void*)retval->object;
}
return NULL;
}
static void *ast_hashtab_remove_object_internal(struct ast_hashtab *tab, struct ast_hashtab_bucket *b, int h)
{
const void *obj2;
if (b->prev) {
b->prev->next = b->next;
} else {
tab->array[h] = b->next;
}
if (b->next) {
b->next->prev = b->prev;
}
tlist_del_item(&(tab->tlist), b);
obj2 = b->object;
b->object = b->next = (void*)2;
free(b); /* free up the hashbucket */
tab->hash_tab_elements--;
#ifdef DEBUG
{
int c2;
struct ast_hashtab_bucket *b2;
/* do a little checking */
for (c2 = 0, b2 = tab->tlist;b2;b2=b2->tnext) {
c2++;
}
if (c2 != tab->hash_tab_elements) {
printf("Hey! we didn't delete right! there are %d elements in the list, and we expected %d\n",
c2, tab->hash_tab_elements);
}
for (c2 = 0, b2 = tab->tlist;b2;b2=b2->tnext) {
unsigned int obj3 = (unsigned long)obj2;
unsigned int b3 = (unsigned long)b;
if (b2->object == obj2)
printf("Hey-- you've still got a bucket pointing at ht_element %x\n", obj3);
if (b2->next == b)
printf("Hey-- you've still got a bucket with next ptr pointing to deleted bucket %x\n", b3);
if (b2->prev == b)
printf("Hey-- you've still got a bucket with prev ptr pointing to deleted bucket %x\n", b3);
if (b2->tprev == b)
printf("Hey-- you've still got a bucket with tprev ptr pointing to deleted bucket %x\n", b3);
if (b2->tnext == b)
printf("Hey-- you've still got a bucket with tnext ptr pointing to deleted bucket %x\n", b3);
}
}
#endif
return (void*)obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
void *ast_hashtab_remove_object_via_lookup(struct ast_hashtab *tab, void *obj)
{
/* looks up the object; removes the corresponding bucket */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next)
{
void *obj2;
if ((*tab->compare)(obj,b->object) == 0) {
obj2 = ast_hashtab_remove_object_internal(tab,b,h);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void*)obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
}
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 0;
}
void *ast_hashtab_remove_object_via_lookup_nolock(struct ast_hashtab *tab, void *obj)
{
/* looks up the object; removes the corresponding bucket */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next)
{
void *obj2;
if ((*tab->compare)(obj,b->object) == 0) {
obj2 = ast_hashtab_remove_object_internal(tab,b,h);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void*)obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
}
return 0;
}
void *ast_hashtab_remove_this_object(struct ast_hashtab *tab, void *obj)
{
/* looks up the object by hash and then comparing pts in bucket list instead of
calling the compare routine; removes the bucket -- a slightly cheaper operation */
/* looks up the object; removes the corresponding bucket */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next)
{
const void *obj2;
if (obj == b->object) {
obj2 = ast_hashtab_remove_object_internal(tab,b,h);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void*)obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
}
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 0;
}
void *ast_hashtab_remove_this_object_nolock(struct ast_hashtab *tab, void *obj)
{
/* looks up the object by hash and then comparing pts in bucket list instead of
calling the compare routine; removes the bucket -- a slightly cheaper operation */
/* looks up the object; removes the corresponding bucket */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b=tab->array[h]; b; b=b->next)
{
const void *obj2;
if (obj == b->object) {
obj2 = ast_hashtab_remove_object_internal(tab,b,h);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void*)obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
}
return 0;
}

File diff suppressed because it is too large Load Diff

@ -123,8 +123,6 @@ static int pbx_load_module(void)
rfilename = alloca(strlen(config) + strlen(ast_config_AST_CONFIG_DIR) + 2);
sprintf(rfilename, "%s/%s", ast_config_AST_CONFIG_DIR, config);
}
ast_log(LOG_NOTICE, "AEL load process: calculated config file name '%s'.\n", rfilename);
if (access(rfilename,R_OK) != 0) {
ast_log(LOG_NOTICE, "File %s not found; AEL declining load\n", rfilename);
return AST_MODULE_LOAD_DECLINE;

@ -5409,18 +5409,18 @@ int do_pbx_load_module(void)
}
parse_tree = ael2_parse(rfilename, &errs);
ast_log(LOG_NOTICE, "AEL load process: parsed config file name '%s'.\n", rfilename);
ast_log(LOG_DEBUG, "AEL load process: parsed config file name '%s'.\n", rfilename);
ael2_semantic_check(parse_tree, &sem_err, &sem_warn, &sem_note);
if (errs == 0 && sem_err == 0) {
ast_log(LOG_NOTICE, "AEL load process: checked config file name '%s'.\n", rfilename);
ast_log(LOG_DEBUG, "AEL load process: checked config file name '%s'.\n", rfilename);
ast_compile_ael2(&local_contexts, parse_tree);
ast_log(LOG_NOTICE, "AEL load process: compiled config file name '%s'.\n", rfilename);
ast_log(LOG_DEBUG, "AEL load process: compiled config file name '%s'.\n", rfilename);
ast_merge_contexts_and_delete(&local_contexts, registrar);
ast_log(LOG_NOTICE, "AEL load process: merged config file name '%s'.\n", rfilename);
ast_log(LOG_DEBUG, "AEL load process: merged config file name '%s'.\n", rfilename);
for (con = ast_walk_contexts(NULL); con; con = ast_walk_contexts(con))
ast_context_verify_includes(con);
ast_log(LOG_NOTICE, "AEL load process: verified config file name '%s'.\n", rfilename);
ast_log(LOG_DEBUG, "AEL load process: verified config file name '%s'.\n", rfilename);
} else {
ast_log(LOG_ERROR, "Sorry, but %d syntax errors and %d semantic errors were detected. It doesn't make sense to compile.\n", errs, sem_err);
destroy_pval(parse_tree); /* free up the memory */

@ -16,7 +16,7 @@
.PHONY: clean all uninstall
# to get check_expr, add it to the ALL_UTILS list
ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted check_expr conf2ael hashtest2
ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted check_expr conf2ael hashtest2 hashtest
UTILS:=$(ALL_UTILS)
include $(ASTTOPDIR)/Makefile.rules
@ -65,7 +65,7 @@ clean:
rm -f *.s *.i
rm -f md5.c strcompat.c ast_expr2.c ast_expr2f.c pbx_ael.c pval.c
rm -f aelparse.c aelbison.c conf2ael
rm -f utils.c threadstorage.c sha1.c astobj2.c hashtest2
rm -f utils.c threadstorage.c sha1.c astobj2.c hashtest2 hashtest
md5.c: ../main/md5.c
@cp $< $@
@ -76,6 +76,9 @@ astman: LIBS+=$(NEWT_LIB)
stereorize: stereorize.o frame.o
stereorize: LIBS+=-lm
hashtab.c: ../main/hashtab.c
@cp $< $@
strcompat.c: ../main/strcompat.c
@cp $< $@
@ -100,7 +103,7 @@ ast_expr2f.o: ASTCFLAGS+=-DSTANDALONE_AEL -I../main
pval.o : ASTCFLAGS+=-DSTANDALONE
check_expr: check_expr.o ast_expr2.o ast_expr2f.o strcompat.o clicompat.o threadstorage.o
check_expr: check_expr.o ast_expr2.o ast_expr2f.o strcompat.o threadstorage.o
aelbison.c: ../res/ael/ael.tab.c
@cp $< $@
@ -135,6 +138,11 @@ hashtest2.o: ASTCFLAGS+=-O0
hashtest2: hashtest2.o md5.o utils.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o
hashtest: hashtest.o md5.o hashtab.o utils.o sha1.o strcompat.o threadstorage.o
hashtest.o : hashtest.c
$(CC) -g -O0 -c hashtest.c -I/usr/include -I../include
extconf.o : extconf.c
conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o

@ -0,0 +1,387 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Steve Murphy
*
* Steve Murphy <murf@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief A program to thoroughly thrash a hash table, testing
* out locking safety, and making sure all functionality
* is functioning. Run with 5 or more threads to get that
* fully intense firestorm of activity. If your
* hash tables don't crash, lock up, or go weird, it must
* be good code! Even features some global counters
* that will get slightly behind because they aren't lock-protected.
*
* \author Steve Murphy <murf@digium.com>
*/
#include "asterisk.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <alloca.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include "asterisk/lock.h"
#include "asterisk/hashtab.h"
#include "asterisk/channel.h"
#include "asterisk/utils.h"
#include "asterisk/module.h"
int testno = 1;
/* stuff we need to make this work with the hashtab stuff */
void ast_cli(int *fd, char *str, ...)
{
}
int64_t ast_mark(int prof_id, int x)
{
}
struct ht_element
{
char *key;
char *val;
};
static int hashtab_compare_strings_nocase(const void *a, const void *b)
{
const struct ht_element *ae = a, *be = b;
return ast_hashtab_compare_strings_nocase(ae->key, be->key);
}
static int hashtab_compare_strings(const void *a, const void *b)
{
const struct ht_element *ae = a, *be = b;
return ast_hashtab_compare_strings(ae->key, be->key);
}
static unsigned int hashtab_hash_string(const void *obj)
{
const struct ht_element *o = obj;
return ast_hashtab_hash_string((const void *)o->key);
}
static unsigned int hashtab_hash_string_nocase(const void *obj)
{
const struct ht_element *o = obj;
return ast_hashtab_hash_string_nocase((const void*)o->key);
}
/* random numbers */
my_rand(int incl_low, int incl_high, unsigned int *seedp)
{
if (incl_high == 0)
return 0;
return incl_low + (rand_r(seedp) % incl_high);
}
/* the testing routines */
static int glob_highwater = 0;
struct ast_hashtab *glob_hashtab = 0;
unsigned int glob_seed = 0;
int els_removed = 0;
int els_added = 0;
int els_lookedup = 0;
int els_found = 0;
int els_traversals = 0;
/* all the operations to perform on the hashtab */
static void add_element(void)
{
char keybuf[100];
struct ht_element *x = malloc(sizeof(struct ht_element));
sprintf(keybuf,"key%08d", glob_highwater++);
x->key = strdup(keybuf);
x->val = strdup("interesting data");
ast_hashtab_insert_immediate(glob_hashtab, x);
els_added++;
}
static void traverse_elements(void)
{
struct ht_element *el;
int c=0;
struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(glob_hashtab);
#ifdef DEBUG
printf("Traverse hashtab\n");
#endif
while ((el = ast_hashtab_next(it))) {
c++;
}
ast_hashtab_end_traversal(it);
els_traversals++; /* unprotected, sometimes off, but, not really important, either */
}
static void * del_element(unsigned int *seedp)
{
char keybuf[100];
struct ht_element *el, lookup;
int x;
/* pick a random element from 0 to highwater-1 */
x = my_rand(0,glob_highwater-1,seedp);
sprintf(keybuf, "key%08d", x);
#if DEBUG
printf("Removing %s", keybuf);
#endif
lookup.key = keybuf;
el = ast_hashtab_remove_object_via_lookup(glob_hashtab, &lookup);
if (el) {
#if DEBUG
printf("...YES (el=%x)\n", (unsigned long)el);
#endif
free(el->key);
free(el->val);
free(el);
els_removed++;
} else {
#if DEBUG
printf("...NO.\n");
#endif
return 0;
}
return el;
}
static int lookup_element(unsigned int *seedp)
{
char keybuf[100];
struct ht_element *el, lookup;
int x;
/* pick a random element from 0 to highwater-1 */
x = my_rand(0,glob_highwater-1,seedp);
sprintf(keybuf, "key%08d", x);
lookup.key = keybuf;
el = ast_hashtab_lookup(glob_hashtab, &lookup);
els_lookedup++;
if (el)
els_found++;
if (el)
return 1;
return 0;
}
static void *hashtest(void *data)
{
int my_els_removed = 0;
int my_els_added = 0;
int my_els_lookedup = 0;
int my_els_found = 0;
int my_els_traversals = 0;
int my_testno = testno++;
/* data will be a random number == use as a seed for random numbers */
unsigned long seed = (unsigned long)data;
printf("hashtest thread created... test beginning\n");
/* main test routine-- a global hashtab exists, pound it like crazy */
int its;
for(its=0;its<100000;its++)
{
void *seed2 = &seed;
int op = my_rand(0,100, seed2);
if (op<60) {
my_els_lookedup++;
#ifdef DEBUG
printf("%d[%d]: LOOKUP\n", my_testno, its);
#endif
if ((my_els_lookedup%1000)==0) {
printf(".");
fflush(stdout);
}
if (lookup_element(seed2))
my_els_found++;
} else if (op < 61) { /* make this 61 and it'll take 15 minutes to run */
#ifdef DEBUG
printf("%d[%d]: TRAVERSE\n", my_testno, its);
#endif
traverse_elements();
my_els_traversals++;
} else if (op < 80) {
#ifdef DEBUG
printf("%d[%d]: REMOVE\n", my_testno, its);
#endif
if (del_element(seed2))
my_els_removed++;
} else {
my_els_added++;
#ifdef DEBUG
printf("%d[%d]: ADD\n", my_testno, its);
#endif
add_element();
}
}
printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals);
printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
els_found, els_lookedup, els_added, els_removed,els_traversals);
pthread_exit(0);
}
void run_hashtest(int numthr)
{
pthread_t thr[numthr];
void *thrres[numthr];
int i, biggest, resize_cnt, numobjs, numbuckets;
/* init a single global hashtab, then... */
glob_hashtab = ast_hashtab_create(180000, hashtab_compare_strings_nocase, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_string_nocase, 1);
printf("starting with %d elements in the hashtable...\n", ast_hashtab_capacity(glob_hashtab));
/* set a random seed */
glob_seed = (unsigned int)time(0);
srand(glob_seed);
/* create threads, each running hashtest */
for(i=0;i<numthr;i++)
{
unsigned long z = rand();
printf("starting hashtest thread %d....\n",i+1);
if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) {
printf("Sorry, couldn't create thread #%d\n", i+1);
}
printf("hashtest thread spawned.... \n");
}
/* collect threads, each running hashtest */
for(i=0;i<numthr;i++)
{
printf("waiting for thread %d....\n", i+1);
if (pthread_join(thr[i], &thrres[i])) {
printf("Sorry, couldn't join thread #%d\n", i+1);
}
printf("hashtest thread %d done.... \n",i+1);
}
/* user has to kill/intr the process to stop the test? */
ast_hashtab_get_stats(glob_hashtab, &biggest, &resize_cnt, &numobjs, &numbuckets);
printf("Some stats: longest bucket chain: %d; number of resizes: %d; number of objects: %d; capacity: %d\n",
biggest, resize_cnt, numobjs, numbuckets);
}
int main(int argc,char **argv)
{
if (argc < 2 || argc > 2 || atoi(argv[1]) < 1)
{
printf("Usage: hashtest <number of threads>\n");
exit(1);
}
/* one arg == number of threads to create */
run_hashtest(atoi(argv[1]));
return 0;
}
struct ast_app *pbx_findapp(const char *app)
{
return (struct ast_app*)1; /* so as not to trigger an error */
}
int ast_add_profile(const char *x, uint64_t scale)
{
}
int ast_loader_register(int (*updater)(void))
{
return 1;
}
int ast_loader_unregister(int (*updater)(void))
{
return 1;
}
void ast_module_register(const struct ast_module_info *x)
{
}
void ast_module_unregister(const struct ast_module_info *x)
{
}
void ast_cli_register_multiple(void)
{
}
void ast_register_file_version(const char *file, const char *version)
{
}
void ast_unregister_file_version(const char *file)
{
}
void ast_cli_unregister_multiple(void)
{
}
void ast_context_destroy(void)
{
}
void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
{
va_list vars;
va_start(vars,fmt);
printf("LOG: lev:%d file:%s line:%d func: %s ",
level, file, line, function);
vprintf(fmt, vars);
fflush(stdout);
va_end(vars);
}
void ast_verbose(const char *fmt, ...)
{
va_list vars;
va_start(vars,fmt);
printf("VERBOSE: ");
vprintf(fmt, vars);
fflush(stdout);
va_end(vars);
}
void ast_register_thread(char *name)
{
}
void ast_unregister_thread(void *id)
{
}
Loading…
Cancel
Save