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

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Mon Nov 6 08:49:45 MST 2006


Author: rizzo
Date: Mon Nov  6 09:49:44 2006
New Revision: 47223

URL: http://svn.digium.com/view/asterisk?rev=47223&view=rev
Log:
improved method for iterators, and related documentation.
This works much better than the previous one.


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=47223&r1=47222&r2=47223&view=diff
==============================================================================
--- team/rizzo/astobj2/include/asterisk/astobj2.h (original)
+++ team/rizzo/astobj2/include/asterisk/astobj2.h Mon Nov  6 09:49:44 2006
@@ -329,11 +329,9 @@
  * We allocate space for a struct astobj_container, struct container
  * and the buckets[] array.
  *
- * \param astobj_hash_fn Pointer to a function computing a hash value.
- * \param astobj_cmp_fn Pointer to a function comparating key-value 
+ * \param my_hash_fn Pointer to a function computing a hash value.
+ * \param my_cmp_fn Pointer to a function comparating key-value 
  * 			with a string. (can be NULL)
- * \param astobj_dump_fn Pointer to a function dumping the objects value.
- * 			used for debug. (can be NULL)
  * \return A pointer to a struct container.
  *
  * destructor is set implicitly.
@@ -342,13 +340,7 @@
 		astobj2_hash_fn hash_fn, astobj2_callback_fn cmp_fn);
 
 /*!
- * Check if a container is empty.
- *
- * This function return 0 if the container is empty
- * otherwise return a non zero value.
- *
- * \param c The container to investigate.
- * \return Zero if the container is empty.
+ * Returns the number of elements in a container.
  */
 int astobj2_container_count(struct container *c);
 
@@ -474,18 +466,21 @@
  */
 
 /*!
+ * You are not supposed to know the internals of an iterator!
  * 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 astobj2_iterator_next()
+ * A freshly-initialized iterator has bucket=0, version = 0.
  */
 struct astobj2_iterator {
         struct container *c;    /* the container */
         int bucket;     /* current bucket */
-        int pos;                /* current position in bucket */
-        struct astobj2 *cur;    /* current object, NULL at start */
+	uint version;	/* container version when the object was created */
 };              
 
-
 void astobj2_iterator_init(struct container *c, struct astobj2_iterator *i);
 
 void *astobj2_iterator_next(struct astobj2_iterator *a);

Modified: team/rizzo/astobj2/main/astobj2.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/main/astobj2.c?rev=47223&r1=47222&r2=47223&view=diff
==============================================================================
--- team/rizzo/astobj2/main/astobj2.c (original)
+++ team/rizzo/astobj2/main/astobj2.c Mon Nov  6 09:49:44 2006
@@ -155,11 +155,27 @@
 	struct bucket_list *tail;
 };
 
+/*!
+ * A container; stores the hash and callback functions, information on
+ * the size, the hash bucket heads, and a version number, starting at 0
+ * (for a newly created, empty container)
+ * and incremented every time an object is inserted or deleted.
+ * The assumption is that an object is never moved in a container,
+ * but removed and readded with the new number.
+ * The version number is especially useful when implementing iterators.
+ * In fact, we can associate a unique, monotonically increasing number to
+ * each object, which means that, within an iterator, we can store the
+ * version number of the current object, and easily look for the next one,
+ * which is the next one in the list with a higher number.
+ * Since all objects have a version >0, we can use 0 as a marker for
+ * 'we need the first object in the bucket'.
+ */
 struct container {
         astobj2_hash_fn hash_fn;
         astobj2_callback_fn cmp_fn;
         int     n_buckets;
         int     elements;       /* number of elements in the container */
+	uint version;		/* please read above */
         struct bucket buckets[0]; /* variable size */
 };
  
@@ -190,6 +206,7 @@
 
 	/* init */
 	c->elements = 0;
+	c->version = 1;	/* 0 is a reserved value here */
 	c->n_buckets = n_buckets;
 	c->hash_fn = hash_fn ? hash_fn : hash_zero;
 	c->cmp_fn = cmp_fn;
@@ -212,6 +229,7 @@
  */
 struct bucket_list {
 	struct bucket_list *next;	/* pointer to next bucket_list */
+	uint version;
 	struct astobj2 *astobj;		/* pointer to internal data */
 }; 
 
@@ -234,6 +252,7 @@
 
 	p->next = NULL;
 	p->astobj = INTERNAL_OBJ(user_data);
+	p->version = ast_atomic_fetchadd_int(&c->version, 1);
 	if (c->buckets[i].head == NULL)
 		c->buckets[i].head = p;
 	else
@@ -262,6 +281,8 @@
 {
 	// ast_verbose("unlinking %p from container\n", user_data);
 	astobj2_callback(c, OBJ_SINGLE | OBJ_UNLINK | OBJ_POINTER, match_by_addr, user_data);
+	/* the container has changed its content, so we update the version */
+	ast_atomic_fetchadd_int(&c->version, -1);
 	astobj2_ref(user_data, -1);
 	return NULL;
 }
@@ -392,10 +413,9 @@
  */
 void astobj2_iterator_init(struct container *c, struct astobj2_iterator *a)
 {
+	a->c = c;
 	a->bucket = 0;
-	a->pos = 0;
-	a->c = c;
-	a->cur = NULL;
+	a->version = 0;
 }
 
 /*
@@ -404,67 +424,31 @@
 void * astobj2_iterator_next(struct astobj2_iterator *a)
 {
 	int lim;
-	struct bucket *bp;
-
-	if (a->cur)
-		astobj2_ref(EXTERNAL_OBJ(a->cur), -1);
-	else {	/* search from the beginning */
-		a->bucket = 0;
-		a->pos = -1;
-	}
+	struct bucket_list *p = NULL;
 
 	astobj2_lock(a->c);
 	lim = a->c->n_buckets;
-	bp = a->c->buckets;
-
-	/* we browse the buckets array, moving to the next
+
+	/* Browse the buckets array, moving to the next
 	 * buckets if we don't find the entry in the current one.
+	 * Stop when we find an element with version number greater
+	 * than the current one (we reset the version to 0 when we
+	 * switch buckets).
 	 */
-	for ( ; a->bucket < lim;
-			/* move to next bucket */
-			a->bucket++, a->pos = -1) {
-		struct bucket_list *p = bp[a->bucket].head, *cand = NULL;
-		int i;
-
-		if (p == NULL)		/* empty bucket */
-			continue;
-
-		if (a->pos == -1) {	/* found at the beginning of the bucket */
-			a->pos = 0;
-			a->cur = p->astobj;
-			break;
-		}
-
-		/* we browse the buckets list for the current object,
-		 * by address or by position.
-		 * When we find it, record it in cand.
-		 * The next element to return will be cand->next.
-		 */
-		for (i = 0; p; i++, p = p->next) {
-			if (p->astobj == a->cur) {	/* found current element */
-				a->pos = i;	/* mark new position */
-				cand = p;
-				break;
-			} else if (i == a->pos) {	/* element not found, but this is a candidate */
-				cand = p;
+	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 */
+				astobj2_ref(EXTERNAL_OBJ(p->astobj), 1);
+				goto done;
 			}
 		}
-		if (cand == NULL || cand->next == NULL)	/* not found, try next bucket */
-			continue;
-		/* ok, what we want is at cand->next */
-		a->pos++;
-		a->cur = cand->next->astobj;
-		break;
-	}
-
-	if (a->bucket == lim) {	/* not found */
-		astobj2_unlock(a->c);
-		return NULL;
-	}
-	astobj2_ref(EXTERNAL_OBJ(a->cur), 1);	/* inc refcount of returned object */
-
+	}
+done:
 	astobj2_unlock(a->c);
-	return EXTERNAL_OBJ(a->cur);
+	return p ? EXTERNAL_OBJ(p->astobj) : NULL;
 }
 
 /* callback for destroying container.
@@ -544,6 +528,7 @@
 		if (x++ & 1) {
 		    astobj2_unlink(c1, obj);
 		}
+		astobj2_ref(obj, -1);
 	}
 }
 	ast_cli(fd, "testing callbacks again\n");



More information about the asterisk-commits mailing list