Merge "translate: Skip matrix_rebuild during shutdown."
[asterisk/asterisk.git] / main / autoservice.c
index 271902f..cd7388b 100644 (file)
  *
  * \brief Automatic channel service routines
  *
- * \author Mark Spencer <markster@digium.com> 
+ * \author Mark Spencer <markster@digium.com>
  * \author Russell Bryant <russell@digium.com>
  */
 
-#include "asterisk.h"
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk.h"
 
 #include <sys/time.h>
 #include <signal.h>
@@ -52,10 +54,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 struct asent {
        struct ast_channel *chan;
        /*! This gets incremented each time autoservice gets started on the same
-        *  channel.  It will ensure that it doesn't actually get stopped until 
+        *  channel.  It will ensure that it doesn't actually get stopped until
         *  it gets stopped for the last time. */
        unsigned int use_count;
        unsigned int orig_end_dtmf_flag:1;
+       unsigned int video_update:1;
+       unsigned int ignore_frame_types;
        /*! Frames go on at the head of deferred_frames, so we have the frames
         *  from newest to oldest.  As we put them at the head of the readq, we'll
         *  end up with them in the right order for the channel's readq. */
@@ -67,17 +71,19 @@ static AST_LIST_HEAD_STATIC(aslist, asent);
 static ast_cond_t as_cond;
 
 static pthread_t asthread = AST_PTHREADT_NULL;
+static volatile int asexit = 0;
 
 static int as_chan_list_state;
 
 static void *autoservice_run(void *ign)
 {
+       ast_callid callid = 0;
        struct ast_frame hangup_frame = {
                .frametype = AST_FRAME_CONTROL,
                .subclass.integer = AST_CONTROL_HANGUP,
        };
 
-       for (;;) {
+       while (!asexit) {
                struct ast_channel *mons[MAX_AUTOMONS];
                struct asent *ents[MAX_AUTOMONS];
                struct ast_channel *chan;
@@ -123,6 +129,9 @@ static void *autoservice_run(void *ign)
                        continue;
                }
 
+               callid = ast_channel_callid(chan);
+               ast_callid_threadassoc_change(callid);
+
                f = ast_read(chan);
 
                if (!f) {
@@ -135,36 +144,54 @@ static void *autoservice_run(void *ign)
                        defer_frame = &hangup_frame;
                } else if (ast_is_deferrable_frame(f)) {
                        defer_frame = f;
+               } else {
+                       /* Can't defer. Discard and continue with next. */
+                       ast_frfree(f);
+                       continue;
                }
 
-               if (defer_frame) {
-                       for (i = 0; i < x; i++) {
-                               struct ast_frame *dup_f;
-                               
-                               if (mons[i] != chan) {
-                                       continue;
+               for (i = 0; i < x; i++) {
+                       struct ast_frame *dup_f;
+
+                       if (mons[i] != chan) {
+                               continue;
+                       }
+
+                       if (!f) { /* defer_frame == &hangup_frame */
+                               if ((dup_f = ast_frdup(defer_frame))) {
+                                       AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
                                }
-                               
-                               if (defer_frame != f) {
-                                       if ((dup_f = ast_frdup(defer_frame))) {
-                                               AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
-                                       }
-                               } else {
-                                       if ((dup_f = ast_frisolate(defer_frame))) {
-                                               if (dup_f != defer_frame) {
-                                                       ast_frfree(defer_frame);
-                                               }
-                                               AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
+                       } else {
+                               if (defer_frame->frametype == AST_FRAME_CONTROL &&
+                                       defer_frame->subclass.integer == AST_CONTROL_VIDUPDATE) {
+
+                                       /* If a video update is already queued don't needlessly queue another */
+                                       if (ents[i]->video_update) {
+                                               ast_frfree(defer_frame);
+                                               break;
                                        }
+
+                                       ents[i]->video_update = 1;
+                               }
+                               if ((dup_f = ast_frisolate(defer_frame))) {
+                                       AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
+                               }
+                               if (dup_f != defer_frame) {
+                                       ast_frfree(defer_frame);
                                }
-                               
-                               break;
                        }
-               } else if (f) {
-                       ast_frfree(f);
+
+                       break;
                }
+               /* The ast_waitfor_n() call will only read frames from
+                * the channels' file descriptors. If ast_waitfor_n()
+                * returns non-NULL, then one of the channels in the
+                * mons array must have triggered the return. It's
+                * therefore impossible that we got here while (i >= x).
+                * If we did, we'd need to ast_frfree(f) if (f). */
        }
 
+       ast_callid_threadassoc_change(0);
        asthread = AST_PTHREADT_NULL;
 
        return NULL;
@@ -191,15 +218,15 @@ int ast_autoservice_start(struct ast_channel *chan)
 
        if (!(as = ast_calloc(1, sizeof(*as))))
                return -1;
-       
+
        /* New entry created */
        as->chan = chan;
        as->use_count = 1;
 
        ast_channel_lock(chan);
-       as->orig_end_dtmf_flag = ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
+       as->orig_end_dtmf_flag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
        if (!as->orig_end_dtmf_flag)
-               ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
+               ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
        ast_channel_unlock(chan);
 
        AST_LIST_LOCK(&aslist);
@@ -216,7 +243,7 @@ int ast_autoservice_start(struct ast_channel *chan)
                        /* There will only be a single member in the list at this point,
                           the one we just added. */
                        AST_LIST_REMOVE(&aslist, as, list);
-                       free(as);
+                       ast_free(as);
                        asthread = AST_PTHREADT_NULL;
                        res = -1;
                } else {
@@ -246,7 +273,7 @@ int ast_autoservice_stop(struct ast_channel *chan)
 
        /* Find the entry, but do not free it because it still can be in the
           autoservice thread array */
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {       
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
                if (as->chan == chan) {
                        as->use_count--;
                        if (as->use_count < 1) {
@@ -276,27 +303,68 @@ int ast_autoservice_stop(struct ast_channel *chan)
        /* Now autoservice thread should have no references to our entry
           and we can safely destroy it */
 
-       if (!chan->_softhangup) {
+       if (!ast_channel_softhangup_internal_flag(chan)) {
                res = 0;
        }
 
+       ast_channel_lock(chan);
        if (!as->orig_end_dtmf_flag) {
-               ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+               ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
        }
 
-       ast_channel_lock(chan);
        while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
-               ast_queue_frame_head(chan, f);
+               if (!((1 << f->frametype) & as->ignore_frame_types)) {
+                       ast_queue_frame_head(chan, f);
+               }
                ast_frfree(f);
        }
        ast_channel_unlock(chan);
 
-       free(as);
+       ast_free(as);
+
+       return res;
+}
+
+void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
+{
+       if (chan && !ast_autoservice_start(chan)) {
+               ast_hangup(peer);
+               ast_autoservice_stop(chan);
+       } else {
+               ast_hangup(peer);
+       }
+}
 
+int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype)
+{
+       struct asent *as;
+       int res = -1;
+
+       AST_LIST_LOCK(&aslist);
+       AST_LIST_TRAVERSE(&aslist, as, list) {
+               if (as->chan == chan) {
+                       res = 0;
+                       as->ignore_frame_types |= (1 << ftype);
+                       break;
+               }
+       }
+       AST_LIST_UNLOCK(&aslist);
        return res;
 }
 
+static void autoservice_shutdown(void)
+{
+       pthread_t th = asthread;
+       asexit = 1;
+       if (th != AST_PTHREADT_NULL) {
+               ast_cond_signal(&as_cond);
+               pthread_kill(th, SIGURG);
+               pthread_join(th, NULL);
+       }
+}
+
 void ast_autoservice_init(void)
 {
+       ast_register_cleanup(autoservice_shutdown);
        ast_cond_init(&as_cond, NULL);
 }