[asterisk-commits] rmudgett: trunk r431161 - in /trunk: ./ apps/ apps/confbridge/include/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jan 27 11:48:24 CST 2015


Author: rmudgett
Date: Tue Jan 27 11:48:18 2015
New Revision: 431161

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=431161
Log:
app_confbridge: Repeatedly starting and stopping recording ref leaks the recording channel.

Starting and stopping conference recording more than once causes the
recording channels to be leaked.  For v13 the channels also show up in the
CLI "core show channels" output.

* Reworked and simplified the recording channel code to use
ast_bridge_impart() instead of managing the recording thread in the
ConfBridge code.  The recording channel's ref handling easily falls into
place and other off nominal code paths get handled better as a result.

ASTERISK-24719 #close
Reported by: John Bigelow

Review: https://reviewboard.asterisk.org/r/4368/
Review: https://reviewboard.asterisk.org/r/4369/
........

Merged revisions 431135 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 431160 from http://svn.asterisk.org/svn/asterisk/branches/13

Modified:
    trunk/   (props changed)
    trunk/apps/app_confbridge.c
    trunk/apps/confbridge/include/confbridge.h

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-13-merged' - no diff available.

Modified: trunk/apps/app_confbridge.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_confbridge.c?view=diff&rev=431161&r1=431160&r2=431161
==============================================================================
--- trunk/apps/app_confbridge.c (original)
+++ trunk/apps/app_confbridge.c Tue Jan 27 11:48:18 2015
@@ -324,14 +324,11 @@
 
 static const char app[] = "ConfBridge";
 
-/* Number of buckets our conference bridges container can have */
+/*! Number of buckets our conference bridges container can have */
 #define CONFERENCE_BRIDGE_BUCKETS 53
 
-enum {
-	CONF_RECORD_EXIT = 0,
-	CONF_RECORD_START,
-	CONF_RECORD_STOP,
-};
+/*! Initial recording filename space. */
+#define RECORD_FILENAME_INITIAL_SPACE	128
 
 /*! \brief Container to hold all conference bridges in progress */
 struct ao2_container *conference_bridges;
@@ -594,10 +591,11 @@
 {
 	if (!ast_strlen_zero(rec_file)) {
 		if (!*orig_rec_file) {
-			*orig_rec_file = ast_str_create(PATH_MAX);
-		}
-
-		if (strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
+			*orig_rec_file = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
+		}
+
+		if (*orig_rec_file
+			&& strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
 			ast_str_set(orig_rec_file, 0, "%s", rec_file);
 			return 1;
 		}
@@ -605,79 +603,48 @@
 	return 0;
 }
 
-static void *record_thread(void *obj)
-{
-	struct confbridge_conference *conference = obj;
-	struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
-	struct ast_channel *chan;
-	struct ast_str *filename = ast_str_alloca(PATH_MAX);
-	struct ast_str *orig_rec_file = NULL;
-	struct ast_bridge_features features;
-
-	ast_mutex_lock(&conference->record_lock);
-	if (!mixmonapp) {
-		ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
-		conference->record_thread = AST_PTHREADT_NULL;
-		ast_mutex_unlock(&conference->record_lock);
-		ao2_ref(conference, -1);
-		return NULL;
-	}
-	if (ast_bridge_features_init(&features)) {
-		ast_bridge_features_cleanup(&features);
-		conference->record_thread = AST_PTHREADT_NULL;
-		ast_mutex_unlock(&conference->record_lock);
-		ao2_ref(conference, -1);
-		return NULL;
-	}
-	ast_set_flag(&features.feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
-
-	/* XXX If we get an EXIT right here, START will essentially be a no-op */
-	while (conference->record_state != CONF_RECORD_EXIT) {
-		set_rec_filename(conference, &filename,
-			is_new_rec_file(conference->b_profile.rec_file, &orig_rec_file));
-		chan = ast_channel_ref(conference->record_chan);
-		ast_answer(chan);
-		pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
-		ast_bridge_join(conference->bridge, chan, NULL, &features, NULL, 0);
-
-		ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
-		/* STOP has been called. Wait for either a START or an EXIT */
-		ast_cond_wait(&conference->record_cond, &conference->record_lock);
-	}
-	ast_bridge_features_cleanup(&features);
-	ast_free(orig_rec_file);
-	ast_mutex_unlock(&conference->record_lock);
-	ao2_ref(conference, -1);
-	return NULL;
-}
-
-/*! \brief Returns whether or not conference is being recorded.
+/*!
+ * \internal
+ * \brief Returns whether or not conference is being recorded.
+ *
  * \param conference The bridge to check for recording
+ *
+ * \note Must be called with the conference locked
+ *
  * \retval 1, conference is recording.
  * \retval 0, conference is NOT recording.
  */
 static int conf_is_recording(struct confbridge_conference *conference)
 {
-	return conference->record_state == CONF_RECORD_START;
-}
-
-/*! \brief Stop recording a conference bridge
+	return conference->record_chan != NULL;
+}
+
+/*!
  * \internal
+ * \brief Stop recording a conference bridge
+ *
  * \param conference The conference bridge on which to stop the recording
+ *
+ * \note Must be called with the conference locked
+ *
  * \retval -1 Failure
  * \retval 0 Success
  */
 static int conf_stop_record(struct confbridge_conference *conference)
 {
 	struct ast_channel *chan;
-	if (conference->record_thread == AST_PTHREADT_NULL || !conf_is_recording(conference)) {
+	struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
+
+	if (!conf_is_recording(conference)) {
 		return -1;
 	}
-	conference->record_state = CONF_RECORD_STOP;
-	chan = ast_channel_ref(conference->record_chan);
-	ast_bridge_remove(conference->bridge, chan);
-	ast_queue_frame(chan, &ast_null_frame);
-	chan = ast_channel_unref(chan);
+
+	/* Remove the recording channel from the conference bridge. */
+	chan = conference->record_chan;
+	conference->record_chan = NULL;
+	ast_queue_frame(chan, &f);
+	ast_channel_unref(chan);
+
 	ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
 	send_stop_record_event(conference);
 
@@ -686,101 +653,72 @@
 
 /*!
  * \internal
- * \brief Stops the confbridge recording thread.
+ * \brief Start recording the conference
+ *
+ * \param conference The conference bridge to start recording
  *
  * \note Must be called with the conference locked
- */
-static int conf_stop_record_thread(struct confbridge_conference *conference)
-{
-	if (conference->record_thread == AST_PTHREADT_NULL) {
-		return -1;
-	}
-	conf_stop_record(conference);
-
-	ast_mutex_lock(&conference->record_lock);
-	conference->record_state = CONF_RECORD_EXIT;
-	ast_cond_signal(&conference->record_cond);
-	ast_mutex_unlock(&conference->record_lock);
-
-	pthread_join(conference->record_thread, NULL);
-	conference->record_thread = AST_PTHREADT_NULL;
-
-	/* this is the reference given to the channel during the channel alloc */
-	if (conference->record_chan) {
-		conference->record_chan = ast_channel_unref(conference->record_chan);
-	}
-
-	return 0;
-}
-
-/*! \brief Start recording the conference
- * \internal
- * \note The conference must be locked when calling this function
- * \param conference The conference bridge to start recording
+ *
  * \retval 0 success
- * \rteval non-zero failure
+ * \retval non-zero failure
  */
 static int conf_start_record(struct confbridge_conference *conference)
 {
+	struct ast_app *mixmonapp;
+	struct ast_channel *chan;
 	struct ast_format_cap *cap;
-
-	if (conference->record_state != CONF_RECORD_STOP) {
+	struct ast_bridge_features *features;
+
+	if (conf_is_recording(conference)) {
 		return -1;
 	}
 
-	if (!pbx_findapp("MixMonitor")) {
-		ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
+	mixmonapp = pbx_findapp("MixMonitor");
+	if (!mixmonapp) {
+		ast_log(LOG_WARNING, "Cannot record ConfBridge, MixMonitor app is not installed\n");
 		return -1;
 	}
+
+	features = ast_bridge_features_new();
+	if (!features) {
+		return -1;
+	}
+	ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
 
 	cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 	if (!cap) {
+		ast_bridge_features_destroy(features);
 		return -1;
 	}
-
 	ast_format_cap_append(cap, ast_format_slin, 0);
 
-	conference->record_chan = ast_request("CBRec", cap, NULL, NULL,
-		conference->name, NULL);
+	/* Create the recording channel. */
+	chan = ast_request("CBRec", cap, NULL, NULL, conference->name, NULL);
 	ao2_ref(cap, -1);
-	if (!conference->record_chan) {
+	if (!chan) {
+		ast_bridge_features_destroy(features);
 		return -1;
 	}
 
-	conference->record_state = CONF_RECORD_START;
-	ast_mutex_lock(&conference->record_lock);
-	ast_cond_signal(&conference->record_cond);
-	ast_mutex_unlock(&conference->record_lock);
+	/* Start recording. */
+	set_rec_filename(conference, &conference->record_filename,
+		is_new_rec_file(conference->b_profile.rec_file, &conference->orig_rec_file));
+	ast_answer(chan);
+	pbx_exec(chan, mixmonapp, ast_str_buffer(conference->record_filename));
+
+	/* Put the channel into the conference bridge. */
+	ast_channel_ref(chan);
+	conference->record_chan = chan;
+	if (ast_bridge_impart(conference->bridge, chan, NULL, features,
+		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
+		ast_hangup(chan);
+		ast_channel_unref(chan);
+		conference->record_chan = NULL;
+		return -1;
+	}
+
 	ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
 	send_start_record_event(conference);
-
-	return 0;
-}
-
-/*! \brief Start the recording thread on a conference bridge
- * \internal
- * \param conference The conference bridge on which to start the recording thread
- * \retval 0 success
- * \retval -1 failure
- */
-static int start_conf_record_thread(struct confbridge_conference *conference)
-{
-	conf_start_record(conference);
-
-	/*
-	 * if the thread has already been started, don't start another
-	 */
-	if (conference->record_thread != AST_PTHREADT_NULL) {
-		return 0;
-	}
-
-	ao2_ref(conference, +1); /* give the record thread a ref */
-
-	if (ast_pthread_create_background(&conference->record_thread, NULL, record_thread, conference)) {
-		ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference->name);
-		ao2_ref(conference, -1); /* error so remove ref */
-		return -1;
-	}
 
 	return 0;
 }
@@ -967,9 +905,11 @@
 		conference->bridge = NULL;
 	}
 
+	ast_channel_cleanup(conference->record_chan);
+	ast_free(conference->orig_rec_file);
+	ast_free(conference->record_filename);
+
 	conf_bridge_profile_destroy(&conference->b_profile);
-	ast_cond_destroy(&conference->record_cond);
-	ast_mutex_destroy(&conference->record_lock);
 	ast_mutex_destroy(&conference->playback_lock);
 }
 
@@ -1212,7 +1152,9 @@
 	/* Called with a reference to conference */
 	ao2_unlink(conference_bridges, conference);
 	send_conf_end_event(conference);
-	conf_stop_record_thread(conference);
+	ao2_lock(conference);
+	conf_stop_record(conference);
+	ao2_unlock(conference);
 }
 
 /*!
@@ -1263,12 +1205,15 @@
 		/* Setup lock for playback channel */
 		ast_mutex_init(&conference->playback_lock);
 
-		/* Setup lock for the record channel */
-		ast_mutex_init(&conference->record_lock);
-		ast_cond_init(&conference->record_cond, NULL);
+		/* Setup for the record channel */
+		conference->record_filename = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
+		if (!conference->record_filename) {
+			ao2_ref(conference, -1);
+			ao2_unlock(conference_bridges);
+			return NULL;
+		}
 
 		/* Setup conference bridge parameters */
-		conference->record_thread = AST_PTHREADT_NULL;
 		ast_copy_string(conference->name, conference_name, sizeof(conference->name));
 		conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
 
@@ -1306,10 +1251,9 @@
 		/* Set the initial state to EMPTY */
 		conference->state = CONF_STATE_EMPTY;
 
-		conference->record_state = CONF_RECORD_STOP;
 		if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
 			ao2_lock(conference);
-			start_conf_record_thread(conference);
+			conf_start_record(conference);
 			ao2_unlock(conference);
 		}
 
@@ -2758,7 +2702,7 @@
 		ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
 	}
 
-	if (start_conf_record_thread(conference)) {
+	if (conf_start_record(conference)) {
 		ast_cli(a->fd, "Could not start recording due to internal error.\n");
 		ao2_unlock(conference);
 		ao2_ref(conference, -1);
@@ -3092,7 +3036,7 @@
 		ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
 	}
 
-	if (start_conf_record_thread(conference)) {
+	if (conf_start_record(conference)) {
 		astman_send_error(s, m, "Internal error starting conference recording.");
 		ao2_unlock(conference);
 		ao2_ref(conference, -1);

Modified: trunk/apps/confbridge/include/confbridge.h
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/confbridge/include/confbridge.h?view=diff&rev=431161&r1=431160&r2=431161
==============================================================================
--- trunk/apps/confbridge/include/confbridge.h (original)
+++ trunk/apps/confbridge/include/confbridge.h Tue Jan 27 11:48:18 2015
@@ -221,13 +221,11 @@
 	unsigned int waitingusers;                                        /*!< Number of waiting users present */
 	unsigned int locked:1;                                            /*!< Is this conference bridge locked? */
 	unsigned int muted:1;                                             /*!< Is this conference bridge muted? */
-	unsigned int record_state:2;                                      /*!< Whether recording is started, stopped, or should exit */
 	struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */
 	struct ast_channel *record_chan;                                  /*!< Channel used for recording the conference */
-	pthread_t record_thread;                                          /*!< The thread the recording chan lives in */
+	struct ast_str *record_filename;                                  /*!< Recording filename. */
+	struct ast_str *orig_rec_file;                                    /*!< Previous b_profile.rec_file. */
 	ast_mutex_t playback_lock;                                        /*!< Lock used for playback channel */
-	ast_mutex_t record_lock;                                          /*!< Lock used for the record thread */
-	ast_cond_t record_cond;                                           /*!< Recording condition variable */
 	AST_LIST_HEAD_NOLOCK(, confbridge_user) active_list;              /*!< List of users participating in the conference bridge */
 	AST_LIST_HEAD_NOLOCK(, confbridge_user) waiting_list;             /*!< List of users waiting to join the conference bridge */
 };




More information about the asterisk-commits mailing list