[svn-commits] dlee: branch dlee/ari-async-bridge r395901 - in /team/dlee/ari-async-bridge: ...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Jul 31 15:35:37 CDT 2013


Author: dlee
Date: Wed Jul 31 15:35:34 2013
New Revision: 395901

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=395901
Log:
Cleanup

Removed:
    team/dlee/ari-async-bridge/res/res_stasis_bridge.c
    team/dlee/ari-async-bridge/res/res_stasis_bridge_add.exports.in
Modified:
    team/dlee/ari-async-bridge/include/asterisk/stasis_app_impl.h
    team/dlee/ari-async-bridge/res/ari/resource_bridges.c
    team/dlee/ari-async-bridge/res/res_stasis.c
    team/dlee/ari-async-bridge/res/res_stasis_playback.c
    team/dlee/ari-async-bridge/res/stasis/control.c
    team/dlee/ari-async-bridge/res/stasis/control.h

Modified: team/dlee/ari-async-bridge/include/asterisk/stasis_app_impl.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-async-bridge/include/asterisk/stasis_app_impl.h?view=diff&rev=395901&r1=395900&r2=395901
==============================================================================
--- team/dlee/ari-async-bridge/include/asterisk/stasis_app_impl.h (original)
+++ team/dlee/ari-async-bridge/include/asterisk/stasis_app_impl.h Wed Jul 31 15:35:34 2013
@@ -95,23 +95,4 @@
  */
 struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control);
 
-
-/*!
- * \since 12
- * \brief Imparts the associated channel into the bridge.
- *
- * \param control Control object for the channel to query.
- * \param bridge New bridge to set on the control object.
- */
-void stasis_app_add_to_bridge(struct stasis_app_control *control,
-	struct ast_bridge *bridge);
-
-/*!
- * \since 12
- * \brief Departs the associated channel from its bridge.
- *
- * \param control Control object for the channel to query.
- */
-void stasis_app_remove_from_bridge(struct stasis_app_control *control);
-
 #endif /* _ASTERISK_RES_STASIS_H */

Modified: team/dlee/ari-async-bridge/res/ari/resource_bridges.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-async-bridge/res/ari/resource_bridges.c?view=diff&rev=395901&r1=395900&r2=395901
==============================================================================
--- team/dlee/ari-async-bridge/res/ari/resource_bridges.c (original)
+++ team/dlee/ari-async-bridge/res/ari/resource_bridges.c Wed Jul 31 15:35:34 2013
@@ -137,6 +137,10 @@
 		return;
 	}
 
+	/* BUGBUG this should make sure the bridge requested for removal is actually
+	 * the bridge the channel is in. This will be possible once the bridge uniqueid
+	 * is added to the channel snapshot. A 409 response should be issued if the bridge
+	 * uniqueids don't match */
 	stasis_app_control_remove_channel_from_bridge(control, bridge);
 
 	ast_ari_response_no_content(response);

Modified: team/dlee/ari-async-bridge/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-async-bridge/res/res_stasis.c?view=diff&rev=395901&r1=395900&r2=395901
==============================================================================
--- team/dlee/ari-async-bridge/res/res_stasis.c (original)
+++ team/dlee/ari-async-bridge/res/res_stasis.c Wed Jul 31 15:35:34 2013
@@ -620,7 +620,6 @@
 			/* Timeout */
 			continue;
 		}
-
 
 		f = ast_read(chan);
 		if (!f) {

Modified: team/dlee/ari-async-bridge/res/res_stasis_playback.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-async-bridge/res/res_stasis_playback.c?view=diff&rev=395901&r1=395900&r2=395901
==============================================================================
--- team/dlee/ari-async-bridge/res/res_stasis_playback.c (original)
+++ team/dlee/ari-async-bridge/res/res_stasis_playback.c Wed Jul 31 15:35:34 2013
@@ -85,6 +85,40 @@
 	enum stasis_app_playback_state state;
 };
 
+static void playback_dtor(void *obj)
+{
+	struct stasis_app_playback *playback = obj;
+
+	ast_string_field_free_memory(playback);
+	ast_cond_destroy(&playback->done_cond);
+}
+
+static struct stasis_app_playback *playback_create(
+	struct stasis_app_control *control)
+{
+	RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
+	char id[AST_UUID_STR_LEN];
+	int res;
+
+	playback = ao2_alloc(sizeof(*playback), playback_dtor);
+	if (!playback || ast_string_field_init(playback, 128)) {
+		return NULL;
+	}
+
+	res = ast_cond_init(&playback->done_cond, NULL);
+	if (res != 0) {
+		ast_log(LOG_ERROR, "Error creating done condition: %s\n",
+			strerror(errno));
+		return NULL;
+	}
+
+	ast_uuid_generate_str(id, sizeof(id));
+	ast_string_field_set(playback, id, id);
+
+	ao2_ref(playback, +1);
+	return playback;
+}
+
 static int playback_hash(const void *obj, int flags)
 {
 	const struct stasis_app_playback *playback = obj;
@@ -150,12 +184,6 @@
 	stasis_app_control_publish(playback->control, message);
 }
 
-static void playback_cleanup(struct stasis_app_playback *playback)
-{
-	ao2_unlink_flags(playbacks, playback,
-		OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
-}
-
 static int playback_first_update(struct stasis_app_playback *playback,
 	const char *uniqueid)
 {
@@ -197,6 +225,9 @@
 	playback_publish(playback);
 }
 
+/*!
+ * \brief RAII_VAR function to mark a playback as done when leaving scope.
+ */
 static void mark_as_done(struct stasis_app_playback *playback)
 {
 	SCOPED_AO2LOCK(lock, playback);
@@ -269,6 +300,12 @@
 	return;
 }
 
+/*!
+ * \brief Special case code to play while a channel is in a bridge.
+ *
+ * \param bridge_channel The channel's bridge_channel.
+ * \param playback_id Id of the playback to start.
+ */
 static void play_on_channel_in_bridge(struct ast_bridge_channel *bridge_channel,
 	const char *playback_id)
 {
@@ -284,11 +321,21 @@
 	play_on_channel(playback, bridge_channel->chan);
 }
 
+/*!
+ * \brief \ref RAII_VAR function to remove a playback from the global list when
+ * leaving scope.
+ */
+static void remove_from_playbacks(struct stasis_app_playback *playback)
+{
+	ao2_unlink_flags(playbacks, playback,
+		OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+}
+
 static void *play_uri(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_playback *, playback, NULL,
-		playback_cleanup);
+		remove_from_playbacks);
 	struct ast_bridge *bridge;
 	int res;
 
@@ -301,6 +348,8 @@
 	bridge = stasis_app_get_bridge(control);
 	if (bridge) {
 		struct ast_bridge_channel *bridge_chan;
+
+		/* Queue up playback on the bridge */
 		ast_bridge_lock(bridge);
 		bridge_chan = bridge_find_channel(bridge, chan);
 		if (bridge_chan) {
@@ -312,6 +361,7 @@
 		}
 		ast_bridge_unlock(bridge);
 
+		/* Wait for playback to complete */
 		ao2_lock(playback);
 		while (!playback->done) {
 			res = ast_cond_wait(&playback->done_cond,
@@ -330,13 +380,6 @@
 	return NULL;
 }
 
-static void playback_dtor(void *obj)
-{
-	struct stasis_app_playback *playback = obj;
-
-	ast_string_field_free_memory(playback);
-}
-
 static void set_target_uri(
 	struct stasis_app_playback *playback,
 	enum stasis_app_playback_target_type target_type,
@@ -364,8 +407,6 @@
 	int skipms, long offsetms)
 {
 	RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
-	char id[AST_UUID_STR_LEN];
-	int res;
 
 	if (skipms < 0 || offsetms < 0) {
 		return NULL;
@@ -374,28 +415,15 @@
 	ast_debug(3, "%s: Sending play(%s) command\n",
 		stasis_app_control_get_channel_id(control), uri);
 
-	playback = ao2_alloc(sizeof(*playback), playback_dtor);
-	if (!playback || ast_string_field_init(playback, 128)) {
-		return NULL;
-	}
-
-	res = ast_cond_init(&playback->done_cond, NULL);
-	if (res != 0) {
-		ast_log(LOG_ERROR, "Error creating done condition: %s\n",
-			strerror(errno));
-		return NULL;
-	}
+	playback = playback_create(control);
 
 	if (skipms == 0) {
 		skipms = PLAYBACK_DEFAULT_SKIPMS;
 	}
 
-	ast_uuid_generate_str(id, sizeof(id));
-	ast_string_field_set(playback, id, id);
 	ast_string_field_set(playback, media, uri);
 	ast_string_field_set(playback, language, language);
 	set_target_uri(playback, target_type, target_id);
-	playback->control = control;
 	playback->skipms = skipms;
 	playback->offsetms = offsetms;
 	ao2_link(playbacks, playback);

Modified: team/dlee/ari-async-bridge/res/stasis/control.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-async-bridge/res/stasis/control.c?view=diff&rev=395901&r1=395900&r2=395901
==============================================================================
--- team/dlee/ari-async-bridge/res/stasis/control.c (original)
+++ team/dlee/ari-async-bridge/res/stasis/control.c Wed Jul 31 15:35:34 2013
@@ -123,7 +123,6 @@
 		return NULL;
 	}
 
-	/* command_queue is a thread safe list; no lock needed */
 	ao2_lock(control->command_queue);
 	ao2_link_flags(control->command_queue, command, OBJ_NOLOCK);
 	ast_cond_signal(&control->wait_cond);
@@ -226,7 +225,9 @@
 
 	ast_assert(control->channel != NULL);
 
-	if (control->bridge) {
+	/* If we're in a Stasis bridge, depart it before going back to the
+	 * dialplan */
+	if (stasis_app_get_bridge(control)) {
 		ast_bridge_depart(control->channel);
 	}
 
@@ -465,24 +466,32 @@
 {
 	if (!control) {
 		return NULL;
-	}
-	return control->bridge;
+	} else {
+		SCOPED_AO2LOCK(lock, control);
+		return control->bridge;
+	}
 }
 
 
 static void bridge_after_cb(struct ast_channel *chan, void *data)
 {
 	struct stasis_app_control *control = data;
+	SCOPED_AO2LOCK(lock, control);
 
 	ast_debug(3, "%s, %s: Channel leaving bridge\n",
 		ast_channel_uniqueid(chan), control->bridge->uniqueid);
 
 	ast_assert(chan == control->channel);
 
+	/* Restore the channel's PBX */
 	ast_channel_pbx_set(control->channel, control->pbx);
 	control->pbx = NULL;
 
+	/* No longer in the bridge */
 	control->bridge = NULL;
+
+	/* Wakeup the command_queue loop */
+	exec_command(control, NULL, NULL);
 }
 
 static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
@@ -494,67 +503,118 @@
 
 	ast_debug(3, "  reason: %s\n",
 		ast_bridge_after_cb_reason_string(reason));
-
-	/* Wakeup the command_queue loop */
-	exec_command(control, NULL, NULL);
-}
-
-void stasis_app_add_to_bridge(struct stasis_app_control *control,
-	struct ast_bridge *bridge)
-{
+}
+
+static void *app_control_add_channel_to_bridge(
+	struct stasis_app_control *control,
+	struct ast_channel *chan, void *data)
+{
+	struct ast_bridge *bridge = data;
 	int res;
 
 	if (!control || !bridge) {
-		return;
-	}
-
-	ast_assert(control->channel != NULL);
-
-	/* Depart whatever Stasis bridge we're currently in */
-	if (control->bridge) {
-		ast_bridge_depart(control->channel);
-	}
-
-	res = ast_bridge_set_after_callback(control->channel, bridge_after_cb,
+		return NULL;
+	}
+
+	ast_debug(3, "%s: Adding to bridge %s\n",
+		stasis_app_control_get_channel_id(control),
+		bridge->uniqueid);
+
+	ast_assert(chan != NULL);
+
+	/* Depart whatever Stasis bridge we're currently in. */
+	if (stasis_app_get_bridge(control)) {
+		/* Note that it looks like there's a race condition here, since
+		 * we don't have control locked. But this happens from the
+		 * control callback thread, so there won't be any other
+		 * concurrent attempts to bridge.
+		 */
+		ast_bridge_depart(chan);
+	}
+
+
+	res = ast_bridge_set_after_callback(chan, bridge_after_cb,
 		bridge_after_cb_failed, control);
 	if (res != 0) {
 		ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
-		return;
-	}
-
-	/* Save off the channel's PBX */
-	ast_assert(!control->pbx || !ast_channel_pbx(control->channel));
-	if (!control->pbx) {
-		control->pbx = ast_channel_pbx(control->channel);
-		ast_channel_pbx_set(control->channel, NULL);
-	}
-
-	res = ast_bridge_impart(bridge,
-		control->channel,
-		NULL, /* swap channel */
-		NULL, /* features */
-		0); /* independent - false allows us to ast_bridge_depart() */
-
-	if (res != 0) {
-		ast_log(LOG_ERROR, "Error adding channel to bridge\n");
-		ast_channel_pbx_set(control->channel, control->pbx);
-		control->pbx = NULL;
-		return;
-	}
-
-	control->bridge = bridge;
-}
-
-void stasis_app_remove_from_bridge(struct stasis_app_control *control)
-{
+		return NULL;
+	}
+
+	{
+		/* pbx and bridge are modified by the bridging impart thread.
+		 * It shouldn't happen concurrently, but we still need to lock
+		 * for the memory fence.
+		 */
+		SCOPED_AO2LOCK(lock, control);
+
+		/* Save off the channel's PBX */
+		ast_assert(control->pbx == NULL);
+		if (!control->pbx) {
+			control->pbx = ast_channel_pbx(chan);
+			ast_channel_pbx_set(chan, NULL);
+		}
+
+		res = ast_bridge_impart(bridge,
+			chan,
+			NULL, /* swap channel */
+			NULL, /* features */
+			0); /* independent - false allows us to ast_bridge_depart() */
+
+		if (res != 0) {
+			ast_log(LOG_ERROR, "Error adding channel to bridge\n");
+			ast_channel_pbx_set(chan, control->pbx);
+			control->pbx = NULL;
+			return NULL;
+		}
+
+		ast_assert(stasis_app_get_bridge(control) == NULL);
+		control->bridge = bridge;
+	}
+	return NULL;
+}
+
+void stasis_app_control_add_channel_to_bridge(
+	struct stasis_app_control *control, struct ast_bridge *bridge)
+{
+	ast_debug(3, "%s: Sending channel add_to_bridge command\n",
+			stasis_app_control_get_channel_id(control));
+	stasis_app_send_command_async(control,
+		app_control_add_channel_to_bridge, bridge);
+}
+
+static void *app_control_remove_channel_from_bridge(
+	struct stasis_app_control *control,
+	struct ast_channel *chan, void *data)
+{
+	struct ast_bridge *bridge = data;
+
+	if (!control) {
+		return NULL;
+	}
+
 	/* We should only depart from our own bridge */
-	if (!control || !control->bridge) {
-		return;
-	}
-
-	ast_assert(control->channel != NULL);
-
-	ast_bridge_depart(control->channel);
+	ast_debug(3, "%s: Departing bridge %s\n",
+		stasis_app_control_get_channel_id(control),
+		bridge->uniqueid);
+
+	if (bridge != stasis_app_get_bridge(control)) {
+		ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n",
+			stasis_app_control_get_channel_id(control),
+			bridge->uniqueid);
+		return NULL;
+	}
+
+	ast_bridge_depart(chan);
+	return NULL;
+}
+
+void stasis_app_control_remove_channel_from_bridge(
+	struct stasis_app_control *control, struct ast_bridge *bridge)
+{
+	ast_debug(3, "%s: Sending channel remove_from_bridge command\n",
+			stasis_app_control_get_channel_id(control));
+	stasis_app_send_command_async(control,
+		app_control_remove_channel_from_bridge, bridge);
 }
 
 const char *stasis_app_control_get_channel_id(
@@ -601,18 +661,17 @@
 
 void control_wait(struct stasis_app_control *control)
 {
-	ast_mutex_t *queue_lock;
 	if (!control) {
 		return;
 	}
 
 	ast_assert(control->command_queue != NULL);
 
-	queue_lock = ao2_object_get_lockaddr(control->command_queue);
 	ao2_lock(control->command_queue);
 	while (ao2_container_count(control->command_queue) == 0) {
-		int r = ast_cond_wait(&control->wait_cond, queue_lock);
-		if (r < 0) {
+		int res = ast_cond_wait(&control->wait_cond,
+			ao2_object_get_lockaddr(control->command_queue));
+		if (res < 0) {
 			ast_log(LOG_ERROR, "Error waiting on command queue\n");
 			break;
 		}

Modified: team/dlee/ari-async-bridge/res/stasis/control.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-async-bridge/res/stasis/control.h?view=diff&rev=395901&r1=395900&r2=395901
==============================================================================
--- team/dlee/ari-async-bridge/res/stasis/control.h (original)
+++ team/dlee/ari-async-bridge/res/stasis/control.h Wed Jul 31 15:35:34 2013
@@ -48,10 +48,29 @@
 int control_dispatch_all(struct stasis_app_control *control,
 	struct ast_channel *chan);
 
+/*!
+ * \brief Blocks until \a control's command queue has a command available.
+ *
+ * \param control Control to block on.
+ */
 void control_wait(struct stasis_app_control *control);
 
+/*!
+ * \brief Signals that a control object should finish and exit back to the
+ * dialplan.
+ *
+ * \param control Control object to continue.
+ */
+void control_continue(struct stasis_app_control *control);
+
+/*!
+ * \brief Returns true if control_continue() has been called on this \a control.
+ *
+ * \param control Control to query.
+ * \return True (non-zero) if control_continue() has been called.
+ * \return False (zero) otherwise.
+ */
 int control_is_done(struct stasis_app_control *control);
 
-void control_continue(struct stasis_app_control *control);
 
 #endif /* _ASTERISK_RES_STASIS_CONTROL_H */




More information about the svn-commits mailing list