[svn-commits] rizzo: branch rizzo/astobj2 r47324 - in /team/rizzo/astobj2: include/asterisk...

svn-commits at lists.digium.com svn-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 svn-commits mailing list