summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/channel.h49
-rw-r--r--main/autoservice.c66
-rw-r--r--main/channel.c106
-rw-r--r--main/channel_internal_api.c6
4 files changed, 89 insertions, 138 deletions
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 1b7246b98..9cf313fc4 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -967,16 +967,6 @@ enum {
* The channel is executing a subroutine or macro
*/
AST_FLAG_SUBROUTINE_EXEC = (1 << 27),
- /*!
- * The channel is currently in an operation where
- * frames should be deferred.
- */
- AST_FLAG_DEFER_FRAMES = (1 << 28),
- /*!
- * The channel is currently deferring hangup frames
- * in addition to other frame types.
- */
- AST_FLAG_DEFER_HANGUP_FRAMES = (1 << 29),
};
/*! \brief ast_bridge_config flags */
@@ -4727,43 +4717,4 @@ enum ast_channel_error {
*/
enum ast_channel_error ast_channel_errno(void);
-/*!
- * \brief Retrieve the deferred read queue.
- */
-struct ast_readq_list *ast_channel_deferred_readq(struct ast_channel *chan);
-
-/*!
- * \brief Start deferring deferrable frames on this channel
- *
- * Sometimes, a channel gets entered into a mode where a "main" application
- * is tasked with servicing frames on the channel, but that application does
- * not need to act on those frames. However, it would be imprudent to simply
- * drop important frames. This function can be called so that important frames
- * will be deferred, rather than placed in the channel frame queue as normal.
- *
- * Hangups are an interesting frame type. Hangups will always be detectable by
- * a reader when a channel is deferring frames. If the defer_hangups parameter
- * is non-zero, then the hangup frame will also be duplicated and deferred, so
- * that the next reader of the channel will get the hangup frame, too.
- *
- * \pre chan MUST be locked before calling
- *
- * \param chan The channel on which frames should be deferred
- * \param defer_hangups Defer hangups in addition to other deferrable frames
- */
-void ast_channel_start_defer_frames(struct ast_channel *chan, int defer_hangups);
-
-/*!
- * \brief Stop deferring deferrable frames on this channel
- *
- * When it is time to stop deferring frames on the channel, all deferred frames
- * will be queued onto the channel's read queue so that the next servicer of
- * the channel can handle those frames as necessary.
- *
- * \pre chan MUST be locked before calling
- *
- * \param chan The channel on which to stop deferring frames.
- */
-void ast_channel_stop_defer_frames(struct ast_channel *chan);
-
#endif /* _ASTERISK_CHANNEL_H */
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;
-}