[asterisk-commits] file: branch file/bridging r114778 - /team/file/bridging/apps/app_confbridge.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Apr 28 12:26:01 CDT 2008


Author: file
Date: Mon Apr 28 12:26:01 2008
New Revision: 114778

URL: http://svn.digium.com/view/asterisk?view=rev&rev=114778
Log:
Convert app_confbridge over to using astobj2.

Modified:
    team/file/bridging/apps/app_confbridge.c

Modified: team/file/bridging/apps/app_confbridge.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/apps/app_confbridge.c?view=diff&rev=114778&r1=114777&r2=114778
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Mon Apr 28 12:26:01 2008
@@ -47,6 +47,7 @@
 #include "asterisk/musiconhold.h"
 #include "asterisk/say.h"
 #include "asterisk/audiohook.h"
+#include "asterisk/astobj2.h"
 
 static char *app = "ConfBridge";
 static char *synopsis = 
@@ -57,7 +58,6 @@
 "      'A' -- Set marked mode\n"
 "      'a' -- Set admin mode\n"
 "      'c' -- Announce user(s) count on joining a conference\n"
-"      'd' -- Dynamically add conference\n"
 "      'm' -- Present menu (user or admin) when '#' is received\n"
 "      'M[(<class>)]'\n"
 "          -- Enable music on hold when the conference has a single caller.\n"
@@ -72,16 +72,15 @@
 "can be set using the CONFBRIDGE_LEAVE_SOUND variable. These can be unique to the caller.";
 
 enum {
-	OPTION_DYNAMIC = (1 << 0),           /*!< Set if the conference is to be dynamically generated */
-	OPTION_ADMIN = (1 << 1),             /*!< Set if the caller is an administrator */
-	OPTION_MENU = (1 << 2),              /*!< Set if the caller should have access to the conference bridge IVR menu */
-	OPTION_MUSICONHOLD = (1 << 3),       /*!< Set if music on hold should be played if nobody else is in the conference bridge */
-	OPTION_NOONLYPERSON = (1 << 4),      /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
-	OPTION_STARTMUTED = (1 << 5),        /*!< Set if the caller should be initially set muted */
-	OPTION_ANNOUNCEUSERCOUNT = (1 << 6), /*!< Set if the number of users should be announced to the caller */
-	OPTION_MARKEDUSER = (1 << 7),        /*!< Set if the caller is a marked user */
-	OPTION_WAITMARKED = (1 << 8),        /*!< Set if the conference must wait for a marked user before starting */
-	OPTION_QUIET = (1 << 9),             /*!< Set if no audio prompts should be played */
+	OPTION_ADMIN = (1 << 0),             /*!< Set if the caller is an administrator */
+	OPTION_MENU = (1 << 1),              /*!< Set if the caller should have access to the conference bridge IVR menu */
+	OPTION_MUSICONHOLD = (1 << 2),       /*!< Set if music on hold should be played if nobody else is in the conference bridge */
+	OPTION_NOONLYPERSON = (1 << 3),      /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
+	OPTION_STARTMUTED = (1 << 4),        /*!< Set if the caller should be initially set muted */
+	OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */
+	OPTION_MARKEDUSER = (1 << 6),        /*!< Set if the caller is a marked user */
+	OPTION_WAITMARKED = (1 << 7),        /*!< Set if the conference must wait for a marked user before starting */
+	OPTION_QUIET = (1 << 8),             /*!< Set if no audio prompts should be played */
 } option_flags;
 
 enum {
@@ -94,7 +93,6 @@
 	AST_APP_OPTION('A', OPTION_MARKEDUSER),
 	AST_APP_OPTION('a', OPTION_ADMIN),
 	AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
-	AST_APP_OPTION('d', OPTION_DYNAMIC),
 	AST_APP_OPTION('m', OPTION_MENU),
 	AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
 	AST_APP_OPTION('1', OPTION_NOONLYPERSON),
@@ -103,7 +101,11 @@
 	AST_APP_OPTION('q', OPTION_QUIET),
 });
 
+/* Maximum length of a conference bridge name */
 #define MAX_CONF_NAME 32
+
+/* Maximum number of buckets our conference bridges container can have */
+#define MAX_CONFERENCE_BRIDGE_BUCKETS 53
 
 /*! \brief The structure that represents a conference bridge */
 struct conference_bridge {
@@ -111,10 +113,8 @@
 	struct ast_bridge *bridge;                                 /*!< Bridge structure doing the mixing */
 	int users;                                                 /*!< Number of users present */
 	int markedusers;                                           /*!< Number of marked users present */
-	unsigned int dynamic:1;                                    /*!< Is this conference bridge dynamically generated? */
 	unsigned int locked:1;                                     /*!< Is this conference bridge locked? */
 	AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list; /*!< List of users participating in the conference bridge */
-	AST_LIST_ENTRY(conference_bridge) list;                    /*!< Linked list information */
 };
 
 /*! \brief The structure that represents a conference bridge user */
@@ -122,16 +122,30 @@
 	struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
 	struct ast_channel *chan;                    /*!< Asterisk channel participating */
 	struct ast_flags flags;                      /*!< Flags passed in when the application was called */
-	char *opt_args[OPTION_ARRAY_SIZE];            /*!< Arguments to options passed when application was called */
+	char *opt_args[OPTION_ARRAY_SIZE];           /*!< Arguments to options passed when application was called */
 	struct ast_bridge_features features;         /*!< Bridge features structure */
 	unsigned int kicked:1;                       /*!< User has been kicked from the conference */
 	AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
 };
 
-/*! \brief List of conference bridges configured or currently in existence */
-static AST_LIST_HEAD_STATIC(conference_bridges, conference_bridge);
+/*! \brief Container to hold all conference bridges in progress */
+static struct ao2_container *conference_bridges;
 
 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
+
+/*! \brief Hashing function used for conference bridges container */
+static int conference_bridge_hash_cb(const void *obj, const int flags)
+{
+	const struct conference_bridge *conference_bridge = obj;
+	return ast_str_hash(conference_bridge->name);
+}
+
+/*! \brief Comparison function used for conference bridges container */
+static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
+{
+	const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
+	return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH : 0);
+}
 
 /*!
  * \brief Announce number of users in the conference bridge to the caller
@@ -184,7 +198,7 @@
 		if (conference_bridge->markedusers >= 2) {
 			return;
 		}
-
+		
 		/* Iterate through every participant stopping MOH on them if need be */
 		AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
 			if (other_conference_bridge_user == conference_bridge_user) {
@@ -196,12 +210,12 @@
 			}
 		}
 
-		/* Next play the audio file stating they are going to placed into the conference */
-		AST_LIST_UNLOCK(&conference_bridges);
+		/* Next play the audio file stating they are going to be placed into the conference */
+		ao2_unlock(conference_bridge);
 		ast_autoservice_start(conference_bridge_user->chan);
 		play_sound_file(conference_bridge, "conf-placeintoconf");
 		ast_autoservice_stop(conference_bridge_user->chan);
-		AST_LIST_LOCK(&conference_bridges);
+		ao2_lock(conference_bridge);
 
 		/* Finally iterate through and unmute them all */
 		AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
@@ -210,7 +224,7 @@
 			}
 			other_conference_bridge_user->features.mute = 0;
 		}
-
+		
 	} else {
 		/* If a marked user already exists in the conference bridge we can just bail out now */
 		if (conference_bridge->markedusers) {
@@ -220,9 +234,9 @@
 		conference_bridge_user->features.mute = 1;
 		/* If we have not been quieted play back that they are waiting for the leader */
 		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
-			AST_LIST_UNLOCK(&conference_bridges);
+			ao2_unlock(conference_bridge);
 			ast_stream_and_wait(conference_bridge_user->chan, "conf-waitforleader", "");
-			AST_LIST_LOCK(&conference_bridges);
+			ao2_lock(conference_bridge);
 		}
 		/* Start music on hold if needed */
 		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
@@ -247,9 +261,9 @@
 	if (conference_bridge->users == 1) {
 		/* If audio prompts have not been quieted or this prompt quieted play it on out */
 		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET) && !ast_test_flag(&conference_bridge_user->flags, OPTION_NOONLYPERSON)) {
-			AST_LIST_UNLOCK(&conference_bridges);
+			ao2_unlock(conference_bridge);
 			ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyperson", "");
-			AST_LIST_LOCK(&conference_bridges);
+			ao2_lock(conference_bridge);
 		}
 		/* If we need to start music on hold on the channel do so now */
 		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
@@ -260,9 +274,9 @@
 
 	/* Announce number of users if need be */
 	if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
-		AST_LIST_UNLOCK(&conference_bridges);
+		ao2_unlock(conference_bridge);
 		announce_user_count(conference_bridge, conference_bridge_user);
-		AST_LIST_LOCK(&conference_bridges);
+		ao2_lock(conference_bridge);
 	}
 
 	/* If we are the second participant we may need to stop music on hold on the first */
@@ -280,6 +294,25 @@
 }
 
 /*!
+ * \brief Destroy a conference bridge
+ *
+ * \param obj The conference bridge object
+ *
+ * \return Nothing
+ */
+static void destroy_conference_bridge(void *obj)
+{
+	struct conference_bridge *conference_bridge = obj;
+
+	ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
+
+	/* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
+	ast_bridge_destroy(conference_bridge->bridge);
+
+	return;
+}
+
+/*!
  * \brief Join a conference bridge
  *
  * \param name The conference name
@@ -290,58 +323,69 @@
 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
 {
 	struct conference_bridge *conference_bridge = NULL;
-
-	AST_LIST_LOCK(&conference_bridges);
-
-	/* Look for an already existing conference bridge */
-	AST_LIST_TRAVERSE(&conference_bridges, conference_bridge, list) {
-		if (!strcasecmp(conference_bridge->name, name)) {
-			break;
-		}
-	}
-
-	/* When finding a conference bridge that already exists make sure it is locked or that we are an admin */
+	struct conference_bridge tmp;
+
+	ast_copy_string(tmp.name, name, sizeof(tmp.name));
+
+	ao2_lock(conference_bridges);
+
+	ast_debug(1, "Trying to find conference bridge '%s'\n", name);
+
+	/* Attempt to find an existing conference bridge */
+	conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
+
+	/* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
 	if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
+		ao2_unlock(conference_bridges);
+		ao2_ref(conference_bridge, -1);
+		ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
 		ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
-		AST_LIST_UNLOCK(&conference_bridges);
 		return NULL;
 	}
 
-	/* If one was not found try to create one if the dynamic option is set */
+	/* If no conference bridge was found see if we can create one */
 	if (!conference_bridge) {
-		/* Oh dear... the dynamic option was not set or we buggered up and couldn't allocate memory for the darn thing */
-		if (!ast_test_flag(&conference_bridge_user->flags, OPTION_DYNAMIC) || !(conference_bridge = ast_calloc(1, sizeof(*conference_bridge)))) {
-			AST_LIST_UNLOCK(&conference_bridges);
+		/* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
+		if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
+			ao2_unlock(conference_bridges);
 			ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
 			return NULL;
 		}
-		/* Set whether this is dynamic and the name, not having those would be so silly */
-		conference_bridge->dynamic = 1;
+
+		/* Setup conference bridge parameters */
 		ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
 
-		/* Actually create the bridge that will do the audio mixing */
+		/* Create an actual bridge that will do the audio mixing */
 		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
-			ast_free(conference_bridge);
-			AST_LIST_UNLOCK(&conference_bridges);
+			ao2_ref(conference_bridge, -1);
+			ao2_unlock(conference_bridges);
+			ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
 			return NULL;
 		}
 
-		/* All done... add it to the list so the next person who joins can find it */
-		AST_LIST_INSERT_TAIL(&conference_bridges, conference_bridge, list);
-	}
+		/* Link it into the conference bridges container */
+		ao2_link(conference_bridges, conference_bridge);
+		ao2_ref(conference_bridge, -1);
+
+		ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
+	}
+
+	ao2_unlock(conference_bridges);
 
 	/* Setup conference bridge user parameters */
 	conference_bridge_user->conference_bridge = conference_bridge;
 
+	ao2_lock(conference_bridge);
+
 	/* All good to go, add them in */
 	AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
 
 	/* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
-	ast_atomic_fetchadd_int(&conference_bridge->users, +1);
+	conference_bridge->users++;
 
 	/* If the caller is a marked user bump up the count */
 	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
-		ast_atomic_fetchadd_int(&conference_bridge->markedusers, +1);
+		conference_bridge->markedusers++;
 	}
 
 	/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
@@ -351,8 +395,7 @@
 		post_join_unmarked(conference_bridge, conference_bridge_user);
 	}
 
-	/* That is that... we are done searchin/modifying the list. */
-	AST_LIST_UNLOCK(&conference_bridges);
+	ao2_unlock(conference_bridge);
 
 	return conference_bridge;
 }
@@ -367,61 +410,58 @@
  */
 static int leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 {
-	AST_LIST_LOCK(&conference_bridges);
+	ao2_lock(conference_bridge);
 
 	/* If this caller is a marked user bump down the count */
 	if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
-		ast_atomic_fetchadd_int(&conference_bridge->markedusers, -1);
+		conference_bridge->markedusers--;
 	}
 
 	/* Decrement the users count while keeping the previous participant count */
-	ast_atomic_fetchadd_int(&conference_bridge->users, -1);
+	conference_bridge->users--;
 
 	/* Drop conference bridge user from the list, they be going bye bye */
 	AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
 
-	if (!conference_bridge->users) {
-		/* If there are no more users in this conference we do not have much to do... */
-		if (conference_bridge->dynamic) {
-			/* Unless it is dynamic in which case we delete it. */
-			ast_bridge_destroy(conference_bridge->bridge);
-			AST_LIST_REMOVE(&conference_bridges, conference_bridge, list);
-			ast_free(conference_bridge);
-		}
-	} else if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
-		/* Now if they were a marked user and no other marked users exist... we have to mute everyone else and start MOH if need be */
-		struct conference_bridge_user *other_participant = NULL;
-
-		/* Start out with muting everyone */
-		AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
-			other_participant->features.mute = 1;
-		}
-
-		/* Play the audio prompt saying the leader has left the conference */
-		AST_LIST_UNLOCK(&conference_bridges);
-		ast_autoservice_start(conference_bridge_user->chan);
-		play_sound_file(conference_bridge, "conf-leaderhasleft");
-		ast_autoservice_stop(conference_bridge_user->chan);
-		AST_LIST_LOCK(&conference_bridges);
-
-		/* Move on to starting MOH if needed */
-		AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
-			if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
-				ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
-				ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
+	/* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
+	if (conference_bridge->users) {
+		if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
+			struct conference_bridge_user *other_participant = NULL;
+
+			/* Start out with muting everyone */
+			AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
+				other_participant->features.mute = 1;
 			}
-		}
-	} else if (conference_bridge->users == 1) {
-		/* Of course if there is one other person in here we may need to start up MOH on them */
-		struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
-
-		if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
-			ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
-			ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
-		}
-	}
-
-	AST_LIST_UNLOCK(&conference_bridges);
+
+			/* Play back the audio prompt saying the leader has left the conference */
+			ao2_unlock(conference_bridge);
+			ast_autoservice_start(conference_bridge_user->chan);
+			play_sound_file(conference_bridge, "conf-leaderhasleft");
+			ast_autoservice_stop(conference_bridge_user->chan);
+			ao2_lock(conference_bridge);
+
+			/* Now on to starting MOH if needed */
+			AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
+				if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
+					ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
+					ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
+				}
+			}
+		} else if (conference_bridge->users == 1) {
+			/* Of course if there is one other person in here we may need to start up MOH on them */
+			struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
+
+			if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
+				ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
+				ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
+			}
+		}
+	}
+
+	/* Done mucking with the conference bridge, huzzah */
+	ao2_unlock(conference_bridge);
+
+	ao2_ref(conference_bridge, -1);
 
 	return 0;
 }
@@ -478,12 +518,12 @@
 	int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
 
 	/* See if music on hold is playing */
-	AST_LIST_LOCK(&conference_bridges);
+	ao2_lock(conference_bridge);
 	if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
 		/* Just us so MOH is probably indeed going, let's stop it */
 		ast_moh_stop(bridge_channel->chan);
 	}
-	AST_LIST_UNLOCK(&conference_bridges);
+	ao2_unlock(conference_bridge);
 
 	/* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
 	if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
@@ -509,14 +549,14 @@
 		/* 3 - Eject last user */
 		struct conference_bridge_user *last_participant = NULL;
 
-		AST_LIST_LOCK(&conference_bridges);
+		ao2_lock(conference_bridge);
 		if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
-			AST_LIST_UNLOCK(&conference_bridges);
+			ao2_unlock(conference_bridge);
 			res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
 		} else {
 			last_participant->kicked = 1;
 			ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
-			AST_LIST_UNLOCK(&conference_bridges);
+			ao2_unlock(conference_bridge);
 		}
 	} else if (digit == '4') {
 		/* 4 - Decrease listening volume */
@@ -539,11 +579,11 @@
 
  finished:
 	/* See if music on hold needs to be started back up again */
-	AST_LIST_LOCK(&conference_bridges);
+	ao2_lock(conference_bridge);
 	if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
 		ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
 	}
-	AST_LIST_UNLOCK(&conference_bridges);
+	ao2_unlock(conference_bridge);
 
 	bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
 
@@ -653,14 +693,24 @@
 /*! \brief Called when module is being unloaded */
 static int unload_module(void)
 {
+	/* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
+	ao2_ref(conference_bridges, -1);
+
 	return ast_unregister_application(app);
 }
 
 /*! \brief Called when module is being loaded */
 static int load_module(void)
 {
-	if (ast_register_application(app, confbridge_exec, synopsis, descrip))
+	/* Create a container to hold the conference bridges */
+	if (!(conference_bridges = ao2_container_alloc(MAX_CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
 		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (ast_register_application(app, confbridge_exec, synopsis, descrip)) {
+		ao2_ref(conference_bridges, -1);
+		return AST_MODULE_LOAD_DECLINE;
+	}
 
 	return AST_MODULE_LOAD_SUCCESS;
 }




More information about the asterisk-commits mailing list