Make the difference clear about what the responsibilities of the core and a spy are when it comes to spying on a channel. The core is responsible for adding a spy to a channel, feeding frames into the spy, removing the spy from the channel, and notifying the spy that is has been detached. The spy is responsible for reading frames in, and cleaning itself up. Each side will not try to do the other's job.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@41959 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Joshua Colp
2006-09-03 23:30:37 +00:00
parent 4de3c0e447
commit 55d594e9da
4 changed files with 94 additions and 83 deletions

View File

@@ -211,21 +211,6 @@ static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, s
return res; 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 /* Map 'volume' levels from -4 through +4 into
decibel (dB) settings for channel drivers 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) && while ((res = ast_waitfor(chan, -1) > -1) &&
csth.spy.status == CHANSPY_RUNNING && csth.spy.status == CHANSPY_RUNNING &&
!ast_check_hangup(chan) &&
csth.spy.chan) { csth.spy.chan) {
if (!(f = ast_read(chan))) if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
running = -1;
break; break;
}
if (ast_test_flag(flags, OPTION_WHISPER) && if (ast_test_flag(flags, OPTION_WHISPER) &&
(f->frametype == AST_FRAME_VOICE)) { (f->frametype == AST_FRAME_VOICE)) {
@@ -381,13 +367,16 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
else else
ast_deactivate_generator(chan); 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) if (option_verbose >= 2)
ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
ast_mutex_destroy(&csth.spy.lock);
return running; return running;
} }
@@ -532,6 +521,8 @@ static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
peer = NULL; peer = NULL;
} }
} }
if (res == -1)
break;
} }
ast_clear_flag(chan, AST_FLAG_SPYING); ast_clear_flag(chan, AST_FLAG_SPYING);

View File

@@ -122,23 +122,6 @@ AST_APP_OPTIONS(mixmonitor_opts, {
AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME), 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) static int startmon(struct ast_channel *chan, struct ast_channel_spy *spy)
{ {
struct ast_channel *peer; struct ast_channel *peer;
@@ -176,9 +159,8 @@ static void *mixmonitor_thread(void *obj)
ast_channel_spy_trigger_wait(&mixmonitor->spy); 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; break;
}
while (1) { while (1) {
if (!(f = ast_channel_spy_read_frame(&mixmonitor->spy, SAMPLES_PER_FRAME))) 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); next = AST_LIST_NEXT(f, frame_list);
if (write) if (write)
ast_writestream(mixmonitor->fs, f); ast_writestream(mixmonitor->fs, f);
ast_frfree(f); ast_frame_free(f, 0);
} }
} }
} }
ast_mutex_unlock(&mixmonitor->spy.lock); ast_mutex_unlock(&mixmonitor->spy.lock);
stopmon(&mixmonitor->spy);
ast_channel_spy_free(&mixmonitor->spy);
if (option_verbose > 1) if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); 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_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
ast_safe_system(mixmonitor->post_process); ast_safe_system(mixmonitor->post_process);
} }
ast_mutex_destroy(&mixmonitor->spy.lock);
ast_closestream(mixmonitor->fs); ast_closestream(mixmonitor->fs);

View File

@@ -94,6 +94,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); 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. \brief Find all spies of a particular type on a channel and stop them.
\param chan The channel to operate on \param chan The channel to operate on

View File

@@ -1203,22 +1203,61 @@ int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
return 0; 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) 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) if (!chan->spies)
return; return;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) { AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
ast_mutex_lock(&spy->lock); ast_mutex_lock(&spy->lock);
if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) { if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
spy->status = CHANSPY_STOP; ast_mutex_unlock(&spy->lock);
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
ast_cond_signal(&spy->trigger); spy_detach(spy, chan);
} } else
ast_mutex_unlock(&spy->lock); ast_mutex_unlock(&spy->lock);
} }
AST_LIST_TRAVERSE_SAFE_END
spy_cleanup(chan);
} }
void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy) 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) void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
{ {
struct ast_frame *f;
if (!chan->spies) if (!chan->spies)
return; return;
AST_LIST_REMOVE(&chan->spies->list, spy, list); 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;
spy->chan = NULL; if (spy->status == CHANSPY_DONE)
return;
while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list))) /* Switch status to done in case we get called twice */
ast_frfree(f); spy->status = CHANSPY_DONE;
/* Drop any frames in the queue */
while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list))) while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
ast_frfree(f); 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) if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_destroy(&spy->trigger); 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", return;
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;
}
} }
static void detach_spies(struct ast_channel *chan) static void detach_spies(struct ast_channel *chan)
{ {
struct ast_channel_spy *spy; struct ast_channel_spy *spy = NULL;
if (!chan->spies) if (!chan->spies)
return; return;
/* Marking the spies as done is sufficient. Chanspy or spy users will get the picture. */ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) { AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
ast_mutex_lock(&spy->lock); spy_detach(spy, chan);
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_END
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) spy_cleanup(chan);
ast_channel_spy_remove(chan, spy);
AST_LIST_TRAVERSE_SAFE_END;
} }
/*! \brief Softly hangup a channel, don't lock */ /*! \brief Softly hangup a channel, don't lock */