Fixed Confbridge file recording deadlock and appending.
authorKevin Harwell <kharwell@digium.com>
Mon, 18 Feb 2013 22:23:52 +0000 (22:23 +0000)
committerKevin Harwell <kharwell@digium.com>
Mon, 18 Feb 2013 22:23:52 +0000 (22:23 +0000)
A deadlock occurred after starting/stopping and then restarting a confbridge
recording.  Upon starting a recording a record thread is created that holds a
lock until just before exiting.  Stopping the recording does not stop/exit the
thread or release the lock.  The thread waits until recording begins again.
Starting a stopped recording signals the thread to continue and start recording
again.  However restarting the recording also created another record thread
resulting in a deadlock.  The fix was to make sure the record thread was only
created once.

Also it was noted that filenames for the recordings were being concatenated for
each start/stop.  This was fixed by creating a new file for each conference
session and appending the actual recorded data within the file (e.g. passing
the 'a' option to MixMonitor).

(issue AST-1088)
Reported by: John Bigelow
Review: http://reviewboard.digium.internal/r/374/
........

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

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@381703 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_confbridge.c

index 4190b9f..5bc1c5d 100644 (file)
@@ -596,6 +596,34 @@ static struct ast_channel *rec_request(const char *type, struct ast_format_cap *
        return tmp;
 }
 
+static void set_rec_filename(struct conference_bridge *bridge, struct ast_str **filename)
+{
+       char *rec_file = bridge->b_profile.rec_file;
+       time_t now;
+       char *ext;
+
+       if (ast_str_strlen(*filename)) {
+                   return;
+       }
+
+       time(&now);
+
+       ast_str_reset(*filename);
+       if (ast_strlen_zero(rec_file)) {
+               ast_str_set(filename, 0, "confbridge-%s-%u.wav", bridge->name, (unsigned int)now);
+       } else {
+               /* insert time before file extension */
+               ext = strrchr(rec_file, '.');
+               if (ext) {
+                       ast_str_set_substr(filename, 0, rec_file, ext - rec_file);
+                       ast_str_append(filename, 0, "-%u%s", (unsigned int)now, ext);
+               } else {
+                       ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int)now);
+               }
+       }
+       ast_str_append(filename, 0, ",a");
+}
+
 static void *record_thread(void *obj)
 {
        struct conference_bridge *conference_bridge = obj;
@@ -614,16 +642,7 @@ static void *record_thread(void *obj)
 
        /* XXX If we get an EXIT right here, START will essentially be a no-op */
        while (conference_bridge->record_state != CONF_RECORD_EXIT) {
-               if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
-                       ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
-               } else {
-                       time_t now;
-                       time(&now);
-                       ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
-                               conference_bridge->name,
-                               (unsigned int) now);
-               }
-
+               set_rec_filename(conference_bridge, &filename);
                chan = ast_channel_ref(conference_bridge->record_chan);
                ast_answer(chan);
                pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
@@ -757,6 +776,13 @@ static int start_conf_record_thread(struct conference_bridge *conference_bridge)
 
        conf_start_record(conference_bridge);
 
+       /*
+        * if the thread has already been started, don't start another
+        */
+       if (conference_bridge->record_thread != AST_PTHREADT_NULL) {
+               return 0;
+       }
+
        if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
                ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
                ao2_ref(conference_bridge, -1); /* error so remove ref */