[asterisk-commits] rizzo: branch rizzo/astobj2 r45885 - in
/team/rizzo/astobj2: include/asterisk...
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Sun Oct 22 07:17:28 MST 2006
Author: rizzo
Date: Sun Oct 22 09:17:27 2006
New Revision: 45885
URL: http://svn.digium.com/view/asterisk?rev=45885&view=rev
Log:
implement buckets as queues with head and tail.
This is a very common usage for a container and there is
no significant gain in not doing that.
On passing, do a bunch of simplifications and name changes
to improve readability.
Modified:
team/rizzo/astobj2/include/asterisk/astobj2.h
team/rizzo/astobj2/main/astobj2.c
Modified: team/rizzo/astobj2/include/asterisk/astobj2.h
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/include/asterisk/astobj2.h?rev=45885&r1=45884&r2=45885&view=diff
==============================================================================
--- team/rizzo/astobj2/include/asterisk/astobj2.h (original)
+++ team/rizzo/astobj2/include/asterisk/astobj2.h Sun Oct 22 09:17:27 2006
@@ -325,7 +325,7 @@
* \param c The container to investigate.
* \return Zero if the container is empty.
*/
-int astobj2_container_empty(struct container *c);
+int astobj2_container_count(struct container *c);
/*
* Here we have functions to manage objects.
@@ -345,6 +345,7 @@
* \note Remember to set the key before calling this function.
*/
void *astobj2_link(struct container *c, void *newobj);
+void *astobj2_unlink(struct container *c, void *newobj);
/*! \struct Used as return value if the flag OBJ_MULTIPLE is set */
struct astobj2_list {
Modified: team/rizzo/astobj2/main/astobj2.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/main/astobj2.c?rev=45885&r1=45884&r2=45885&view=diff
==============================================================================
--- team/rizzo/astobj2/main/astobj2.c (original)
+++ team/rizzo/astobj2/main/astobj2.c Sun Oct 22 09:17:27 2006
@@ -23,8 +23,7 @@
/* TODO: Revision: ? */
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-// #include <stdio.h>
-#include <unistd.h> /* sleep */
+#include <unistd.h> /* sleep, only for debugging */
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
@@ -37,7 +36,7 @@
* The refcount is used to decide when it is time to
* invoke the destructor.
*/
-struct astobj2_priv_data {
+struct __priv_data {
ast_mutex_t lock;
int ref_counter;
astobj2_destructor_fn destructor_fn;
@@ -48,7 +47,7 @@
* followed by variable-size user data.
*/
struct astobj2 {
- struct astobj2_priv_data priv_data;
+ struct __priv_data priv_data;
void *user_data[0];
};
@@ -103,7 +102,7 @@
sleep(1);
}
- if ( current_value <= 0 ) { /* last reference, destroy the object */
+ if (current_value <= 0) { /* last reference, destroy the object */
ast_log(LOG_DEBUG, "refcount %d on object %p, we destroy the object\n", current_value, a);
if (obj->priv_data.destructor_fn != NULL)
@@ -115,7 +114,7 @@
* allocated.
*/
bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
- free( obj );
+ free(obj);
}
return ret;
@@ -149,15 +148,29 @@
/* internal callback to destroy a container. */
static void container_destruct(void *c);
+
+/* each bucket in the container is a tailq. */
+struct bucket {
+ struct bucket_list *head;
+ struct bucket_list *tail;
+};
struct container {
astobj2_hash_fn hash_fn;
astobj2_callback_fn cmp_fn;
int n_buckets;
int elements; /* number of elements in the container */
- struct astobj2_bucket_list *buckets[0]; /* variable size */
+ struct bucket buckets[0]; /* variable size */
};
+/* it is convenient to have a hash function that always returns 0.
+ * This is basically used when we want to have a container that is
+ * a simple linked list.
+ */
+static int hash_zero(const void *obj, const int flags)
+{
+ return 0;
+}
/*
* A container is just an object, after all!
*/
@@ -165,27 +178,26 @@
astobj2_container_alloc(const uint n_buckets, astobj2_hash_fn hash_fn,
astobj2_callback_fn cmp_fn)
{
+ /* XXX maybe consistency check on arguments ? */
/* compute the container size */
- size_t container_size = sizeof (struct container)
- + n_buckets * sizeof (struct astobj2_bucket_list *);
-
- /* XXX must fix the destructor */
+ size_t container_size = sizeof(struct container) + n_buckets * sizeof(struct bucket);
+
struct container *c = astobj2_obj_alloc(container_size, container_destruct);
- if ( c == NULL )
+ if (c == NULL)
return NULL;
/* init */
c->elements = 0;
c->n_buckets = n_buckets;
- c->hash_fn = hash_fn;
+ c->hash_fn = hash_fn ? hash_fn : hash_zero;
c->cmp_fn = cmp_fn;
return c;
}
/* XXX better to keep a global count of elements */
-int astobj2_container_empty(struct container *c)
+int astobj2_container_count(struct container *c)
{
return c->elements;
}
@@ -195,51 +207,62 @@
* used within a bucket.
* XXX this should be private to the container code
*/
-struct astobj2_bucket_list {
- struct astobj2_bucket_list *next; /* pointer to next bucket_list */
+struct bucket_list {
+ struct bucket_list *next; /* pointer to next bucket_list */
struct astobj2 *obj; /* pointer to internal data */
};
/*!
* link an object to a container
*/
-void *astobj2_link(struct container *c, void *newobj)
-{
- struct astobj2_bucket_list *list_pointer;
- uint i = c->hash_fn ? c->hash_fn( newobj, OBJ_POINTER ) : 0;
+void *astobj2_link(struct container *c, void *obj)
+{
+ int i;
+ /* create a new list entry */
+ struct bucket_list *p = ast_calloc(1, sizeof(struct bucket_list));
+
+ if (p == NULL) /* alloc failure, die */
+ return NULL;
+ /* apply the hash function */
+ i = c->hash_fn(obj, OBJ_POINTER);
astobj2_lock(c);
i %= c->n_buckets;
- list_pointer = ast_calloc( 1, sizeof (struct astobj2_bucket_list) );
-
- /* linking the object to head */
- if (list_pointer != NULL) {
- list_pointer->next = c->buckets[i];
- list_pointer->obj = INTERNAL_OBJ(newobj);
- c->buckets[i] = list_pointer;
- list_pointer = newobj;
- }
+
+ p->next = NULL;
+ p->obj = INTERNAL_OBJ(obj);
+ if (c->buckets[i].head == NULL)
+ c->buckets[i].head = p;
+ else
+ c->buckets[i].tail->next = p;
+ c->buckets[i].tail = p;
+ ast_atomic_fetchadd_int(&c->elements, 1);
astobj2_unlock(c);
- return list_pointer;
+ return p;
+}
+
+/*
+ * another convenience function is a callback that matches on address
+ */
+static int match_by_addr(void *obj, void *arg, int flags)
+{
+ return (obj == arg) ? CMP_MATCH | CMP_STOP : 0;
}
/*!
- * \internal
* \brief Unlink an object from the list
* and destroy the associated * astobj2_bucket_list structure.
*/
-static void obj_unlink(struct astobj2_bucket_list *prev_pointer)
-{
- struct astobj2_bucket_list *to_delete = prev_pointer->next;
-
- prev_pointer->next = to_delete->next;
- free(to_delete);
+void *astobj2_unlink(struct container *c, void *arg)
+{
+ astobj2_callback(c, OBJ_SINGLE | OBJ_UNLINK | OBJ_POINTER, match_by_addr, arg);
+ return NULL;
}
/* special callback that matches all */
static int cb_true(void *obj, void *arg, int flags)
{
- return 1;
+ return CMP_MATCH;
}
/*!
@@ -252,23 +275,30 @@
astobj2_callback_fn cb_fn, void *arg)
{
int i, last;
- int match;
struct astobj2 *ret = NULL;
if ( (flags & (OBJ_SINGLE | OBJ_DATA)) == OBJ_DATA) {
ast_log(LOG_WARNING, "multiple data return not implemented yet");
return NULL;
}
- if (cb_fn == NULL) /* if NULL, match everything */
+
+ /* override the match function if necessary */
+ if (flags & OBJ_POINTER)
+ cb_fn = match_by_addr;
+ else if (cb_fn == NULL) /* if NULL, match everything */
cb_fn = cb_true;
/*
* XXX this can be optimized.
+ * If we have a hash function and lookup by pointer,
+ * run the hash function. Otherwise, scan the whole container
+ * (this only for the time being. We need to optimize this.)
*/
- if (c->hash_fn && flags & OBJ_POINTER) /* we know hash can handle this case */
+ if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
else /* don't know, let's scan all buckets */
i = -1; /* XXX this must be fixed later. */
+ /* determine the search boundaries: i..last-1 */
if (i < 0) {
i = 0;
last = c->n_buckets;
@@ -276,37 +306,41 @@
last = i+1;
}
- astobj2_lock(c);
+ astobj2_lock(c); /* avoid modifications to the content */
for(; i < last ; i++) {
- struct astobj2_bucket_list *prev = NULL, *cur = c->buckets[i];
+ struct bucket_list *prev = NULL, *cur = c->buckets[i].head;
while (cur) {
- /* match by pointer or exec a function */
- if ( flags & OBJ_POINTER )
- match = ( cur->obj == INTERNAL_OBJ(arg) );
- else
- match = cb_fn(EXTERNAL_OBJ(cur->obj), arg, flags );
+ int match = cb_fn(EXTERNAL_OBJ(cur->obj), arg, flags );
/* we found the object, performing operations according flags */
- if (!match) {
+ if (match == 0) { /* no match, no stop, continue */
prev = cur;
cur = cur->next;
continue;
+ } else if (match == CMP_STOP) { /* we are done */
+ i = last;
+ break;
}
- if (flags & OBJ_DATA)
+ /* we know CMP_MATCH is set here */
+ if (flags & OBJ_DATA) /* must return the object, mark the value */
ret = cur->obj;
- if (flags & OBJ_UNLINK) {
- struct astobj2_bucket_list *x = cur;
-
- if (prev == NULL)
- c->buckets[i] = cur->next;
- else
+ if (flags & OBJ_UNLINK) { /* must unlink */
+ struct bucket_list *x = cur;
+
+ if (prev == NULL) /* gone from head */
+ c->buckets[i].head = cur->next;
+ else /* gone from the middle */
prev->next = cur->next;
+ /* update tail if needed needed */
+ if (c->buckets[i].tail == cur)
+ c->buckets[i].tail = prev;
cur = cur->next ;
free(x);
+ ast_atomic_fetchadd_int(&c->elements, -1);
}
- if (flags & OBJ_SINGLE) {
+ if ((match & CMP_STOP) || (flags & OBJ_SINGLE)) {
/* We found the only match we need */
i = last; /* force exit from outer loop */
break;
@@ -368,7 +402,7 @@
void * astobj2_iterator_next(struct astobj2_iterator *a)
{
int lim;
- struct astobj2_bucket_list **bp;
+ struct bucket *bp;
if (a->cur)
astobj2_ref(EXTERNAL_OBJ(a->cur), -1);
@@ -380,14 +414,14 @@
astobj2_lock(a->c);
lim = a->c->n_buckets;
bp = a->c->buckets;
-
+
/* we browse the buckets array, moving to the next
* buckets if we don't find the entry in the current one.
*/
for ( ; a->bucket < lim;
/* move to next bucket */
a->bucket++, a->pos = -1) {
- struct astobj2_bucket_list *p = bp[a->bucket], *cand = NULL;
+ struct bucket_list *p = bp[a->bucket].head, *cand = NULL;
int i;
if (p == NULL) /* empty bucket */
More information about the asterisk-commits
mailing list