[asterisk-commits] rizzo: branch rizzo/astobj2 r47324 - in
/team/rizzo/astobj2: include/asterisk...
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Wed Nov 8 09:11:16 MST 2006
Author: rizzo
Date: Wed Nov 8 10:11:15 2006
New Revision: 47324
URL: http://svn.digium.com/view/asterisk?rev=47324&view=rev
Log:
optimize iterators for the common case where the
container does not change among iterations.
Now this is a O(1) operation, equivalent to
following a pointer.
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=47324&r1=47323&r2=47324&view=diff
==============================================================================
--- team/rizzo/astobj2/include/asterisk/astobj2.h (original)
+++ team/rizzo/astobj2/include/asterisk/astobj2.h Wed Nov 8 10:11:15 2006
@@ -238,7 +238,7 @@
ao2_iterator i;
void *o;
- ao2_iterator_init(c, &i);
+ i = ao2_iterator_init(c, flags);
while ( (o = ao2_iterator_next(&i)) ) {
... do something on o ...
@@ -424,13 +424,12 @@
When we need to walk through a container, we use
ao2_iterator to keep track of the current position.
-Because the navigation is typically done without locking
-the container, objects can be inserted or deleted or moved
+Because the navigation is typically done without holding the
+lock on the container across the loop,
+objects can be inserted or deleted or moved
while we work. As a consequence, there is no guarantee that
the we manage to touch all the elements on the list, or it
is possible that we touch the same object multiple times.
-We do implement a few things to reduce the chance of the
-above happening.
An iterator must be first initialized with ao2_iterator_init(),
then we can use o = ao2_iterator_next() to move from one
@@ -448,7 +447,7 @@
ao2_iterator i;
struct my_obj *o;
- ao2_iterator_init(c, &i);
+ i = ao2_iterator_init(c, flags);
while ( (o = ao2_iterator_next(&i)) ) {
... do something on o ...
@@ -464,20 +463,36 @@
* We would like the iterator to be opaque, unfortunately
* its size needs to be known if we want to store it around
* without too much trouble.
- * It contains a pointer to the container, the bucket where the current
- * object is, and the version number of the current object. How this is
- * used, you will find in the implementation of ao2_iterator_next()
+ * Anyways...
+ * The iterator has a pointer to the container, and a flags
+ * field specifying various things e.g. whether the container
+ * should be locked or not while navigating on it.
+ * The iterator "points" to the current object, which is identified
+ * by three values:
+ * - a bucket number;
+ * - the object_id, which is also the container version number
+ * when the object was inserted. This identifies the object
+ * univoquely, however reaching the desired object requires
+ * scanning a list.
+ * - a pointer, and a container version when we saved the pointer.
+ * If the container has not changed its version number, then we
+ * can safely follow the pointer to reach the object in constant time.
+ * Details are in the implementation of ao2_iterator_next()
* A freshly-initialized iterator has bucket=0, version = 0.
*/
struct __ao2_iterator {
ao2_container *c; /* the container */
+ int flags; /* operation flags */
+#define F_AO2I_DONTLOCK 1 /* don't lock when iterating */
int bucket; /* current bucket */
+ uint c_version; /* container version */
+ void *obj; /* pointer to the current object */
uint version; /* container version when the object was created */
};
typedef struct __ao2_iterator ao2_iterator;
-void ao2_iterator_init(ao2_container *c, ao2_iterator *i);
+ao2_iterator ao2_iterator_init(ao2_container *c, int flags);
void *ao2_iterator_next(ao2_iterator *a);
Modified: team/rizzo/astobj2/main/astobj2.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/main/astobj2.c?rev=47324&r1=47323&r2=47324&view=diff
==============================================================================
--- team/rizzo/astobj2/main/astobj2.c (original)
+++ team/rizzo/astobj2/main/astobj2.c Wed Nov 8 10:11:15 2006
@@ -415,11 +415,16 @@
/*!
* initialize an iterator so we start from the first object
*/
-void ao2_iterator_init(ao2_container *c, ao2_iterator *a)
-{
- a->c = c;
- a->bucket = 0;
- a->version = 0;
+ao2_iterator ao2_iterator_init(ao2_container *c, int flags)
+{
+ ao2_iterator a;
+ a.c = c;
+ a.flags = flags;
+ a.c_version = 0;
+ a.bucket = 0;
+ a.version = 0;
+ a.obj = NULL;
+ return a;
}
/*
@@ -430,7 +435,22 @@
int lim;
struct bucket_list *p = NULL;
- ao2_lock(a->c);
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_lock(a->c);
+ /* optimization. If the container is unchanged and
+ * we have a pointer, try follow it
+ */
+ if (a->c->version == a->c_version && (p = a->obj) ) {
+ ast_verbose("optimized lookup for %u\n",
+ a->version);
+ if ( (p = p->next) )
+ goto found;
+ /* nope, start from the next bucket */
+ a->bucket++;
+ a->version = 0;
+ a->obj = NULL;
+ }
+
lim = a->c->n_buckets;
/* Browse the buckets array, moving to the next
@@ -442,16 +462,20 @@
for (; a->bucket < lim; a->bucket++, a->version = 0) {
/* scan the current bucket */
for (p = a->c->buckets[a->bucket].head; p; p = p->next) {
- if (p->version > a->version) { /* found */
- a->version = p->version;
- /* inc refcount of returned object */
- ao2_ref(EXTERNAL_OBJ(p->astobj), 1);
- goto done;
- }
+ if (p->version > a->version)
+ goto found;
}
}
-done:
- ao2_unlock(a->c);
+found:
+ if (p) {
+ a->version = p->version;
+ a->obj = p;
+ a->c_version = a->c->version;
+ /* inc refcount of returned object */
+ ao2_ref(EXTERNAL_OBJ(p->astobj), 1);
+ }
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_unlock(a->c);
return p ? EXTERNAL_OBJ(p->astobj) : NULL;
}
@@ -491,7 +515,6 @@
int i, lim;
char *obj;
static int prof_id = -1;
- ao2_iterator ai;
if (prof_id == -1)
prof_id = ast_add_profile("ao2_alloc", 0);
@@ -526,17 +549,24 @@
ao2_callback(c1, 0, print_cb, (void *)fd);
ast_cli(fd, "testing iterators, remove every second object\n");
- ao2_iterator_init(c1, &ai);
-{
- int x = 0;
- while ( (obj = ao2_iterator_next(&ai)) ) {
- ast_cli(fd, "iterator on <%s>\n", obj);
- if (x++ & 1) {
- ao2_unlink(c1, obj);
+ {
+ ao2_iterator ai;
+ int x = 0;
+
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(fd, "iterator on <%s>\n", obj);
+ if (x++ & 1)
+ ao2_unlink(c1, obj);
+ ao2_ref(obj, -1);
}
- ao2_ref(obj, -1);
- }
-}
+ ast_cli(fd, "testing iterators again\n");
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(fd, "iterator on <%s>\n", obj);
+ ao2_ref(obj, -1);
+ }
+ }
ast_cli(fd, "testing callbacks again\n");
ao2_callback(c1, 0, print_cb, (void*)fd);
@@ -546,4 +576,3 @@
ao2_total_objects);
return 0;
}
-
More information about the asterisk-commits
mailing list