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

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sat Oct 21 08:59:25 MST 2006


Author: rizzo
Date: Sat Oct 21 10:59:25 2006
New Revision: 45815

URL: http://svn.digium.com/view/asterisk?rev=45815&view=rev
Log:
astobj2 cleanup and fixes, namely:
+ remove an extra unref in the container destructor;
+ move private structure declarations to the .c file
+ cleanup documentation in the .h file
+ add some testing code which can be invoked by the "astobj2 test N"
  CLI command.
  Currently the test covers container allocation and
  destruction, object allocation and destruction,
  callbacks and iterators.


Modified:
    team/rizzo/astobj2/include/asterisk/astobj2.h
    team/rizzo/astobj2/main/asterisk.c
    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=45815&r1=45814&r2=45815&view=diff
==============================================================================
--- team/rizzo/astobj2/include/asterisk/astobj2.h (original)
+++ team/rizzo/astobj2/include/asterisk/astobj2.h Sat Oct 21 10:59:25 2006
@@ -17,15 +17,12 @@
 #ifndef _ASTERISK_ASTOBJ2_H
 #define _ASTERISK_ASTOBJ2_H
 
-#include <inttypes.h>
-#include <strings.h> 
-
 #include "asterisk/lock.h"
 
 /*! \file 
  *
  * \brief Object Model implementing objects and containers.
- *
+
 These functions implement a container for user-defined object,
 supporting locking, reference counting and callbacks.
 The internal implementation of the container is, in principle,
@@ -91,9 +88,9 @@
 is not inserted in the container). Other values mean success
 (we are not supposed to use the value as a pointer to anything).
 
-\note inserting the object in the container creates another reference
-to the object (owned by the container) so we still need to drop
-ours when we are done.
+\note inserting the object in the container grabs the reference
+to the object (which is now owned by the container) so we do not
+need to drop ours when we are done.
 
 \note While an object o is in a container, we expect that
 my_hash_fn(o) will always return the same value. The function
@@ -112,13 +109,12 @@
 called and the object will be free'd.
  */
 
-#ifndef FALSE
-#define FALSE    0
-#endif
-
-#ifndef TRUE
-#define TRUE     1
-#endif
+/*!
+ * What an astobj2 object looks like: fixed-size private data
+ * followed by variable-size user data.
+ * However you are not supposed to know.
+ */
+struct astobj2;
 
 /*!
  * Invoked just before freeing the memory for the object.
@@ -127,42 +123,22 @@
 typedef void (*astobj2_destructor_fn)(void *);
 
 /*!
- * astobj2 objects are always prepended this data structure,
- * which contains a lock, a reference counter,
- * the flags and a pointer to a destructor.
- * The refcount is used to decide when it is time to
- * invoke the destructor.
- */
-struct astobj2_priv_data {
-	ast_mutex_t	lock;
-	int		ref_counter;
-	astobj2_destructor_fn destructor_fn;
-};
-
-/*!
- * What an astobj2 object looks like: fixed-size private data
- * followed by variable-size user data.
- */
-struct astobj2 {
-	struct astobj2_priv_data priv_data;
-	void *user_data[0];
-};
-
-/*!
- * Lock an object.
+ * Allocate and initialize an object.
  * 
- * \param a A pointer to the object we want lock.
- * \return 0 on success, other values on error.
- */
-int astobj2_lock(void *a);
-
-/*!
- * Unlock an object.
- * 
- * \param a A pointer to the object we want unlock.
- * \return 0 on success, other values on error.
- */
-int astobj2_unlock(void *a);
+ * \param data_size The sizeof() of user-defined structure.
+ * \param destructor_fn The function destructor (can be NULL)
+ * \return A pointer to user data. 
+ *
+ * Allocates a struct astobj2 with sufficient space for the
+ * user-defined structure.
+ * \notes:
+ * - storage is zeroed; XXX maybe we want a flag to enable/disable this.
+ * - the refcount of the object just created is 1
+ * - the returned pointer cannot be free()'d or realloc()'ed;
+ *   rather, we just call astobj2_ref(o, -1);
+ */
+void *astobj2_obj_alloc(const size_t data_size, 
+		astobj2_destructor_fn destructor_fn);
 
 /*!
  * Reference/unreference an object.
@@ -182,22 +158,83 @@
 int astobj2_ref(void *o, int delta);
 
 /*!
- * Allocate and initialize an object.
+ * Lock an object.
  * 
- * \param data_size The sizeof() of user-defined structure.
- * \param destructor_fn The function destructor (can be NULL)
- * \return A pointer to user data. 
- *
- * Allocates a struct astobj2 with sufficient space for the
- * user-defined structure.
- * \notes:
- * - storage is zeroed; XXX maybe we want a flag to enable/disable this.
- * - the refcount of the object just created is 1
- * - the returned pointer cannot be free()'d or realloc()'ed;
- *   rather, we just call astobj2_ref(o, -1);
- */
-void *astobj2_obj_alloc(const size_t data_size, 
-		astobj2_destructor_fn destructor_fn);
+ * \param a A pointer to the object we want lock.
+ * \return 0 on success, other values on error.
+ */
+int astobj2_lock(void *a);
+
+/*!
+ * Unlock an object.
+ * 
+ * \param a A pointer to the object we want unlock.
+ * \return 0 on success, other values on error.
+ */
+int astobj2_unlock(void *a);
+
+/*!
+ *
+ * Containers
+
+containers are data structures meant to store several objects,
+and perform various operations on them.
+Internally, objects are stored in lists, hash tables or other
+data structures depending on the needs.
+
+NOTA BENE: at the moment the only container we support is the
+hash table and its degenerate form, the list.
+
+Operations on container include:
+
+    c = astobj2_container_alloc(size, cmp_fn, hash_fn)
+	allocate a container with desired size and default compare
+	and hash function
+
+    astobj2_find(c, arg, flags)
+	returns zero or more element matching a given criteria
+	(specified as arg). Flags indicate how many results we
+	want (only one or all matching entries), and whether we
+	should unlink the object from the container.
+
+    astobj2_callback(c, flags, fn, arg)
+	apply fn(obj, arg) to all objects in the container.
+	Similar to find. fn() can tell when to stop, and
+	do anything with the object including unlinking it.
+	Note that the entire operation is run with the container
+	locked, so noone else can change its content while we work on it.
+	However, we pay this with the fact that doing
+	anything blocking in the callback keeps the container
+	blocked.
+	The mechanism is very flexible because the callback function fn()
+	can do basically anything e.g. counting, deleting records, etc.
+	possibly using arg to store the results.
+   
+    iterate on a container
+	this is done with the following sequence
+
+	    struct container *c = ... // our container
+	    struct astobj2_iterator i;
+	    void *o;
+
+	    astobj2_iterator_init(c, &i);
+     
+	    while ( (o = astobj2_iterator_next(&i)) ) {
+		... do something on o ...
+	    }
+	    if (o) // early exit
+		astobj2_ref(o, -1);
+
+	The difference with the callback is that the control
+	on how to iterate is left to us.
+
+    astobj2_ref(c, -1)
+	dropping a reference to a container destroys it, very simple!
+ 
+Containers are astobj2 object themselves, and this is why their
+implementation is simple too.
+
+ */
 
 /*!
  * We can perform different operation on an object. We do this
@@ -207,14 +244,14 @@
 	OBJ_UNLINK	= 0x01,	/* unlink the object found */
 	OBJ_DATA	= 0x02,	/* on match, return the object. */
 	OBJ_SINGLE	= 0x08,	/* stop at the first match */
-	OBJ_POINTER	= 0x10	/* the pointer is an object pointer */
+	OBJ_POINTER	= 0x10	/* obj is an astobj object */
 };
 
 /*!
  * Type of a generic function to generate a hash value from an object.
  *
  */
-typedef int (*astobj2_hash_fn)(const void *obj, const int is_obj);
+typedef int (*astobj2_hash_fn)(const void *obj, const int flags);
 
 /*!
  * valid callback results:
@@ -248,22 +285,6 @@
  */
 typedef int (*astobj2_callback_fn)(void *obj, void *arg, int flags);
 
-/*! \typedef astobj_dump_fn	XXX should go away
- *
- * \brief Type of a generic function to dump external values of an objects.
- */
-typedef void (*astobj2_dump_fn)(const void *obj);
-
-/*!
- * A structure to create a linked list of entries,
- * 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 astobj2 *obj;			/* pointer to internal data */
-};
-
 /*!
  * Here start declarations of containers.
  */
@@ -271,14 +292,10 @@
 /*!
  * This structure contains the total number of buckets 
  * and variable size array of object pointers.
- */
-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 */
-};
+ * It is opaque, defined in astobj2.c, so we only need
+ * a type declaration.
+ */
+struct container;
 
 /*!
  * Allocate and initialize a container 
@@ -332,7 +349,7 @@
 /*! \struct Used as return value if the flag OBJ_MULTIPLE is set */
 struct astobj2_list {
 	struct astobj2_list *next;
-	void *obj_pointer;
+	void *obj;	/* pointer to the user portion of the object */
 };
 
 /*!
@@ -382,6 +399,7 @@
  * \note When the returned object is no longer in use, astobj2_ref() should
  * be used to free the additional reference possibly created by this function.
  */
+/* XXX order of arguments to find */
 void *astobj2_find(struct container *c, void *arg, enum search_flags flags);
 void *astobj2_callback(struct container *c,
 	enum search_flags flags,
@@ -413,10 +431,11 @@
 
     \code
 
-    struct astobj2_iterator i;;
+    struct container *c = ... // the container we want to iterate on
+    struct astobj2_iterator i;
     struct my_obj *o;
 
-    astobj2_iterator_init(&i);
+    astobj2_iterator_init(c, &i);
  
     while ( (o = astobj2_iterator_next(&i)) ) {
 	... do something on o ...
@@ -427,12 +446,19 @@
     \endcode
 
  */
+
+/*!
+ * 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.
+ */
 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 */
-};
+        struct container *c;    /* the container */
+        int bucket;     /* current bucket */
+        int pos;                /* current position in bucket */
+        struct astobj2 *cur;    /* current object, NULL at start */
+};              
+
 
 void astobj2_iterator_init(struct container *c, struct astobj2_iterator *i);
 

Modified: team/rizzo/astobj2/main/asterisk.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/main/asterisk.c?rev=45815&r1=45814&r2=45815&view=diff
==============================================================================
--- team/rizzo/astobj2/main/asterisk.c (original)
+++ team/rizzo/astobj2/main/asterisk.c Sat Oct 21 10:59:25 2006
@@ -1474,6 +1474,8 @@
 
 #define ASTERISK_PROMPT2 "%s*CLI> "
 
+extern int handle_astobj2_test(int fd, int argc, char *argv[]);
+
 static struct ast_cli_entry cli_asterisk[] = {
 	{ { "abort", "halt", NULL },
 	handle_abort_halt, "Cancel a running halt",
@@ -1533,6 +1535,10 @@
 
 	{ { "profile", "clear", NULL },
 	handle_show_profile, "Clear profiling info",
+	NULL },
+
+	{ { "astobj2", "test", NULL },
+	handle_astobj2_test, "Test astobj2",
 	NULL },
 #endif /* ! LOW_MEMORY */
 };

Modified: team/rizzo/astobj2/main/astobj2.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/astobj2/main/astobj2.c?rev=45815&r1=45814&r2=45815&view=diff
==============================================================================
--- team/rizzo/astobj2/main/astobj2.c (original)
+++ team/rizzo/astobj2/main/astobj2.c Sat Oct 21 10:59:25 2006
@@ -23,10 +23,34 @@
 /* TODO: Revision: ? */
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <stdio.h>
-#include <stdlib.h>
+// #include <stdio.h>
+#include <unistd.h>	/* sleep */
 
 #include "asterisk/astobj2.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+/*!
+ * astobj2 objects are always prepended this data structure,
+ * which contains a lock, a reference counter,
+ * the flags and a pointer to a destructor.
+ * The refcount is used to decide when it is time to
+ * invoke the destructor.
+ */
+struct astobj2_priv_data {
+        ast_mutex_t     lock;
+        int             ref_counter;
+        astobj2_destructor_fn destructor_fn;
+};
+
+/*!
+ * What an astobj2 object looks like: fixed-size private data
+ * followed by variable-size user data.
+ */
+struct astobj2 {
+        struct astobj2_priv_data priv_data;
+        void *user_data[0];
+};
 
 /*
  * From a pointer _p to a user-defined object,
@@ -71,10 +95,13 @@
 	/* we modify with an atomic operation the reference counter */
 	ret = ast_atomic_fetchadd_int( &obj->priv_data.ref_counter, delta );
 	current_value = ret + delta;
+ 	ast_log(LOG_NOTICE, "+++ refcount %d for %p\n", current_value, a);
 
 	/* this case must never happen */
-	if (current_value < 0)
+	if (current_value < 0) {
 		ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, a);
+		sleep(1);
+	}
 
 	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);
@@ -106,7 +133,7 @@
 	if (data_size < sizeof(void *))
 		data_size = sizeof(void *);
 
-	a = calloc ( 1, sizeof(struct astobj2) + data_size );
+	a = ast_calloc( 1, sizeof(struct astobj2) + data_size );
 
 	if ( a == NULL)
 		return NULL;
@@ -120,9 +147,17 @@
 	return EXTERNAL_OBJ(a);
 }
 
-/* XXX todo callback that unlinks and unrefs every element */
+/* internal callback to destroy a container. */
 static void container_destruct(void *c);
 
+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 */
+};
+ 
 /*
  * A container is just an object, after all!
  */
@@ -156,16 +191,26 @@
 }
 
 /*!
+ * A structure to create a linked list of entries,
+ * 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 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( newobj, TRUE );
+	uint i = c->hash_fn ? c->hash_fn( newobj, OBJ_POINTER ) : 0;
 
 	astobj2_lock(c);
 	i %= c->n_buckets;
-	list_pointer = calloc( 1, sizeof (struct astobj2_bucket_list) );
+	list_pointer = ast_calloc( 1, sizeof (struct astobj2_bucket_list) );
 
 	/* linking the object to head */
 	if (list_pointer != NULL) {
@@ -191,6 +236,12 @@
 	free(to_delete);
 }
 
+/* special callback that matches all */
+static int cb_true(void *obj, void *arg, int flags)
+{
+	return 1;
+}
+
 /*!
  * Browse the container using different stategies accoding the flags.
  * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is 
@@ -208,10 +259,12 @@
 		ast_log(LOG_WARNING, "multiple data return not implemented yet");
 		return NULL;
 	}
+	if (cb_fn == NULL)	/* if NULL, match everything */
+		cb_fn = cb_true;
 	/*
 	 * XXX this can be optimized.
 	 */
-	if (flags & OBJ_POINTER)	/* we know hash can handle this case */
+	if (c->hash_fn && 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. */
@@ -263,7 +316,7 @@
 				/* here build a list of object pointers and increase
 				 * the reference counter for each reference created */
 				struct astobj2_list *new_obj;
-				new_obj = calloc(1, sizeof(struct astobj2_list));
+				new_obj = ast_calloc(1, sizeof(struct astobj2_list));
 				if ( new_obj == NULL ) {
 					ast_log(LOG_WARNING, "No list created, out of memory\n");
 					continue;
@@ -287,6 +340,9 @@
 	return ret;
 }
 
+/*!
+ * the find function just invokes the default callback with some reasonable flags.
+ */
 void *astobj2_find(struct container *c, void *p, enum search_flags flags)
 {
 	if (flags == 0)
@@ -295,6 +351,9 @@
 	return astobj2_callback(c, flags, c->cmp_fn, p);
 }
 
+/*!
+ * initialize an iterator so we start from the first object
+ */
 void astobj2_iterator_init(struct container *c, struct astobj2_iterator *a)
 {
 	a->bucket = 0;
@@ -384,5 +443,68 @@
 	struct container *c = _c;
 	
 	astobj2_callback(c, OBJ_UNLINK, cd_cb, NULL);
-	astobj2_ref(c, -1);
-}
+}
+
+static int print_cb(void *obj, void *arg, int flag)
+{
+	int fd = (int)arg;
+	char *s = (char *)obj;
+
+	ast_cli(fd, "string <%s>\n", s);
+	return 0;
+}
+
+/*
+ * This is testing code for astobj
+ */
+int handle_astobj2_test(int fd, int argc, char *argv[]);
+int handle_astobj2_test(int fd, int argc, char *argv[])
+
+{
+	struct container *c1;
+	int i, lim;
+	char *obj;
+	static int prof_id = -1;
+	struct astobj2_iterator ai;
+
+	if (prof_id == -1)
+		prof_id = ast_add_profile("astobj2_obj_alloc", 0);
+
+	ast_cli(fd, "argc %d argv %s %s %s\n", argc, argv[0], argv[1], argv[2]);
+	lim = atoi(argv[2]);
+	ast_cli(fd, "called astobj_test\n");
+
+	/*
+	 * allocate a container with no default callback, and no hash function.
+	 * No hash means everything goes in the same bucket.
+	 */
+	c1 = astobj2_container_alloc(100, NULL /* no callback */, NULL /* no hash */);
+	ast_cli(fd, "container allocated as %p\n", c1);
+
+	/*
+	 * fill the container with objects.
+	 * astobj2_obj_alloc() gives us a reference which we pass to the
+	 * container when we do the insert.
+	 */
+	for (i = 0; i < lim; i++) {
+		ast_mark(prof_id, 1 /* start */);
+		obj = astobj2_obj_alloc(80, NULL);
+		ast_mark(prof_id, 0 /* stop */);
+		ast_cli(fd, "object %d allocated as %p\n", i, obj);
+		sprintf(obj, "-- this is obj %d --", i);
+		astobj2_link(c1, obj);
+	}
+	ast_cli(fd, "testing callbacks");
+	astobj2_callback(c1, 0, print_cb, (void*)fd);
+
+	ast_cli(fd, "testing iterators");
+	astobj2_iterator_init(c1, &ai);
+
+	while ( (obj = astobj2_iterator_next(&ai)) ) {
+		ast_cli(fd, "iterator on <%s>", obj);
+	}
+
+	astobj2_ref(c1, -1);	/* destroy all */
+	return 0;
+}
+



More information about the asterisk-commits mailing list