Merge "translate: Skip matrix_rebuild during shutdown."
[asterisk/asterisk.git] / main / autoservice.c
index 4d53f15..cd7388b 100644 (file)
@@ -31,8 +31,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include <sys/time.h>
 #include <signal.h>
 
@@ -60,6 +58,7 @@ struct asent {
         *  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
@@ -72,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;
@@ -128,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) {
@@ -140,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;
+               for (i = 0; i < x; i++) {
+                       struct ast_frame *dup_f;
 
-                               if (mons[i] != chan) {
-                                       continue;
-                               }
+                       if (mons[i] != chan) {
+                               continue;
+                       }
 
-                               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);
-                                       }
+                       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);
                                }
+                       } 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;
+                                       }
 
-                               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);
+                               }
                        }
-               } 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;
@@ -221,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 {
@@ -285,11 +307,11 @@ int ast_autoservice_stop(struct ast_channel *chan)
                res = 0;
        }
 
+       ast_channel_lock(chan);
        if (!as->orig_end_dtmf_flag) {
                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))) {
                if (!((1 << f->frametype) & as->ignore_frame_types)) {
                        ast_queue_frame_head(chan, f);
@@ -298,11 +320,21 @@ int ast_autoservice_stop(struct ast_channel *chan)
        }
        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;
@@ -320,7 +352,19 @@ int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype)
        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);
 }