summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2017-01-31 16:32:18 -0600
committerRichard Mudgett <rmudgett@digium.com>2017-02-02 13:02:03 -0600
commit72e3fc58455e08307a06e57099c8362879688ea0 (patch)
tree5317a774bdc20a7d305b43e5abe00274afc0530b /main
parent3c558b4b4a0b4b8943a5d0231ffab6d6b6d9878f (diff)
Frame deferral: Revert API refactoring.
There are several issues with deferring frames that are caused by the refactoring. 1) The code deferring frames mishandles adding a deferred frame to the deferred queue. As a result the deferred queue can only be one frame long. 2) Deferrable frames can come directly from the channel driver as well as the read queue. These frames need to be added to the deferred queue. 3) Whoever is deferring frames is really only doing the __ast_read() to collect deferred frames and doesn't care about the returned frames except to detect a hangup event. When frame deferral is completed we must make the normal frame processing see the hangup as a frame anyway. As such, there is no need to have varying hangup frame deferral methods. We also need to be aware of the AST_SOFTHANGUP_ASYNCGOTO hangup that isn't real. That fake hangup is to cause the PBX thread to break out of loops to go execute a new dialplan location. 4) To properly deal with deferrable frames from the channel driver as pointed out by (2) above, means that it is possible to process a dialplan interception routine while frames are deferred because of the AST_CONTROL_READ_ACTION control frame. Deferring frames is not implemented as a re-entrant operation so you could have the unsupported case of two sections of code thinking they have control of the media stream. A worse problem is because of the bad implementation of the AMI PlayDTMF action. It can cause two threads to be deferring frames on the same channel at the same time. (ASTERISK_25940) * Rather than fix all these problems simply revert the API refactoring as there is going to be only autoservice and safe_sleep deferring frames anyway. ASTERISK-26343 ASTERISK-26716 #close Change-Id: I45069c779aa3a35b6c863f65245a6df2c7865496
Diffstat (limited to 'main')
-rw-r--r--main/autoservice.c66
-rw-r--r--main/channel.c106
-rw-r--r--main/channel_internal_api.c6
3 files changed, 89 insertions, 89 deletions
diff --git a/main/autoservice.c b/main/autoservice.c
index c3f24276c..11c9eab96 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -59,6 +59,10 @@ struct asent {
unsigned int use_count;
unsigned int orig_end_dtmf_flag: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. */
+ AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
AST_LIST_ENTRY(asent) list;
};
@@ -73,13 +77,19 @@ 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,
+ };
while (!asexit) {
struct ast_channel *mons[MAX_AUTOMONS];
+ struct asent *ents[MAX_AUTOMONS];
struct ast_channel *chan;
struct asent *as;
- int x = 0, ms = 50;
+ int i, x = 0, ms = 50;
struct ast_frame *f = NULL;
+ struct ast_frame *defer_frame = NULL;
AST_LIST_LOCK(&aslist);
@@ -94,6 +104,7 @@ static void *autoservice_run(void *ign)
AST_LIST_TRAVERSE(&aslist, as, list) {
if (!ast_check_hangup(as->chan)) {
if (x < MAX_AUTOMONS) {
+ ents[x] = as;
mons[x++] = as->chan;
} else {
ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
@@ -121,9 +132,51 @@ static void *autoservice_run(void *ign)
ast_callid_threadassoc_change(callid);
f = ast_read(chan);
- if (f) {
+
+ if (!f) {
+ /* No frame means the channel has been hung up.
+ * A hangup frame needs to be queued here as ast_waitfor() may
+ * never return again for the condition to be detected outside
+ * of autoservice. So, we'll leave a HANGUP queued up so the
+ * thread in charge of this channel will know. */
+
+ 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;
}
+
+ 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);
+ }
+ } else {
+ 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;
+ }
+ /* 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);
@@ -162,7 +215,6 @@ int ast_autoservice_start(struct ast_channel *chan)
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(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
- ast_channel_start_defer_frames(chan, 1);
ast_channel_unlock(chan);
AST_LIST_LOCK(&aslist);
@@ -196,6 +248,7 @@ int ast_autoservice_stop(struct ast_channel *chan)
{
int res = -1;
struct asent *as, *removed = NULL;
+ struct ast_frame *f;
int chan_list_state;
AST_LIST_LOCK(&aslist);
@@ -247,7 +300,12 @@ int ast_autoservice_stop(struct ast_channel *chan)
}
ast_channel_lock(chan);
- ast_channel_stop_defer_frames(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);
+ }
+ ast_frfree(f);
+ }
ast_channel_unlock(chan);
ast_free(as);
diff --git a/main/channel.c b/main/channel.c
index 6e88a2906..7c8d3a989 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1062,25 +1062,6 @@ struct ast_channel *__ast_dummy_channel_alloc(const char *file, int line, const
return tmp;
}
-void ast_channel_start_defer_frames(struct ast_channel *chan, int defer_hangups)
-{
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES);
- ast_set2_flag(ast_channel_flags(chan), defer_hangups, AST_FLAG_DEFER_HANGUP_FRAMES);
-}
-
-void ast_channel_stop_defer_frames(struct ast_channel *chan)
-{
- struct ast_frame *f;
-
- ast_clear_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES);
-
- /* Move the deferred frames onto the channel read queue, ahead of other queued frames */
- while ((f = AST_LIST_REMOVE_HEAD(ast_channel_deferred_readq(chan), frame_list))) {
- ast_queue_frame_head(chan, f);
- ast_frfree(f);
- }
-}
-
static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after)
{
struct ast_frame *f;
@@ -1545,18 +1526,19 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
int res = 0;
struct timeval start;
int ms;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
+
+ AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
/* If no other generator is present, start silencegen while waiting */
if (ast_opt_transmit_silence && !ast_channel_generatordata(chan)) {
silgen = ast_channel_start_silence_generator(chan);
}
- ast_channel_lock(chan);
- ast_channel_start_defer_frames(chan, 0);
- ast_channel_unlock(chan);
-
start = ast_tvnow();
while ((ms = ast_remaining_ms(start, timeout_ms))) {
+ struct ast_frame *dup_f = NULL;
+
if (cond && ((*cond)(data) == 0)) {
break;
}
@@ -1571,7 +1553,18 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
res = -1;
break;
}
- ast_frfree(f);
+
+ if (!ast_is_deferrable_frame(f)) {
+ ast_frfree(f);
+ continue;
+ }
+
+ if ((dup_f = ast_frisolate(f))) {
+ if (dup_f != f) {
+ ast_frfree(f);
+ }
+ AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
+ }
}
}
@@ -1580,8 +1573,17 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
ast_channel_stop_silence_generator(chan, silgen);
}
+ /* We need to free all the deferred frames, but we only need to
+ * queue the deferred frames if there was no error and no
+ * hangup was received
+ */
ast_channel_lock(chan);
- ast_channel_stop_defer_frames(chan);
+ while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
+ if (!res) {
+ ast_queue_frame_head(chan, f);
+ }
+ ast_frfree(f);
+ }
ast_channel_unlock(chan);
return res;
@@ -3883,36 +3885,6 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
if (!AST_LIST_EMPTY(ast_channel_readq(chan))) {
int skip_dtmf = should_skip_dtmf(chan);
- if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES)) {
- AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {
- if (ast_is_deferrable_frame(f)) {
- if(f->frametype == AST_FRAME_CONTROL &&
- (f->subclass.integer == AST_CONTROL_HANGUP ||
- f->subclass.integer == AST_CONTROL_END_OF_Q)) {
- /* Hangup is a special case. We want to defer the frame, but we also do not
- * want to remove it from the frame queue. So rather than just moving the frame
- * over, we duplicate it and move the copy to the deferred readq.
- *
- * The reason for this? This way, whoever calls ast_read() will get a NULL return
- * immediately and can tell the channel has hung up and do what it needs to. Also,
- * when frame deferral finishes, then whoever calls ast_read() next will also get
- * the hangup.
- */
- if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_HANGUP_FRAMES)) {
- struct ast_frame *dup;
-
- dup = ast_frdup(f);
- AST_LIST_INSERT_HEAD(ast_channel_deferred_readq(chan), dup, frame_list);
- }
- } else {
- AST_LIST_INSERT_HEAD(ast_channel_deferred_readq(chan), f, frame_list);
- AST_LIST_REMOVE_CURRENT(frame_list);
- }
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- }
-
AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {
/* We have to be picky about which frame we pull off of the readq because
* there are cases where we want to leave DTMF frames on the queue until
@@ -10361,15 +10333,9 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc
ast_party_connected_line_copy(ast_channel_connected(macro_chan), connected);
}
- ast_channel_start_defer_frames(macro_chan, 0);
ast_channel_unlock(macro_chan);
retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args);
-
- ast_channel_lock(macro_chan);
- ast_channel_stop_defer_frames(macro_chan);
- ast_channel_unlock(macro_chan);
-
if (!retval) {
struct ast_party_connected_line saved_connected;
@@ -10417,15 +10383,9 @@ int ast_channel_redirecting_macro(struct ast_channel *autoservice_chan, struct a
ast_party_redirecting_copy(ast_channel_redirecting(macro_chan), redirecting);
}
- ast_channel_start_defer_frames(macro_chan, 0);
ast_channel_unlock(macro_chan);
retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args);
-
- ast_channel_lock(macro_chan);
- ast_channel_stop_defer_frames(macro_chan);
- ast_channel_unlock(macro_chan);
-
if (!retval) {
struct ast_party_redirecting saved_redirecting;
@@ -10466,15 +10426,9 @@ int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct
ast_party_connected_line_copy(ast_channel_connected(sub_chan), connected);
}
- ast_channel_start_defer_frames(sub_chan, 0);
ast_channel_unlock(sub_chan);
retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0);
-
- ast_channel_lock(sub_chan);
- ast_channel_stop_defer_frames(sub_chan);
- ast_channel_unlock(sub_chan);
-
if (!retval) {
struct ast_party_connected_line saved_connected;
@@ -10515,15 +10469,9 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast
ast_party_redirecting_copy(ast_channel_redirecting(sub_chan), redirecting);
}
- ast_channel_start_defer_frames(sub_chan, 0);
ast_channel_unlock(sub_chan);
retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0);
-
- ast_channel_lock(sub_chan);
- ast_channel_stop_defer_frames(sub_chan);
- ast_channel_unlock(sub_chan);
-
if (!retval) {
struct ast_party_redirecting saved_redirecting;
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index 691dec0a1..a0cbe8643 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -221,7 +221,6 @@ struct ast_channel {
struct stasis_cp_single *topics; /*!< Topic for all channel's events */
struct stasis_forward *endpoint_forward; /*!< Subscription for event forwarding to endpoint's topic */
struct stasis_forward *endpoint_cache_forward; /*!< Subscription for cache updates to endpoint's topic */
- struct ast_readq_list deferred_readq;
};
/*! \brief The monotonically increasing integer counter for channel uniqueids */
@@ -1730,8 +1729,3 @@ enum ast_channel_error ast_channel_internal_errno(void)
return *error_code;
}
-
-struct ast_readq_list *ast_channel_deferred_readq(struct ast_channel *chan)
-{
- return &chan->deferred_readq;
-}