Make the difference clear about what the responsibilities of the core and a spy are...
authorJoshua Colp <jcolp@digium.com>
Sun, 3 Sep 2006 23:30:37 +0000 (23:30 +0000)
committerJoshua Colp <jcolp@digium.com>
Sun, 3 Sep 2006 23:30:37 +0000 (23:30 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@41959 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_chanspy.c
apps/app_mixmonitor.c
include/asterisk/chanspy.h
main/channel.c

index f433c0d..56052a4 100644 (file)
@@ -211,21 +211,6 @@ static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, s
        return res;
 }
 
-static void stop_spying(struct ast_channel_spy *spy) 
-{
-       /* If our status has changed to DONE, then the channel we're spying on is gone....
-          DON'T TOUCH IT!!!  RUN AWAY!!! */
-       if (spy->status == CHANSPY_DONE)
-               return;
-
-       if (!spy->chan)
-               return;
-
-       ast_channel_lock(spy->chan);
-       ast_channel_spy_remove(spy->chan, spy);
-       ast_channel_unlock(spy->chan);
-};
-
 /* Map 'volume' levels from -4 through +4 into
    decibel (dB) settings for channel drivers
 */
@@ -327,10 +312,11 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
        */
        while ((res = ast_waitfor(chan, -1) > -1) &&
               csth.spy.status == CHANSPY_RUNNING &&
-              !ast_check_hangup(chan) &&
               csth.spy.chan) {
-               if (!(f = ast_read(chan)))
+               if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
+                       running = -1;
                        break;
+               }
 
                if (ast_test_flag(flags, OPTION_WHISPER) &&
                    (f->frametype == AST_FRAME_VOICE)) {
@@ -381,13 +367,16 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
        else
                ast_deactivate_generator(chan);
 
-       stop_spying(&csth.spy);
+       /* If a channel still exists on our spy structure then we need to remove ourselves */
+        if (csth.spy.chan) {
+                csth.spy.status = CHANSPY_DONE;
+                ast_channel_spy_remove(csth.spy.chan, &csth.spy);
+        }
+       ast_channel_spy_free(&csth.spy);
        
        if (option_verbose >= 2)
                ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
        
-       ast_mutex_destroy(&csth.spy.lock);
-
        return running;
 }
 
@@ -532,6 +521,8 @@ static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
                                peer = NULL;
                        }
                }
+               if (res == -1)
+                       break;
        }
        
        ast_clear_flag(chan, AST_FLAG_SPYING);
index aa7602f..5038776 100644 (file)
@@ -122,23 +122,6 @@ AST_APP_OPTIONS(mixmonitor_opts, {
        AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
 });
 
-static void stopmon(struct ast_channel_spy *spy) 
-{
-       struct ast_channel *chan = spy->chan;
-
-       /* If our status has changed to DONE, then the channel we're spying on is gone....
-          DON'T TOUCH IT!!!  RUN AWAY!!! */
-       if (spy->status == CHANSPY_DONE)
-               return;
-  
-       if (!chan)
-               return;
-
-       ast_channel_lock(chan);
-       ast_channel_spy_remove(chan, spy);
-       ast_channel_unlock(chan);
-}
-
 static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
 {
        struct ast_channel *peer;
@@ -176,9 +159,8 @@ static void *mixmonitor_thread(void *obj)
 
                ast_channel_spy_trigger_wait(&mixmonitor->spy);
                
-               if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING) {
+               if (!mixmonitor->spy.chan || mixmonitor->spy.status != CHANSPY_RUNNING)
                        break;
-               }
                
                while (1) {
                        if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME)))
@@ -194,15 +176,15 @@ static void *mixmonitor_thread(void *obj)
                                next = AST_LIST_NEXT(f, frame_list);
                                if (write)
                                        ast_writestream(mixmonitor->fs, f);
-                               ast_frfree(f);
+                               ast_frame_free(f, 0);
                        }
                }
        }
 
        ast_mutex_unlock(&mixmonitor->spy.lock);
-       
-       stopmon(&mixmonitor->spy);
 
+       ast_channel_spy_free(&mixmonitor->spy);
+       
        if (option_verbose > 1)
                ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
 
@@ -211,8 +193,6 @@ static void *mixmonitor_thread(void *obj)
                        ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
                ast_safe_system(mixmonitor->post_process);
        }
-
-       ast_mutex_destroy(&mixmonitor->spy.lock);
                
        ast_closestream(mixmonitor->fs);
 
index dd8d79e..8550210 100644 (file)
@@ -95,6 +95,15 @@ int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy);
 void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy);
 
 /*!
+  \brief Free a spy.
+  \param spy The spy to free
+  \return nothing
+
+  Note: This function MUST NOT be called with the spy locked.
+*/
+void ast_channel_spy_free(struct ast_channel_spy *spy);
+
+/*!
   \brief Find all spies of a particular type on a channel and stop them.
   \param chan The channel to operate on
   \param type A character string identifying the type of spies to be stopped
index 18e8baa..dec1f86 100644 (file)
@@ -1203,22 +1203,61 @@ int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
        return 0;
 }
 
+/* Clean up a channel's spy information */
+static void spy_cleanup(struct ast_channel *chan)
+{
+       if (!AST_LIST_EMPTY(&chan->spies->list))
+               return;
+       if (chan->spies->read_translator.path)
+               ast_translator_free_path(chan->spies->read_translator.path);
+       if (chan->spies->write_translator.path)
+               ast_translator_free_path(chan->spies->write_translator.path);
+       free(chan->spies);
+       chan->spies = NULL;
+       return;
+}
+
+/* Detach a spy from it's channel */
+static void spy_detach(struct ast_channel_spy *spy, struct ast_channel *chan)
+{
+       ast_mutex_lock(&spy->lock);
+
+       /* We only need to poke them if they aren't already done */
+       if (spy->status != CHANSPY_DONE) {
+               /* Indicate to the spy to stop */
+               spy->status = CHANSPY_STOP;
+               spy->chan = NULL;
+               /* Poke the spy if needed */
+               if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
+                       ast_cond_signal(&spy->trigger);
+       }
+
+       /* Print it out while we still have a lock so the structure can't go away (if signalled above) */
+       ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n", spy->type, chan->name);
+
+       ast_mutex_unlock(&spy->lock);
+
+       return;
+}
+
 void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
 {
-       struct ast_channel_spy *spy;
+       struct ast_channel_spy *spy = NULL;
        
        if (!chan->spies)
                return;
 
-       AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
                ast_mutex_lock(&spy->lock);
                if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
-                       spy->status = CHANSPY_STOP;
-                       if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
-                               ast_cond_signal(&spy->trigger);
-               }
-               ast_mutex_unlock(&spy->lock);
+                       ast_mutex_unlock(&spy->lock);
+                       AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
+                       spy_detach(spy, chan);
+               } else
+                       ast_mutex_unlock(&spy->lock);
        }
+       AST_LIST_TRAVERSE_SAFE_END
+       spy_cleanup(chan);
 }
 
 void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
@@ -1235,62 +1274,54 @@ void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
 
 void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
 {
-       struct ast_frame *f;
-
        if (!chan->spies)
                return;
 
        AST_LIST_REMOVE(&chan->spies->list, spy, list);
+       spy_detach(spy, chan);
+       spy_cleanup(chan);
+}
 
-       ast_mutex_lock(&spy->lock);
+void ast_channel_spy_free(struct ast_channel_spy *spy)
+{
+       struct ast_frame *f = NULL;
+
+       if (spy->status == CHANSPY_DONE)
+               return;
 
-       spy->chan = NULL;
+       /* Switch status to done in case we get called twice */
+       spy->status = CHANSPY_DONE;
 
-       while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
-               ast_frfree(f);
-       
+       /* Drop any frames in the queue */
        while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
                ast_frfree(f);
+       while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
+               ast_frfree(f);
 
+       /* Destroy the condition if in use */
        if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
                ast_cond_destroy(&spy->trigger);
 
-       ast_mutex_unlock(&spy->lock);
+       /* Destroy our mutex since it is no longer in use */
+       ast_mutex_destroy(&spy->lock);
 
-       ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n",
-               spy->type, chan->name);
-
-       if (AST_LIST_EMPTY(&chan->spies->list)) {
-               if (chan->spies->read_translator.path)
-                       ast_translator_free_path(chan->spies->read_translator.path);
-               if (chan->spies->write_translator.path)
-                       ast_translator_free_path(chan->spies->write_translator.path);
-               free(chan->spies);
-               chan->spies = NULL;
-       }
+       return;
 }
 
 static void detach_spies(struct ast_channel *chan)
 {
-       struct ast_channel_spy *spy;
+       struct ast_channel_spy *spy = NULL;
 
        if (!chan->spies)
                return;
 
-       /* Marking the spies as done is sufficient.  Chanspy or spy users will get the picture. */
-       AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
-               ast_mutex_lock(&spy->lock);
-               spy->chan = NULL;
-               if (spy->status == CHANSPY_RUNNING)
-                       spy->status = CHANSPY_DONE;
-               if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
-                       ast_cond_signal(&spy->trigger);
-               ast_mutex_unlock(&spy->lock);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
+               AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
+               spy_detach(spy, chan);
        }
+       AST_LIST_TRAVERSE_SAFE_END
 
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list)
-               ast_channel_spy_remove(chan, spy);
-       AST_LIST_TRAVERSE_SAFE_END;
+       spy_cleanup(chan);
 }
 
 /*! \brief Softly hangup a channel, don't lock */