[svn-commits] rmudgett: branch rmudgett/bridge_phase r393328 - in /team/rmudgett/bridge_pha...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Jul 1 13:38:25 CDT 2013


Author: rmudgett
Date: Mon Jul  1 13:38:23 2013
New Revision: 393328

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393328
Log:
Change when after bridge callback failed calls happen.

The after bridge failed callbacks happen when the channel leaves the
bridge or is destroyed.  The channel environment is now predictable.

Modified:
    team/rmudgett/bridge_phase/include/asterisk/bridging.h
    team/rmudgett/bridge_phase/main/bridging.c

Modified: team/rmudgett/bridge_phase/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/include/asterisk/bridging.h?view=diff&rev=393328&r1=393327&r2=393328
==============================================================================
--- team/rmudgett/bridge_phase/include/asterisk/bridging.h (original)
+++ team/rmudgett/bridge_phase/include/asterisk/bridging.h Mon Jul  1 13:38:23 2013
@@ -1647,7 +1647,7 @@
 
 /*! Reason the the after bridge callback will not be called. */
 enum ast_after_bridge_cb_reason {
-	/*! The datastore is being destroyed.  Likely due to hangup. */
+	/*! The datastore is being destroyed.  Likely due to hangup. (Enum value must be zero.) */
 	AST_AFTER_BRIDGE_CB_REASON_DESTROY,
 	/*! Something else replaced the callback with another. */
 	AST_AFTER_BRIDGE_CB_REASON_REPLACED,
@@ -1666,6 +1666,9 @@
  * \param reason Reason callback is failing.
  * \param data Extra data what setup the callback wanted to pass.
  *
+ * \note Called when the channel leaves the bridging system or
+ * is destroyed.
+ *
  * \return Nothing
  */
 typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
@@ -1705,6 +1708,9 @@
  *
  * \note chan is locked by this function.
  *
+ * \note failed is called when the channel leaves the bridging
+ * system or is destroyed.
+ *
  * \retval 0 on success.
  * \retval -1 on error.
  */

Modified: team/rmudgett/bridge_phase/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/main/bridging.c?view=diff&rev=393328&r1=393327&r2=393328
==============================================================================
--- team/rmudgett/bridge_phase/main/bridging.c (original)
+++ team/rmudgett/bridge_phase/main/bridging.c Mon Jul  1 13:38:23 2013
@@ -3145,15 +3145,70 @@
 	return bridge_channel;
 }
 
-struct after_bridge_cb_ds {
+struct after_bridge_cb_node {
+	/*! Next list node. */
+	AST_LIST_ENTRY(after_bridge_cb_node) list;
 	/*! Desired callback function. */
 	ast_after_bridge_cb callback;
 	/*! After bridge callback will not be called and destroy any resources data may contain. */
 	ast_after_bridge_cb_failed failed;
 	/*! Extra data to pass to the callback. */
 	void *data;
+	/*! Reason the after bridge callback failed. */
+	enum ast_after_bridge_cb_reason reason;
 };
 
+struct after_bridge_cb_ds {
+	/*! After bridge callbacks container. */
+	AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
+};
+
+/*!
+ * \internal
+ * \brief Indicate after bridge callback failed.
+ * \since 12.0.0
+ *
+ * \param node After bridge callback node.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
+{
+	if (node->failed) {
+		node->failed(node->reason, node->data);
+		node->failed = NULL;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Run discarding any after bridge callbacks.
+ * \since 12.0.0
+ *
+ * \param after_bridge After bridge callback container process.
+ * \param reason Why are we doing this.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason)
+{
+	struct after_bridge_cb_node *node;
+
+	for (;;) {
+		AST_LIST_LOCK(&after_bridge->callbacks);
+		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
+		AST_LIST_UNLOCK(&after_bridge->callbacks);
+		if (!node) {
+			break;
+		}
+		if (!node->reason) {
+			node->reason = reason;
+		}
+		after_bridge_cb_failed(node);
+		ast_free(node);
+	}
+}
+
 /*!
  * \internal
  * \brief Destroy the after bridge callback datastore.
@@ -3167,10 +3222,9 @@
 {
 	struct after_bridge_cb_ds *after_bridge = data;
 
-	if (after_bridge->failed) {
-		after_bridge->failed(AST_AFTER_BRIDGE_CB_REASON_DESTROY, after_bridge->data);
-		after_bridge->failed = NULL;
-	}
+	after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY);
+
+	AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
 	ast_free(after_bridge);
 }
 
@@ -3187,7 +3241,6 @@
  */
 static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 {
-	/* There can be only one.  Discard any already on the new channel. */
 	ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
 }
 
@@ -3199,86 +3252,144 @@
 
 /*!
  * \internal
- * \brief Remove channel after the bridge callback and return it.
- * \since 12.0.0
- *
- * \param chan Channel to remove after bridge callback.
- *
- * \retval datastore on success.
- * \retval NULL on error or not found.
- */
-static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-
-	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
-	if (datastore && ast_channel_datastore_remove(chan, datastore)) {
-		datastore = NULL;
-	}
-
-	return datastore;
-}
-
-static void ast_after_bridge_callback_replace(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason, struct ast_datastore *new_datastore)
-{
-	struct ast_datastore *old_datastore;
-
-	ast_channel_lock(chan);
-	old_datastore = after_bridge_cb_remove(chan);
-	if (new_datastore) {
-		ast_channel_datastore_add(chan, new_datastore);
-	}
-	ast_channel_unlock(chan);
-	if (old_datastore) {
-		struct after_bridge_cb_ds *after_bridge = old_datastore->data;
-
-		if (after_bridge->failed) {
-			after_bridge->failed(reason, after_bridge->data);
-			after_bridge->failed = NULL;
-		}
-		ast_datastore_free(old_datastore);
-	}
-}
-
-void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
-{
-	ast_after_bridge_callback_replace(chan, reason, NULL);
-}
-
-/*!
- * \internal
- * \brief Run any after bridge callback.
- * \since 12.0.0
- *
- * \param chan Channel to run after bridge callback.
- *
- * \return Nothing
- */
-static void after_bridge_callback_run(struct ast_channel *chan)
+ * \brief Setup/create an after bridge callback datastore container.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup/create the after bridge callback container on.
+ *
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
+ */
+static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
 {
 	struct ast_datastore *datastore;
 	struct after_bridge_cb_ds *after_bridge;
-
-	/* Get after bridge goto datastore. */
-	ast_channel_lock(chan);
-	datastore = after_bridge_cb_remove(chan);
-	ast_channel_unlock(chan);
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
+	if (datastore) {
+		return datastore->data;
+	}
+
+	/* Create a new datastore. */
+	datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
 	if (!datastore) {
+		return NULL;
+	}
+	after_bridge = ast_calloc(1, sizeof(*after_bridge));
+	if (!after_bridge) {
+		ast_datastore_free(datastore);
+		return NULL;
+	}
+	AST_LIST_HEAD_INIT(&after_bridge->callbacks);
+	datastore->data = after_bridge;
+	ast_channel_datastore_add(chan, datastore);
+
+	return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Find an after bridge callback datastore container.
+ * \since 12.0.0
+ *
+ * \param chan Channel to find the after bridge callback container on.
+ *
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
+ */
+static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
+	if (!datastore) {
+		return NULL;
+	}
+	return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Run any after bridge callback.
+ * \since 12.0.0
+ *
+ * \param chan Channel to run after bridge callback.
+ *
+ * \return Nothing
+ */
+static void after_bridge_callback_run(struct ast_channel *chan)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *node;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
 		return;
 	}
 
-	after_bridge = datastore->data;
-	after_bridge->failed = NULL;
-	after_bridge->callback(chan, after_bridge->data);
-
-	/* Discard after bridge callback datastore. */
-	ast_datastore_free(datastore);
+	for (;;) {
+		AST_LIST_LOCK(&after_bridge->callbacks);
+		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
+		AST_LIST_UNLOCK(&after_bridge->callbacks);
+		if (!node) {
+			break;
+		}
+		if (node->reason) {
+			after_bridge_cb_failed(node);
+		} else {
+			node->failed = NULL;
+			node->callback(chan, node->data);
+		}
+		ast_free(node);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Run discarding any after bridge callbacks.
+ * \since 12.0.0
+ *
+ * \param chan Channel to run after bridge callback.
+ *
+ * \return Nothing
+ */
+static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
+{
+	struct after_bridge_cb_ds *after_bridge;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
+		return;
+	}
+
+	after_bridge_cb_run_discard(after_bridge, reason);
+}
+
+void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *node;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
+		return;
+	}
+
+	AST_LIST_LOCK(&after_bridge->callbacks);
+	node = AST_LIST_LAST(&after_bridge->callbacks);
+	if (node && !node->reason) {
+		node->reason = reason;
+	}
+	AST_LIST_UNLOCK(&after_bridge->callbacks);
 }
 
 int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
 {
-	struct ast_datastore *datastore;
 	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *new_node;
+	struct after_bridge_cb_node *last_node;
 
 	/* Sanity checks. */
 	ast_assert(chan != NULL);
@@ -3286,26 +3397,28 @@
 		return -1;
 	}
 
-	/* Create a new datastore. */
-	datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
-	if (!datastore) {
+	after_bridge = after_bridge_cb_setup(chan);
+	if (!after_bridge) {
 		return -1;
 	}
-	after_bridge = ast_calloc(1, sizeof(*after_bridge));
-	if (!after_bridge) {
-		ast_datastore_free(datastore);
+
+	/* Create a new callback node. */
+	new_node = ast_calloc(1, sizeof(*new_node));
+	if (!new_node) {
 		return -1;
 	}
-
-	/* Initialize it. */
-	after_bridge->callback = callback;
-	after_bridge->failed = failed;
-	after_bridge->data = data;
-	datastore->data = after_bridge;
-
-	/* Put it on the channel replacing any existing one. */
-	ast_after_bridge_callback_replace(chan, AST_AFTER_BRIDGE_CB_REASON_REPLACED,
-		datastore);
+	new_node->callback = callback;
+	new_node->failed = failed;
+	new_node->data = data;
+
+	/* Put it in the container disabling any previously active one. */
+	AST_LIST_LOCK(&after_bridge->callbacks);
+	last_node = AST_LIST_LAST(&after_bridge->callbacks);
+	if (last_node && !last_node->reason) {
+		last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED;
+	}
+	AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
+	AST_LIST_UNLOCK(&after_bridge->callbacks);
 	return 0;
 }
 
@@ -3770,7 +3883,7 @@
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
 
-	ast_after_bridge_callback_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
+	after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
 	ast_after_bridge_goto_discard(bridge_channel->chan);
 
 	return NULL;




More information about the svn-commits mailing list