changeset 98:7d3d175c3b8d

Completed the session module and test
author Sebastien Decugis <sdecugis@nict.go.jp>
date Tue, 22 Jul 2008 18:32:12 +0900
parents 55d9e3443e59
children d5fcb2af8c5a
files include/waaad/session-api.h waaad/session.c waaad/session.h waaad/tests/Makefile.am waaad/tests/testsess.c
diffstat 5 files changed, 725 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/include/waaad/session-api.h	Thu Jul 17 18:23:09 2008 +0900
+++ b/include/waaad/session-api.h	Tue Jul 22 18:32:12 2008 +0900
@@ -133,6 +133,7 @@
  * RETURN VALUE:
  *  0      	: The session is created.
  *  EINVAL 	: A parameter is invalid.
+ *  EALREADY	: A session with the same name already exists (only for SESSION_NEW_SEQUENCE and SESSION_NEW_FULL)
  *  ENOMEM	: Not enough memory to complete the operation
  */
 int sess_new ( sess_id_t ** session, sess_flags_t flags, char * opt );
@@ -275,6 +276,7 @@
  * RETURN VALUE:
  *  0      	: The data has been retrieved.
  *  EINVAL 	: A parameter is invalid.
+ *  ENOENT	: No data was associated with this session and client
  */
 int sess_data_get ( sess_id_t * session, sess_reg_t * client, void ** data ); 
 
@@ -284,15 +286,17 @@
  * PARAMETERS:
  *  session	: the session with which the data is associated.
  *  client	: the client that registered the data.
- *  data	: The pointer to the data is written here before being unregistered.
+ *  data	: if !NULL, the pointer to the data is written here before being unregistered.
  *
  * DESCRIPTION: 
- *  Remove data from the session module. The data is stored in the "data" parameter so
- * that the caller can clean it up properly. This makes an implicit call to sess_unlink.
+ *  Remove data from the session module. If data is not NULL, the data is stored in the "data" parameter so
+ * that the caller can clean it up properly. Otherwise, if data is NULL, the cleanup function is called if any. 
+ * This function makes an implicit call to sess_unlink.
  *
  * RETURN VALUE:
  *  0      	: The data is not registered anymore.
  *  EINVAL 	: A parameter is invalid.
+ *  ENOENT	: No data was associated with this session and client
  */
 int sess_data_dereg ( sess_id_t * session, sess_reg_t * client, void ** data );
 
--- a/waaad/session.c	Thu Jul 17 18:23:09 2008 +0900
+++ b/waaad/session.c	Tue Jul 22 18:32:12 2008 +0900
@@ -62,8 +62,9 @@
 /* Types and globals definitions */
 
 /* The following two integers are used to generate values which are eternaly unique */
-static uint32_t   g_sid_h;	/* initialized to the current time at module initialization */
-static uint32_t   g_sid_l;	/* incremented each time a session id is created. There is no lock yet... hum :s */
+static uint32_t   	g_sid_h;	/* initialized to the current time at module initialization */
+static uint32_t   	g_sid_l;	/* incremented each time a session id is created. */
+static pthread_mutex_t 	g_sid_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /************ sess_id_t ***********/
 
@@ -124,10 +125,12 @@
 static struct {
 	_sli_t		sentinel;	/* sentinel element for this sublist */
 	pthread_mutex_t lock;		/* the mutex for this sublist */
+	int		count;		/* Number of items in this list */
 } sess_hash [ 1 << SESS_HASH_SIZE ] ;
 #define H_MASK( __hash ) ((__hash) & (( 1 << SESS_HASH_SIZE ) - 1))
 #define H_LIST( _hash ) (&(sess_hash[H_MASK(_hash)].sentinel))
 #define H_LOCK( _hash ) (&(sess_hash[H_MASK(_hash)].lock    ))
+#define H_COUNT(_hash ) (  sess_hash[H_MASK(_hash)].count    )
 
 
 /************ sess_reg_t and data ***********/
@@ -210,7 +213,7 @@
 } 
 /********************************************************************************************************/
 
-/* Function to compare a (hash, sid) with a sess_id_element. */
+/* Function to compare a (hash, sid, len) with a sess_id_element. */
 static __inline__ int cmp_si(uint32_t hash1, char * sid1, size_t sid1len,  _sess_id_t * si2)
 {
 	if (hash1 < si2->hash)
@@ -257,6 +260,95 @@
 
 /********************************************************************************************************/
 
+/* Remove a piece of data linked from a _sess_reg_t. The _sess_reg_t lock must be held */
+static int _destroy_sr_data(_sd_data_t * sd, void ** data)
+{
+	int ret = 0;
+
+	/* Call the destroy function for this element */
+	if (data)
+		*data = sd->data;
+	else
+		if (sd->clncb)
+			sd->clncb(sd->data);
+
+	/* Unlink the session */
+	ret = sess_unlink((sess_id_t **)&sd->si);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "sess_unlink failed: %s", strerror(ret));
+		return ret;
+	}
+
+	/* Unchain this element */
+	SLI_remove( sd );
+
+	/* And free it */
+	free(sd);
+	
+	return 0;
+}
+
+/* Remove any data linked from a _sess_reg_t. The _sess_reg_t lock must be held */
+static int _destroy_sr_alldata(_sli_t * sentinel)
+{
+	int ret = 0;
+	while (!SLI_isempty( sentinel )) {
+		ret = _destroy_sr_data(_SD(sentinel->next), NULL);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "_destroy_sr_data failed: %s", strerror(ret));
+			return ret;
+		}
+	}
+	return 0;
+}
+
+/* Remove a _sess_reg_t and any data linked from it. The _str_lock lock must be held */	
+static int _destroy_sr(_sess_reg_t * item)
+{
+	int ret = 0;
+	
+	/* Lock the object */
+	ret = pthread_mutex_lock( &item->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Invalidate */
+	item->eyec = 0xdead;
+	
+	/* Destroy any data contained in this object */
+	ret = _destroy_sr_alldata(&item->sentinel);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "_destroy_sr_alldata failed: %s", strerror(ret));
+		(void)pthread_mutex_unlock( &item->lock );
+		return ret;
+	}
+	
+	/* Unlink */
+	SLI_remove(item);
+	
+	/* Destroy the mutex */
+	ret = pthread_mutex_unlock( &item->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	ret = pthread_mutex_destroy( &item->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_destroy failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Free the object */
+	free(item);
+	
+	return 0;
+}
+	
+
+/********************************************************************************************************/
+
 /* Initialize the module */
 int sess_init ( void )
 {
@@ -272,6 +364,7 @@
 			return ret;
 		}
 		SLI_init( &sess_hash[i].sentinel );
+		sess_hash[i].count = 0;
 	}
 	
 	/* Initialize the sentinel for registered clients */
@@ -331,9 +424,6 @@
 		_sess_id_t * newsi;
 		
 		/* The element did not exist already, li points to the previous element */
-		if (new)
-			*new = 0;
-		
 		/* Create a new element -- how to do this outside the mutex section, and efficiently? */
 		newsi = malloc(sizeof(_sess_id_t));
 		if (newsi == NULL) {
@@ -370,8 +460,14 @@
 		/* Store this element in the list */
 		SLI_addafter(li, newsi);
 		
+		/* Increment the counter */
+		H_COUNT(hash) += 1;
+		
 		/* Save the reference to it */
 		*session = (sess_id_t *)newsi; 
+		
+		if (new)
+			*new = 1;
 	}
 end:	
 	/* Finish, unlock the line */
@@ -426,13 +522,24 @@
 				memset(str, 0, len+1);
 				
 				/* Increment the value of the low32 */
+				ret = pthread_mutex_lock(&g_sid_lock);
+				if (ret != 0) {
+					TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+					goto end;
+				}
 				++g_sid_l;
 				
 				/* Now write the new value */
 				if (opt)
-					strsz = snprintf(str, len + 1, "%s;%ju;%ju;%s", g_pconf->diameter_identity, g_sid_h, g_sid_l, opt);
+					strsz = snprintf(str, len + 1, "%s;%u;%u;%s", g_pconf->diameter_identity, g_sid_h, g_sid_l, opt);
 				else
-					strsz = snprintf(str, len + 1, "%s;%ju;%ju", g_pconf->diameter_identity, g_sid_h, g_sid_l);
+					strsz = snprintf(str, len + 1, "%s;%u;%u", g_pconf->diameter_identity, g_sid_h, g_sid_l);
+				
+				ret = pthread_mutex_unlock(&g_sid_lock);
+				if (ret != 0) {
+					TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+					goto end;
+				}
 			}
 			
 			break;
@@ -477,6 +584,7 @@
 	}
 	
 	/* Now str points to the new session string. Create the new session */
+	TRACE_DEBUG(FULL, "Creating session object for sid: '%s'", str);
 	
 	ret = sess_fromsid( str, strsz, session, &new );
 	if (ret != 0) {
@@ -495,7 +603,7 @@
 			goto end;
 		}
 		
-		ret = EINVAL;
+		ret = EALREADY;
 	}
 	
 end:
@@ -509,71 +617,459 @@
 int sess_getsid ( sess_id_t * session, char ** sid )
 {
 	TRACE_ENTRY( "%p %p", session, sid );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check the parameter */
+	if ( (! VALIDATE_SI(session)) || (sid == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Write the location and exit */
+	*sid = _SI(session)->sid;
+	return 0;
 }
 
 /* Increase refcount of a session */
 int sess_link ( sess_id_t * session )
 {
+	int ret = 0;
+	
 	TRACE_ENTRY( "%p", session );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check the parameter */
+	if (! VALIDATE_SI(session)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Lock the list entry */
+	ret = pthread_mutex_lock(H_LOCK(_SI(session)->hash));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Increment the refcount */
+	_SI(session)->rc++;
+	
+	/* Unlock the list entry */
+	ret = pthread_mutex_unlock(H_LOCK(_SI(session)->hash));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
 }
 
 /* Decrease refcount of a session, eventually free it. */
 int sess_unlink ( sess_id_t ** session )
 {
+	int ret = 0;
+	uint32_t hash;
+	
 	TRACE_ENTRY( "%p", session );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check the parameter */
+	if ((session == NULL) || (! VALIDATE_SI(*session))) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Copy the hash value */
+	hash = _SI(*session)->hash;
+	
+	/* Lock the list entry */
+	ret = pthread_mutex_lock(H_LOCK(hash));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Decrement the refcount */
+	_SI(*session)->rc--;
+	
+	/* If the count is now 0, we destroy the object */
+	if (_SI(*session)->rc == 0) {
+		_SI(*session)->eyec = 0xdead;
+		
+		/* Unlink the element from the chain */
+		SLI_remove(*session); /* actually &(*session)->chain, which is the same */
+		
+		/* Update the count */
+		H_COUNT(hash) -= 1;
+		
+		/* Now free the element */
+		if (_SI(*session)->sid)
+			free(_SI(*session)->sid);
+		free(*session);
+	}
+	
+	/* Unlock the list entry */
+	ret = pthread_mutex_unlock(H_LOCK(hash));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Update the location for the calling function */
+	*session = NULL;
+	
+	return 0;
 }
 
 /* Create a new client of the module. */
 int sess_regext ( sess_reg_t ** client )
 {
+	int ret = 0;
+	_sess_reg_t * new = NULL;
+	
 	TRACE_ENTRY( "%p", client );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check the parameter */
+	if (client == NULL) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Create a new object */
+	new = (_sess_reg_t *) malloc(sizeof(_sess_reg_t));
+	if (new == NULL) {
+		log_error("Memory allocation failed: %s\n", strerror(errno));
+		TRACE_DEBUG(INFO, "malloc failed");
+		return ENOMEM;
+	}
+	
+	/* Initialize the object */
+	memset(new, 0, sizeof(_sess_reg_t));
+	SLI_init(&new->list);
+	new->eyec = _SR_EYEC;
+	ret = pthread_mutex_init(&new->lock, NULL);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_init failed: %s", strerror(ret));
+		free(new);
+		return ret;
+	}
+	SLI_init(&new->sentinel);
+	
+	/* Save the new object in the list */
+	ret = pthread_mutex_lock(&_str_lock);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		free(new);
+		return ret;
+	}
+	
+	SLI_addafter( &_srt_sentinel, &new->list);
+	
+	ret = pthread_mutex_unlock(&_str_lock);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Copy the reference to the calling process */
+	*client = (sess_reg_t *)new;
+	
+	return 0;
 }
 
 /* Destroy a client of the module. */
 int sess_deregext ( sess_reg_t * client )
 {
+	int ret = 0;
+	
 	TRACE_ENTRY( "%p", client );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	if (!VALIDATE_SR(client)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Lock the list of clients */
+	ret = pthread_mutex_lock( &_str_lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Now destroy the client handler */
+	ret = _destroy_sr(_SR(client));
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "_destroy_sr failed: %s", strerror(ret));
+		(void)pthread_mutex_unlock( &_str_lock );
+		return ret;
+	}
+	
+	/* Unlock the list of clients */
+	ret = pthread_mutex_unlock( &_str_lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
 }
 
 /* Register data in a session. */
 int sess_data_reg ( sess_id_t * session, sess_reg_t * client, void * data, void (*cleanup)(void *) )
 {
+	int ret = 0;
+	_sd_data_t * new = NULL;
+	 _sli_t *prev = NULL;
+	
 	TRACE_ENTRY( "%p %p %p %p", session, client, data, cleanup );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check parameters */
+	if ( (!VALIDATE_SI(session)) || (!VALIDATE_SR(client)) || (data == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	/* Create a new object to hold the data */
+	new = (_sd_data_t *) malloc(sizeof(_sd_data_t));
+	if (new == NULL) {
+		log_error("Memory allocation failed: %s\n", strerror(errno));
+		TRACE_DEBUG(INFO, "malloc failed");
+		return ENOMEM;
+	}
+	memset(new, 0, sizeof(_sd_data_t));
+	SLI_init(&new->list);
+	new->si = _SI(session);
+	new->clncb = cleanup;
+	new->data = data;
+	
+	/* Link the session */
+	ret = sess_link((sess_id_t *)new->si);
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "sess_link failed: %s", strerror(ret));
+		free(new);
+		return ret;
+	}
+	
+	/* Lock the client object */
+	ret = pthread_mutex_lock( &_SR(client)->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		(void)sess_unlink((sess_id_t **)&new->si);
+		free(new);
+		return ret;
+	}
+	
+	/* Find the place in the list */
+	ret = find_sd(new->si, &_SR(client)->sentinel, &prev);
+	if (ret == 0) {
+		/* There is already data associated with this session and client */
+		TRACE_DEBUG(INFO, "Data already associated with session/client.");
+		(void)sess_unlink((sess_id_t **)&new->si);
+		free(new);
+		(void)pthread_mutex_unlock( &_SR(client)->lock );
+		return EALREADY;
+	}
+	
+	/* Now "prev" is the previous element where the new element must be put */
+	SLI_addafter(prev, new);
+	
+	/* Unlock, we're done */
+	ret = pthread_mutex_unlock( &_SR(client)->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return 0;
 }
 
 /* Retrieve registered data. */
 int sess_data_get ( sess_id_t * session, sess_reg_t * client, void ** data )
 {
+	int ret = 0;
+	 _sli_t *item = NULL;
+	 
 	TRACE_ENTRY( "%p %p %p", session, client, data );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check parameters */
+	if ( (!VALIDATE_SI(session)) || (!VALIDATE_SR(client)) || (data == NULL)) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	*data = NULL;
+	
+	/* Lock the client object */
+	ret = pthread_mutex_lock( &_SR(client)->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Find the data */
+	ret = find_sd(_SI(session), &_SR(client)->sentinel, &item);
+	if (ret == 0) {
+		*data = _SD(item)->data;
+	}
+	
+	/* Unlock, we're done */
+	ret = pthread_mutex_unlock( &_SR(client)->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return (*data == NULL) ? ENOENT : 0;
 }
 
 /* Remove registered data from a session. */
 int sess_data_dereg ( sess_id_t * session, sess_reg_t * client, void ** data )
 {
+	int ret = 0;
+	int noent = 0;
+	 _sli_t *item = NULL;
+	 
 	TRACE_ENTRY( "%p %p %p", session, client, data );
-	TRACE_DEBUG (INFO, "@@@ %s: not implemented yet.", __FUNCTION__ );
-	return ENOTSUP;
+	
+	/* Check parameters */
+	if ( (!VALIDATE_SI(session)) || (!VALIDATE_SR(client)) ) {
+		TRACE_DEBUG(INFO, "Invalid parameter");
+		return EINVAL;
+	}
+	
+	if (data)
+		*data = NULL;
+	
+	/* Lock the client object */
+	ret = pthread_mutex_lock( &_SR(client)->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Find the data */
+	ret = find_sd(_SI(session), &_SR(client)->sentinel, &item);
+	if (ret == 0) {
+		ret = _destroy_sr_data(_SD(item), data);
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "_destroy_sr_data failed: %s", strerror(ret));
+			(void)pthread_mutex_unlock( &_SR(client)->lock );
+			return ret;
+		}
+	} else {
+		TRACE_DEBUG(INFO, "data not found.");
+		noent = 1;
+	}
+	
+	/* Unlock, we're done */
+	ret = pthread_mutex_unlock( &_SR(client)->lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	return noent ? ENOENT : 0;
 }
 
 /* End of the module */
 int sess_fini ( void )
 {
+	int ret = 0;
+	int i;
+	
 	TRACE_ENTRY( "" );
-	TRACE_DEBUG (INFO, "@@@ Not implemented yet." );
+	
+	/* Lock the list of clients */
+	ret = pthread_mutex_lock( &_str_lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_lock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Destroy all the registered clients */
+	while (!SLI_isempty( &_srt_sentinel )) {
+		TRACE_DEBUG(FULL, "Destroying module %p...", _srt_sentinel.next);
+		ret = _destroy_sr( _SR(_srt_sentinel.next) );
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "_destroy_sr failed: %s", strerror(ret));
+			(void)pthread_mutex_unlock( &_str_lock );
+			return ret;
+		}
+	}
+	
+	/* Unlock the list of clients */
+	ret = pthread_mutex_unlock( &_str_lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_unlock failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Destroy the lock */
+	ret = pthread_mutex_destroy( &_str_lock );
+	if (ret != 0) {
+		TRACE_DEBUG(INFO, "pthread_mutex_destroy failed: %s", strerror(ret));
+		return ret;
+	}
+	
+	/* Check that there is no more session registered */
+	for (i = 0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
+		if (!SLI_isempty(&sess_hash[i].sentinel)) {
+			TRACE_DEBUG(INFO, "List of session (%d) is not empty. Maybe messages are still refering sessions? EBUSY", i);
+			return EBUSY;
+		}
+		ret = pthread_mutex_destroy( &sess_hash[i].lock );
+		if (ret != 0) {
+			TRACE_DEBUG(INFO, "pthread_mutex_destroy failed: %s", strerror(ret));
+			return ret;
+		}
+	}
+	
+	/* Done! */
 	return 0;
 }
 
+/********************************************************************************************************/
+/* debug functions */
+
+static void _dump_si ( _sess_id_t * si, int indent )
+{
+	TRACE_DEBUG(FULL, "%*ssession @%p: h:%8x rc:%d ts:%d sid:'%s'", indent, "", si, si->hash, si->rc, si->ts.tv_sec, si->sid);
+}
+
+static void _dump_hash (void)
+{
+	int i;
+	
+	TRACE_DEBUG(FULL, " ======= Dumping the session hash table ======= ");
+	for (i=0; i < sizeof(sess_hash) / sizeof(sess_hash[0]); i++) {
+		_sli_t * item;
+		TRACE_DEBUG(FULL, " l:%02d cnt:%d", i, sess_hash[i].count);
+		for (item = &sess_hash[i].sentinel; item->next != &sess_hash[i].sentinel; item = item->next)
+			_dump_si(_SI(item->next), 3);
+	}
+	TRACE_DEBUG(FULL, " =======/end of session hash table dump/======= ");
+}	
+
+static void _dump_sd ( _sd_data_t * sd, int indent )
+{
+	TRACE_DEBUG(FULL, "%*sdata @%p: data:%p clncb:%p sid:'%s'", indent, "", sd, sd->data, sd->clncb, sd->si->sid);
+}
+
+static void _dump_regs (void)
+{
+	_sli_t * client;
+	_sli_t * data;
+	
+	TRACE_DEBUG(FULL, " ======= Dumping the registered clients list ======= ");
+	for (client = &_srt_sentinel; client->next != &_srt_sentinel; client = client->next) {
+		TRACE_DEBUG(FULL, "client @%p:", client->next);
+		for (data = &_SR(client->next)->sentinel; data->next != &_SR(client->next)->sentinel; data = data->next) {
+			_dump_sd(_SD(data->next), 3);
+		}
+	}
+	TRACE_DEBUG(FULL, " =======/end of registered clients list dump/======= ");
+}	
+
+void sess_dump(void) 
+{
+	_dump_hash();
+	_dump_regs();
+}
+
--- a/waaad/session.h	Thu Jul 17 18:23:09 2008 +0900
+++ b/waaad/session.h	Tue Jul 22 18:32:12 2008 +0900
@@ -79,4 +79,7 @@
 int sess_fini ( void );
 
 
+/* For debug only */
+void sess_dump( void );
+
 #endif /* _SESSION_H */
--- a/waaad/tests/Makefile.am	Thu Jul 17 18:23:09 2008 +0900
+++ b/waaad/tests/Makefile.am	Tue Jul 22 18:32:12 2008 +0900
@@ -36,6 +36,9 @@
 # Testing the queues:
 testmeq_SOURCES = testmeq.c tests.h $(WAAADSOURCES)
 
-check_PROGRAMS = testdict testmesg testmeq
+# Testing the sessions:
+testsess_SOURCES = testsess.c tests.h $(WAAADSOURCES)
+
+check_PROGRAMS = testdict testmesg testmeq testsess
 
 TESTS = $(check_PROGRAMS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/waaad/tests/testsess.c	Tue Jul 22 18:32:12 2008 +0900
@@ -0,0 +1,189 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2008, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "tests.h"
+
+/* Test for the sessions module */
+
+/* Structure that is passed to the module */
+typedef struct {
+	int hasbeenfreed;
+	int id;
+} td_t;
+
+/* Function that "cleanup" the data */
+void cleanup(void * data) {
+	td_t * d = (td_t *)data;
+	d->hasbeenfreed += 1;
+}
+
+#define TEST_DIAM_ID "testsess.myid"
+
+/* Main test routine */
+int main(int argc, char *argv[])
+{
+	sess_reg_t * client1 = NULL;
+	sess_reg_t * client2 = NULL;
+	
+	sess_id_t * session1 = NULL;
+	sess_id_t * session2 = NULL;
+	sess_id_t * session3 = NULL;
+	
+	td_t td[4] = { {0,1}, {0,2}, {0,3}, {0,4}};
+	td_t *ptd;
+	
+	char * str = NULL;
+	int new;
+	
+	/* First, initialize the daemon modules */
+	INIT_WAAAD();
+	g_pconf->diameter_identity = TEST_DIAM_ID;
+	
+	/* Tests creation and destruction of sessions */
+	{
+		sess_id_t * session  = NULL;
+
+		/* Create one session */
+		CHECK( 0, sess_new ( &session1, SESSION_NEW_DEFAULT, NULL ) );
+
+		/* Check it begins as expected */
+		CHECK( 0, sess_getsid ( session1, &str) );
+		CHECK( 0, strncmp( TEST_DIAM_ID, str, strlen(TEST_DIAM_ID)) );
+
+		/* Check we can not create another session with the same name */
+		CHECK( EALREADY, sess_new ( &session, SESSION_NEW_FULL, str ) );
+		CHECK( EALREADY, sess_new ( &session, SESSION_NEW_SEQUENCE, str + strlen(TEST_DIAM_ID) + 1 ) );
+
+		/* Now create a session using the opt part */
+		CHECK( 0, sess_new ( &session2, SESSION_NEW_DEFAULT, "user@testsess" ) );
+		CHECK( 0, sess_getsid ( session2, &str) );
+		CHECK( 0, strcmp( ";user@testsess", str + strlen(str) - strlen(";user@testsess")) ); /* verify the end of the string */
+
+		/* Test the link and unlink */
+		CHECK( 0, sess_link ( session2 ) );
+		session = session2;
+		CHECK( 0, sess_unlink ( &session ) );
+		session = session2;
+		CHECK( 0, sess_unlink ( &session ) );
+		CHECK( EINVAL, sess_unlink ( &session2 ) );
+		CHECK( 0, sess_new ( &session2, SESSION_NEW_DEFAULT, "user@testsess" ) );
+
+		/* Create a third session */
+		CHECK( 0, sess_new ( &session3, SESSION_NEW_SEQUENCE, "1;1;another_test_session" ) );
+
+		/* Check we find it again */
+		CHECK( 0, sess_fromsid ( TEST_DIAM_ID ";" "1;1;another_test_session" "and some trash data follow", strlen(TEST_DIAM_ID ";" "1;1;another_test_session"), &session, &new) );
+		CHECK( 0, new);
+		CHECK( 0, sess_unlink( &session ) );
+
+		#if 0
+		sess_dump();
+		#endif
+	}
+	
+	/* Tests the registration of client functions */
+	{
+		/* Create the two module clients */
+		CHECK( 0, sess_regext ( &client1 ) );
+		CHECK( 0, sess_regext ( &client2 ) );
+
+		/* Test that we can delete the clients */
+		CHECK( 0, sess_deregext ( client1 ) );
+		CHECK( 0, sess_deregext ( client2 ) );
+
+		/* Register it again */
+		CHECK( 0, sess_regext ( &client1 ) );
+		CHECK( 0, sess_regext ( &client2 ) );
+	}
+	
+	/* Now associate data with the sessions */
+	{
+		/* Associate data with all sessions */
+		CHECK( 0, sess_data_reg( session1, client1, &td[0], cleanup) );
+		CHECK( 0, sess_data_reg( session2, client1, &td[1], cleanup) );
+		CHECK( 0, sess_data_reg( session3, client1, &td[2], cleanup) );
+		
+		/* Destroy the client, the cleanup function must be called */		
+		CHECK( 0, sess_deregext ( client1 ) );
+		CHECK( 1, td[0].hasbeenfreed );
+		td[0].hasbeenfreed = 0;
+		CHECK( 1, td[1].hasbeenfreed );
+		td[1].hasbeenfreed = 0;
+		CHECK( 1, td[2].hasbeenfreed );
+		td[2].hasbeenfreed = 0;
+		
+		/* Recreate the client */
+		CHECK( 0, sess_regext ( &client1 ) );
+		
+		/* Associate data */
+		CHECK( 0, sess_data_reg( session1, client1, &td[0], cleanup) );
+		CHECK( 0, sess_data_reg( session2, client1, &td[1], cleanup) );
+		CHECK( 0, sess_data_reg( session2, client2, &td[2], cleanup) );
+		CHECK( 0, sess_data_reg( session3, client2, &td[3], cleanup) );
+		
+		/* Check we cannot associate more data with the same tuplet */
+		CHECK( EALREADY, sess_data_reg( session2, client1, &td[3], cleanup) );
+		
+		/* Get the data */
+		CHECK( 0, sess_data_get( session2, client1, (void **)&ptd ) );
+		CHECK( 2, ptd->id );
+		CHECK( 0, sess_data_get( session2, client2, (void **)&ptd ) );
+		CHECK( 3, ptd->id );
+		
+		/* Check case where there is no data */
+		CHECK( ENOENT, sess_data_get( session1, client2, (void **)&ptd ) );
+		
+		/* Now remove the data */
+		CHECK( 0, sess_data_dereg( session3, client2, (void **)&ptd ) );
+		CHECK( 4, ptd->id );
+		CHECK( 0, ptd->hasbeenfreed );
+		
+		CHECK( 0, sess_data_dereg( session2, client2, NULL ) );
+		CHECK( 1, td[2].hasbeenfreed );
+		
+		#if 0
+		sess_dump();
+		#endif
+	}
+	
+	CHECK( 0, sess_unlink( &session1 ) );
+	CHECK( 0, sess_unlink( &session2 ) );
+	CHECK( 0, sess_unlink( &session3 ) );
+	CHECK( 0, sess_fini() );
+	
+	/* That's all for the tests yet */
+	PASSTEST();
+} 
+	
"Welcome to our mercurial repository"