[asterisk-commits] russell: trunk r190423 - in /trunk: ./ apps/ build_tools/ channels/ funcs/ in...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Apr 24 09:04:32 CDT 2009


Author: russell
Date: Fri Apr 24 09:04:26 2009
New Revision: 190423

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=190423
Log:
Convert the ast_channel data structure over to the astobj2 framework.

There is a lot that could be said about this, but the patch is a big 
improvement for performance, stability, code maintainability, 
and ease of future code development.

The channel list is no longer an unsorted linked list.  The main container 
for channels is an astobj2 hash table.  All of the code related to searching 
for channels or iterating active channels has been rewritten.  Let n be 
the number of active channels.  Iterating the channel list has gone from 
O(n^2) to O(n).  Searching for a channel by name went from O(n) to O(1).  
Searching for a channel by extension is still O(n), but uses a new method 
for doing so, which is more efficient.

The ast_channel object is now a reference counted object.  The benefits 
here are plentiful.  Some benefits directly related to issues in the 
previous code include:

1) When threads other than the channel thread owning a channel wanted 
   access to a channel, it had to hold the lock on it to ensure that it didn't 
   go away.  This is no longer a requirement.  Holding a reference is 
   sufficient.

2) There are places that now require less dealing with channel locks.

3) There are places where channel locks are held for much shorter periods 
   of time.

4) There are places where dealing with more than one channel at a time becomes 
   _MUCH_ easier.  ChanSpy is a great example of this.  Writing code in the 
   future that deals with multiple channels will be much easier.

Some additional information regarding channel locking and reference count 
handling can be found in channel.h, where a new section has been added that 
discusses some of the rules associated with it.

Mark Michelson also assisted with the development of this patch.  He did the 
conversion of ChanSpy and introduced a new API, ast_autochan, which makes it 
much easier to deal with holding on to a channel pointer for an extended period 
of time and having it get automatically updated if the channel gets masqueraded.
Mark was also a huge help in the code review process.

Thanks to David Vossel for his assistance with this branch, as well.  David 
did the conversion of the DAHDIScan application by making it become a wrapper 
for ChanSpy internally.

The changes come from the svn/asterisk/team/russell/ast_channel_ao2 branch.

Review: http://reviewboard.digium.com/r/203/

Added:
    trunk/include/asterisk/autochan.h   (with props)
    trunk/main/autochan.c   (with props)
Removed:
    trunk/apps/app_dahdiscan.c
Modified:
    trunk/CHANGES
    trunk/UPGRADE.txt
    trunk/apps/app_channelredirect.c
    trunk/apps/app_chanspy.c
    trunk/apps/app_directed_pickup.c
    trunk/apps/app_minivm.c
    trunk/apps/app_mixmonitor.c
    trunk/apps/app_senddtmf.c
    trunk/apps/app_softhangup.c
    trunk/apps/app_voicemail.c
    trunk/build_tools/cflags.xml
    trunk/channels/chan_agent.c
    trunk/channels/chan_bridge.c
    trunk/channels/chan_dahdi.c
    trunk/channels/chan_gtalk.c
    trunk/channels/chan_iax2.c
    trunk/channels/chan_local.c
    trunk/channels/chan_mgcp.c
    trunk/channels/chan_misdn.c
    trunk/channels/chan_sip.c
    trunk/channels/chan_unistim.c
    trunk/funcs/func_channel.c
    trunk/funcs/func_global.c
    trunk/funcs/func_logic.c
    trunk/funcs/func_odbc.c
    trunk/include/asterisk/channel.h
    trunk/include/asterisk/lock.h
    trunk/main/Makefile
    trunk/main/channel.c
    trunk/main/cli.c
    trunk/main/devicestate.c
    trunk/main/features.c
    trunk/main/logger.c
    trunk/main/manager.c
    trunk/main/pbx.c
    trunk/res/res_agi.c
    trunk/res/res_clioriginate.c
    trunk/res/res_monitor.c
    trunk/res/snmp/agent.c

Modified: trunk/CHANGES
URL: http://svn.digium.com/svn-view/asterisk/trunk/CHANGES?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Apr 24 09:04:26 2009
@@ -28,6 +28,10 @@
    regardless if the call has been answered or not.
  * Added functionality to the app_dial F() option to continue with execution
    at the current location when no parameters are provided.
+ * Added c() option to app_chanspy. This option allows custom DTMF to be set
+   to cycle through the next avaliable channel.  By default this is still '*'.
+ * Added x() option to app_chanspy.  This option allows DTMF to be set to
+   exit the application.
 
 Dialplan Functions
 ------------------

Modified: trunk/UPGRADE.txt
URL: http://svn.digium.com/svn-view/asterisk/trunk/UPGRADE.txt?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/UPGRADE.txt (original)
+++ trunk/UPGRADE.txt Fri Apr 24 09:04:26 2009
@@ -25,6 +25,9 @@
   If you are not using autoload=yes in modules.conf you will need to ensure
   it is set to load. If not, then any module which uses RTP (such as chan_sip)
   will not be able to send or receive calls.
+* The app_dahdiscan.c file has been removed, but the dialplan app DAHDIScan still 
+  remains. It now exists within app_chanspy.c and retains the exact same 
+  functionality as before. 
 
 From 1.6.1 to 1.6.2:
 

Modified: trunk/apps/app_channelredirect.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/apps/app_channelredirect.c?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/apps/app_channelredirect.c (original)
+++ trunk/apps/app_channelredirect.c Fri Apr 24 09:04:26 2009
@@ -86,8 +86,7 @@
 		return -1;
 	}
 
-	chan2 = ast_get_channel_by_name_locked(args.channel);
-	if (!chan2) {
+	if (!(chan2 = ast_channel_get_by_name(args.channel))) {
 		ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
 		pbx_builtin_setvar_helper(chan, "CHANNELREDIRECT_STATUS", "NOCHANNEL");
 		return 0;
@@ -96,9 +95,12 @@
 	if (chan2->pbx) {
 		ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
 	}
+
 	res = ast_async_parseable_goto(chan2, args.label);
+
+	chan2 = ast_channel_unref(chan2);
+
 	pbx_builtin_setvar_helper(chan, "CHANNELREDIRECT_STATUS", "SUCCESS");
-	ast_channel_unlock(chan2);
 
 	return res;
 }

Modified: trunk/apps/app_chanspy.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/apps/app_chanspy.c?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/apps/app_chanspy.c (original)
+++ trunk/apps/app_chanspy.c Fri Apr 24 09:04:26 2009
@@ -50,6 +50,7 @@
 #include "asterisk/module.h"
 #include "asterisk/lock.h"
 #include "asterisk/options.h"
+#include "asterisk/autochan.h"
 
 #define AST_NAME_STRLEN 256
 #define NUM_SPYGROUPS 128
@@ -140,6 +141,16 @@
 						specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
 						name of the last channel that was spied on will be stored
 						in the <variable>SPY_CHANNEL</variable> variable.</para>
+					</option>
+					<option name="x">
+						<argument name="digit" required="true">
+							<para>Specify a DTMF digit that can be used to exit the application.</para>
+						</argument>
+					</option>
+					<option name="c">
+						<argument name="digit" required="true">
+							<para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
+						</argument>
 					</option>
 					<option name="e">
 						<argument name="ext" required="true" />
@@ -262,6 +273,16 @@
 						name of the last channel that was spied on will be stored
 						in the <variable>SPY_CHANNEL</variable> variable.</para>
 					</option>
+					<option name="x">
+						<argument name="digit" required="true">
+							<para>Specify a DTMF digit that can be used to exit the application.</para>
+						</argument>
+					</option>
+					<option name="c">
+						<argument name="digit" required="true">
+							<para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
+						</argument>
+					</option>
 					<option name="e">
 						<argument name="ext" required="true" />
 						<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
@@ -287,11 +308,28 @@
 			<ref type="application">ChanSpy</ref>
 		</see-also>
 	</application>
-
+	
+	<application name="DAHDIScan" language="en_US">
+		<synopsis>
+			Scan DAHDI channels to monitor calls.
+		</synopsis>
+		<syntax>
+			<parameter name="group">
+				<para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Allows a call center manager to monitor DAHDI channels in a
+			convenient way.  Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
+		</description>
+	</application>
  ***/
+
 static const char *app_chan = "ChanSpy";
 
 static const char *app_ext = "ExtenSpy";
+
+static const char *app_dahdiscan = "DAHDIScan";
 
 enum {
 	OPTION_QUIET             = (1 << 0),    /* Quiet, no announcement */
@@ -307,7 +345,10 @@
 	OPTION_NOTECH            = (1 << 10),   /* Skip technology name playback */
 	OPTION_BARGE             = (1 << 11),   /* Barge mode (whisper to both channels) */
 	OPTION_NAME              = (1 << 12),   /* Say the name of the person on whom we will spy */
-	OPTION_DTMF_SWITCH_MODES = (1 << 13),   /*Allow numeric DTMF to switch between chanspy modes */
+	OPTION_DTMF_SWITCH_MODES = (1 << 13),   /* Allow numeric DTMF to switch between chanspy modes */
+	OPTION_DTMF_EXIT         = (1 << 14),	/* Set DTMF to exit, added for DAHDIScan integration */
+	OPTION_DTMF_CYCLE        = (1 << 15),	/* Custom DTMF for cycling next avaliable channel, (default is '*') */
+	OPTION_DAHDI_SCAN        = (1 << 16),	/* Scan groups in DAHDIScan mode */
 } chanspy_opt_flags;
 
 enum {
@@ -316,6 +357,8 @@
 	OPT_ARG_RECORD,
 	OPT_ARG_ENFORCED,
 	OPT_ARG_NAME,
+	OPT_ARG_EXIT,
+	OPT_ARG_CYCLE,
 	OPT_ARG_ARRAY_SIZE,
 } chanspy_opt_args;
 
@@ -334,9 +377,9 @@
 	AST_APP_OPTION('s', OPTION_NOTECH),
 	AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
 	AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
+	AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT),
+	AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
 });
-
-static int next_unique_id_to_use = 0;
 
 struct chanspy_translation_helper {
 	/* spy data */
@@ -347,6 +390,12 @@
 	int volfactor;
 };
 
+struct spy_dtmf_options {
+	char exit;
+	char cycle;
+	char volume;
+};
+
 static void *spy_alloc(struct ast_channel *chan, void *data)
 {
 	/* just store the data pointer in the channel structure */
@@ -399,26 +448,20 @@
 	.generate = spy_generate,
 };
 
-static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
+static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
 {
 	int res = 0;
 	struct ast_channel *peer = NULL;
 
-	ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
-
-	res = ast_audiohook_attach(chan, audiohook);
-
-	if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
+	ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, autochan->chan->name);
+
+	res = ast_audiohook_attach(autochan->chan, audiohook);
+
+	if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
 		ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
 	}
 	return res;
 }
-
-struct chanspy_ds {
-	struct ast_channel *chan;
-	char unique_id[20];
-	ast_mutex_t lock;
-};
 
 static void change_spy_mode(const char digit, struct ast_flags *flags)
 {
@@ -434,8 +477,9 @@
 	}
 }
 
-static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, 
-	int *volfactor, int fd, struct ast_flags *flags, char *exitcontext) 
+static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
+	int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
+	char *exitcontext)
 {
 	struct chanspy_translation_helper csth;
 	int running = 0, res, x = 0;
@@ -443,32 +487,22 @@
 	char *name;
 	struct ast_frame *f;
 	struct ast_silence_generator *silgen = NULL;
-	struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
+	struct ast_autochan *spyee_bridge_autochan = NULL;
 	const char *spyer_name;
 
 	ast_channel_lock(chan);
 	spyer_name = ast_strdupa(chan->name);
 	ast_channel_unlock(chan);
 
-	ast_mutex_lock(&spyee_chanspy_ds->lock);
-	if (spyee_chanspy_ds->chan) {
-		spyee = spyee_chanspy_ds->chan;
-		ast_channel_lock(spyee);
-	}
-	ast_mutex_unlock(&spyee_chanspy_ds->lock);
-
-	if (!spyee) {
+	/* We now hold the channel lock on spyee */
+
+	if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) {
 		return 0;
 	}
 
-	/* We now hold the channel lock on spyee */
-
-	if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
-		ast_channel_unlock(spyee);
-		return 0;
-	}
-
-	name = ast_strdupa(spyee->name);
+	ast_channel_lock(spyee_autochan->chan);
+	name = ast_strdupa(spyee_autochan->chan->name);
+	ast_channel_unlock(spyee_autochan->chan);
 
 	ast_verb(2, "Spying on channel %s\n", name);
 	manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
@@ -480,26 +514,23 @@
 
 	ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
 
-	if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
+	if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
 		ast_audiohook_destroy(&csth.spy_audiohook);
-		ast_channel_unlock(spyee);
 		return 0;
 	}
 
- 	ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
+	ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
 	ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
-  	if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
-		ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
-	}
-	if ((spyee_bridge = ast_bridged_channel(spyee))) {
-		ast_channel_lock(spyee_bridge);
-		if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
-			ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
-		}
-		ast_channel_unlock(spyee_bridge);
-	}
-	ast_channel_unlock(spyee);
-	spyee = NULL;
+	if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
+		ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
+	}
+	if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
+		ast_channel_lock(spyee_bridge_autochan->chan);
+		if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
+			ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
+		}
+		ast_channel_unlock(spyee_bridge_autochan->chan);
+	}
 
 	ast_channel_lock(chan);
 	ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
@@ -589,10 +620,13 @@
 			}
 		}
 
-		if (res == '*') {
+		if (res == user_options->cycle) {
 			running = 0;
 			break;
-		} else if (res == '#') {
+		} else if (res == user_options->exit) {
+			running = -2;
+			break;
+		} else if (res == user_options->volume) {
 			if (!ast_strlen_zero(inp)) {
 				running = atoi(inp);
 				break;
@@ -632,128 +666,45 @@
 	ast_audiohook_detach(&csth.spy_audiohook);
 	ast_audiohook_unlock(&csth.spy_audiohook);
 	ast_audiohook_destroy(&csth.spy_audiohook);
-	
+
+	if (spyee_bridge_autochan) {
+		ast_autochan_destroy(spyee_bridge_autochan);
+	}
+
 	ast_verb(2, "Done Spying on channel %s\n", name);
 	manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
 
 	return running;
 }
 
-/*!
- * \note This relies on the embedded lock to be recursive, as it may be called
- * due to a call to chanspy_ds_free with the lock held there.
- */
-static void chanspy_ds_destroy(void *data)
-{
-	struct chanspy_ds *chanspy_ds = data;
-
-	/* Setting chan to be NULL is an atomic operation, but we don't want this
-	 * value to change while this lock is held.  The lock is held elsewhere
-	 * while it performs non-atomic operations with this channel pointer */
-
-	ast_mutex_lock(&chanspy_ds->lock);
-	chanspy_ds->chan = NULL;
-	ast_mutex_unlock(&chanspy_ds->lock);
-}
-
-static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
-	struct chanspy_ds *chanspy_ds = data;
-	
-	ast_mutex_lock(&chanspy_ds->lock);
-	chanspy_ds->chan = new_chan;
-	ast_mutex_unlock(&chanspy_ds->lock);
-}
-
-static const struct ast_datastore_info chanspy_ds_info = {
-	.type = "chanspy",
-	.destroy = chanspy_ds_destroy,
-	.chan_fixup = chanspy_ds_chan_fixup,
-};
-
-static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
-{
-	if (!chanspy_ds)
-		return NULL;
-
-	ast_mutex_lock(&chanspy_ds->lock);
-	if (chanspy_ds->chan) {
-		struct ast_datastore *datastore;
-		struct ast_channel *chan;
-
-		chan = chanspy_ds->chan;
-
-		ast_channel_lock(chan);
-		if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
-			ast_channel_datastore_remove(chan, datastore);
-			/* chanspy_ds->chan is NULL after this call */
-			chanspy_ds_destroy(datastore->data);
-			datastore->data = NULL;
-			ast_datastore_free(datastore);
-		}
-		ast_channel_unlock(chan);
-	}
-	ast_mutex_unlock(&chanspy_ds->lock);
-
-	return NULL;
-}
-
-/*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
-static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
-{
-	struct ast_datastore *datastore = NULL;
-
-	ast_mutex_lock(&chanspy_ds->lock);
-
-	if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
-		ast_mutex_unlock(&chanspy_ds->lock);
-		chanspy_ds = chanspy_ds_free(chanspy_ds);
-		ast_channel_unlock(chan);
-		return NULL;
-	}
-	
-	chanspy_ds->chan = chan;
-	datastore->data = chanspy_ds;
-	ast_channel_datastore_add(chan, datastore);
-
-	return chanspy_ds;
-}
-
-static struct chanspy_ds *next_channel(struct ast_channel *chan,
-	const struct ast_channel *last, const char *spec,
-	const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
+static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
+		struct ast_autochan *autochan, struct ast_channel *chan)
 {
 	struct ast_channel *next;
 	const size_t pseudo_len = strlen("DAHDI/pseudo");
 
+	if (!iter) {
+		return NULL;
+	}
+
 redo:
-	if (!ast_strlen_zero(spec))
-		next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
-	else if (!ast_strlen_zero(exten))
-		next = ast_walk_channel_by_exten_locked(last, exten, context);
-	else
-		next = ast_channel_walk_locked(last);
-
-	if (!next)
+	if (!(next = ast_channel_iterator_next(iter))) {
 		return NULL;
+	}
 
 	if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
-		last = next;
-		ast_channel_unlock(next);
 		goto redo;
 	} else if (next == chan) {
-		last = next;
-		ast_channel_unlock(next);
 		goto redo;
 	}
 
-	return setup_chanspy_ds(next, chanspy_ds);
+	return ast_autochan_setup(next);
 }
 
 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
-	int volfactor, const int fd, const char *mygroup, const char *myenforced,
-	const char *spec, const char *exten, const char *context, const char *mailbox,
-	const char *name_context)
+	int volfactor, const int fd, struct spy_dtmf_options *user_options,
+	const char *mygroup, const char *myenforced, const char *spec, const char *exten,
+	const char *context, const char *mailbox, const char *name_context)
 {
 	char nameprefix[AST_NAME_STRLEN];
 	char peer_name[AST_NAME_STRLEN + 5];
@@ -764,7 +715,7 @@
 	char *ptr;
 	int num;
 	int num_spyed_upon = 1;
-	struct chanspy_ds chanspy_ds = { 0, };
+	struct ast_channel_iterator *iter = NULL;
 
 	if (ast_test_flag(flags, OPTION_EXIT)) {
 		const char *c;
@@ -779,10 +730,6 @@
 		ast_channel_unlock(chan);
 	}
 
-	ast_mutex_init(&chanspy_ds.lock);
-
-	snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
-
 	if (chan->_state != AST_STATE_UP)
 		ast_answer(chan);
 
@@ -791,8 +738,8 @@
 	waitms = 100;
 
 	for (;;) {
-		struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
-		struct ast_channel *prev = NULL, *peer = NULL;
+		struct ast_autochan *autochan = NULL, *next_autochan = NULL;
+		struct ast_channel *prev = NULL;
 
 		if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
 			res = ast_streamfile(chan, "beep", chan->language);
@@ -813,6 +760,19 @@
 			}
 		}
 
+		/* Set up the iterator we'll be using during this call */
+		if (!ast_strlen_zero(spec)) {
+			iter = ast_channel_iterator_by_name_new(0, spec, strlen(spec));
+		} else if (!ast_strlen_zero(exten)) {
+			iter = ast_channel_iterator_by_exten_new(0, exten, context);
+		} else {
+			iter = ast_channel_iterator_all_new(0);
+		}
+
+		if (!iter) {
+			return -1;
+		}
+
 		res = ast_waitfordigit(chan, waitms);
 		if (res < 0) {
 			ast_clear_flag(chan, AST_FLAG_SPYING);
@@ -832,38 +792,30 @@
 		waitms = 100;
 		num_spyed_upon = 0;
 
-		for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
-		     peer_chanspy_ds;
-			 chanspy_ds_free(peer_chanspy_ds), prev = peer,
-		     peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
-			 	next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
+		for (autochan = next_channel(iter, autochan, chan);
+		     autochan;
+			 prev = autochan->chan, ast_autochan_destroy(autochan),
+		     autochan = next_autochan ? next_autochan : 
+				next_channel(iter, autochan, chan), next_autochan = NULL) {
 			int igrp = !mygroup;
 			int ienf = !myenforced;
 			char *s;
 
-			peer = peer_chanspy_ds->chan;
-
-			ast_mutex_unlock(&peer_chanspy_ds->lock);
-
-			if (peer == prev) {
-				ast_channel_unlock(peer);
-				chanspy_ds_free(peer_chanspy_ds);
+			if (autochan->chan == prev) {
+				ast_autochan_destroy(autochan);
 				break;
 			}
 
 			if (ast_check_hangup(chan)) {
-				ast_channel_unlock(peer);
-				chanspy_ds_free(peer_chanspy_ds);
+				ast_autochan_destroy(autochan);
 				break;
 			}
 
-			if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
-				ast_channel_unlock(peer);
+			if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
 				continue;
 			}
 
-			if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
-				ast_channel_unlock(peer);
+			if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
 				continue;
 			}
 
@@ -874,14 +826,22 @@
 				char dup_mygroup[512];
 				char *groups[NUM_SPYGROUPS];
 				char *mygroups[NUM_SPYGROUPS];
-				const char *group;
+				const char *group = NULL;
 				int x;
 				int y;
 				ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
 				num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
 					ARRAY_LEN(mygroups));
 
-				if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
+				/* Before dahdi scan was part of chanspy, it would use the "GROUP" variable 
+				 * rather than "SPYGROUP", this check is done to preserve expected behavior */
+				if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
+					group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
+				} else {
+					group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
+				}
+
+				if (!ast_strlen_zero(group)) {
 					ast_copy_string(dup_group, group, sizeof(dup_group));
 					num_groups = ast_app_separate_args(dup_group, ':', groups,
 						ARRAY_LEN(groups));
@@ -898,10 +858,8 @@
 			}
 
 			if (!igrp) {
-				ast_channel_unlock(peer);
 				continue;
 			}
-
 			if (myenforced) {
 				char ext[AST_CHANNEL_NAME + 3];
 				char buffer[512];
@@ -909,7 +867,7 @@
 
 				snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
 
-				ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
+				ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
 				if ((end = strchr(ext, '-'))) {
 					*end++ = ':';
 					*end = '\0';
@@ -927,18 +885,13 @@
 			}
 
 			strcpy(peer_name, "spy-");
-			strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
+			strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
 			ptr = strchr(peer_name, '/');
 			*ptr++ = '\0';
 			ptr = strsep(&ptr, "-");
 
 			for (s = peer_name; s < ptr; s++)
 				*s = tolower(*s);
-			/* We have to unlock the peer channel here to avoid a deadlock.
-			 * So, when we need to dereference it again, we have to lock the 
-			 * datastore and get the pointer from there to see if the channel 
-			 * is still valid. */
-			ast_channel_unlock(peer);
 
 			if (!ast_test_flag(flags, OPTION_QUIET)) {
 				if (ast_test_flag(flags, OPTION_NAME)) {
@@ -954,7 +907,7 @@
 								res = ast_waitstream(chan, "");
 							}
 							if (res) {
-								chanspy_ds_free(peer_chanspy_ds);
+								ast_autochan_destroy(autochan);
 								break;
 							}
 						} else {
@@ -966,42 +919,38 @@
 				}
 			}
 
-			res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
-			num_spyed_upon++;	
+			res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
+			num_spyed_upon++;
 
 			if (res == -1) {
-				chanspy_ds_free(peer_chanspy_ds);
+				ast_autochan_destroy(autochan);
 				goto exit;
 			} else if (res == -2) {
 				res = 0;
-				chanspy_ds_free(peer_chanspy_ds);
+				ast_autochan_destroy(autochan);
 				goto exit;
 			} else if (res > 1 && spec) {
 				struct ast_channel *next;
 
 				snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
 
-				if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
-					peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
-					next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
+				if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
+					next_autochan = ast_autochan_setup(next);
+					next = ast_channel_unref(next);
 				} else {
 					/* stay on this channel, if it is still valid */
-
-					ast_mutex_lock(&peer_chanspy_ds->lock);
-					if (peer_chanspy_ds->chan) {
-						ast_channel_lock(peer_chanspy_ds->chan);
-						next_chanspy_ds = peer_chanspy_ds;
-						peer_chanspy_ds = NULL;
+					if (!ast_check_hangup(autochan->chan)) {
+						next_autochan = ast_autochan_setup(autochan->chan);
 					} else {
 						/* the channel is gone */
-						ast_mutex_unlock(&peer_chanspy_ds->lock);
-						next_chanspy_ds = NULL;
+						next_autochan = NULL;
 					}
 				}
-
-				peer = NULL;
-			}
-		}
+			}
+		}
+
+		iter = ast_channel_iterator_destroy(iter);
+
 		if (res == -1 || ast_check_hangup(chan))
 			break;
 	}
@@ -1010,10 +959,6 @@
 	ast_clear_flag(chan, AST_FLAG_SPYING);
 
 	ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
-
-	ast_mutex_lock(&chanspy_ds.lock);
-	ast_mutex_unlock(&chanspy_ds.lock);
-	ast_mutex_destroy(&chanspy_ds.lock);
 
 	return res;
 }
@@ -1025,6 +970,11 @@
 	char *recbase = NULL;
 	int fd = 0;
 	struct ast_flags flags;
+	struct spy_dtmf_options user_options = {
+		.cycle = '*',
+		.volume = '#',
+		.exit = '\0',
+	};
 	int oldwf = 0;
 	int volfactor = 0;
 	int res;
@@ -1043,6 +993,7 @@
 		args.spec = NULL;
 
 	if (args.options) {
+		char tmp;
 		ast_app_parse_options(spy_opts, &flags, opts, args.options);
 		if (ast_test_flag(&flags, OPTION_GROUP))
 			mygroup = opts[OPT_ARG_GROUP];
@@ -1050,6 +1001,24 @@
 		if (ast_test_flag(&flags, OPTION_RECORD) &&
 			!(recbase = opts[OPT_ARG_RECORD]))
 			recbase = "chanspy";
+
+		if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
+			tmp = opts[OPT_ARG_EXIT][0];
+			if (strchr("0123456789*#", tmp) && tmp != '\0') {
+				user_options.exit = tmp;
+			} else {
+				ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
+			}
+		}
+
+		if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
+			tmp = opts[OPT_ARG_CYCLE][0];
+			if (strchr("0123456789*#", tmp) && tmp != '\0') {
+				user_options.cycle = tmp;
+			} else {
+				ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
+			}
+		}
 
 		if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
 			int vol;
@@ -1065,7 +1034,7 @@
 
 		if (ast_test_flag(&flags, OPTION_ENFORCED))
 			myenforced = opts[OPT_ARG_ENFORCED];
-		
+
 		if (ast_test_flag(&flags, OPTION_NAME)) {
 			if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
 				char *delimiter;
@@ -1078,10 +1047,9 @@
 				}
 			}
 		}
-
-
-	} else
+	} else {
 		ast_clear_flag(&flags, AST_FLAGS_ALL);
+	}
 
 	oldwf = chan->writeformat;
 	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
@@ -1099,7 +1067,7 @@
 		}
 	}
 
-	res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
+	res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
 
 	if (fd)
 		close(fd);
@@ -1117,6 +1085,11 @@
 	char *recbase = NULL;
 	int fd = 0;
 	struct ast_flags flags;
+	struct spy_dtmf_options user_options = {
+		.cycle = '*',
+		.volume = '#',
+		.exit = '\0',
+	};
 	int oldwf = 0;
 	int volfactor = 0;
 	int res;
@@ -1141,6 +1114,7 @@
 
 	if (args.options) {
 		char *opts[OPT_ARG_ARRAY_SIZE];
+		char tmp;
 
 		ast_app_parse_options(spy_opts, &flags, opts, args.options);
 		if (ast_test_flag(&flags, OPTION_GROUP))
@@ -1149,6 +1123,24 @@
 		if (ast_test_flag(&flags, OPTION_RECORD) &&
 			!(recbase = opts[OPT_ARG_RECORD]))
 			recbase = "chanspy";
+
+		if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
+			tmp = opts[OPT_ARG_EXIT][0];
+			if (strchr("0123456789*#", tmp) && tmp != '\0') {
+				user_options.exit = tmp;
+			} else {
+				ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
+			}
+		}
+
+		if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
+			tmp = opts[OPT_ARG_CYCLE][0];
+			if (strchr("0123456789*#", tmp) && tmp != '\0') {
+				user_options.cycle = tmp;
+			} else {
+				ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
+			}
+		}
 
 		if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
 			int vol;
@@ -1162,7 +1154,6 @@
 		if (ast_test_flag(&flags, OPTION_PRIVATE))
 			ast_set_flag(&flags, OPTION_WHISPER);
 
-		
 		if (ast_test_flag(&flags, OPTION_NAME)) {
 			if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
 				char *delimiter;
@@ -1176,8 +1167,9 @@
 			}
 		}
 
-	} else
+	} else {
 		ast_clear_flag(&flags, AST_FLAGS_ALL);
+	}
 
 	oldwf = chan->writeformat;
 	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
@@ -1196,10 +1188,46 @@
 	}
 
 
-	res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
+	res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
 
 	if (fd)
 		close(fd);
+
+	if (oldwf && ast_set_write_format(chan, oldwf) < 0)
+		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+
+	return res;
+}
+
+static int dahdiscan_exec(struct ast_channel *chan, void *data)
+{
+	const char *spec = "DAHDI";
+	struct ast_flags flags;
+	struct spy_dtmf_options user_options = {
+		.cycle = '#',
+		.volume = '\0',
+		.exit = '*',
+	};
+	int oldwf = 0;
+	int res;
+	char *mygroup = NULL;
+
+	ast_clear_flag(&flags, AST_FLAGS_ALL);
+
+	if (!ast_strlen_zero(data)) {
+		mygroup = ast_strdupa(data);
+	}
+	ast_set_flag(&flags, OPTION_DTMF_EXIT);
+	ast_set_flag(&flags, OPTION_DTMF_CYCLE);
+	ast_set_flag(&flags, OPTION_DAHDI_SCAN);
+
+	oldwf = chan->writeformat;
+	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+		return -1;
+	}
+
+	res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
 
 	if (oldwf && ast_set_write_format(chan, oldwf) < 0)
 		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
@@ -1213,6 +1241,7 @@
 
 	res |= ast_unregister_application(app_chan);
 	res |= ast_unregister_application(app_ext);
+	res |= ast_unregister_application(app_dahdiscan);
 
 	return res;
 }
@@ -1223,6 +1252,7 @@
 
 	res |= ast_register_application_xml(app_chan, chanspy_exec);
 	res |= ast_register_application_xml(app_ext, extenspy_exec);
+	res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
 
 	return res;
 }

Modified: trunk/apps/app_directed_pickup.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/apps/app_directed_pickup.c?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/apps/app_directed_pickup.c (original)
+++ trunk/apps/app_directed_pickup.c Fri Apr 24 09:04:26 2009
@@ -135,17 +135,35 @@
 		return 0;
 }
 
+struct pickup_by_name_args {
+	const char *name;
+	size_t len;
+};
+
+static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags)
+{
+	struct ast_channel *chan = obj;
+	struct pickup_by_name_args *args = data;
+
+	ast_channel_lock(chan);
+	if (!strncasecmp(chan->name, args->name, args->len) && can_pickup(chan)) {
+		/* Return with the channel still locked on purpose */
+		return CMP_MATCH | CMP_STOP;
+	}
+	ast_channel_unlock(chan);
+
+	return 0;
+}
+
 /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
 static struct ast_channel *my_ast_get_channel_by_name_locked(const char *channame)
 {
-	struct ast_channel *chan;
 	char *chkchan;
-	size_t channame_len, chkchan_len;
-
-	channame_len = strlen(channame);
-	chkchan_len = channame_len + 2;
-
- 	chkchan = alloca(chkchan_len);
+	struct pickup_by_name_args pickup_args;
+
+	pickup_args.len = strlen(channame) + 2;
+
+	chkchan = alloca(pickup_args.len);
 
 	/* need to append a '-' for the comparison so we check full channel name,
 	 * i.e SIP/hgc- , use a temporary variable so original stays the same for
@@ -154,15 +172,9 @@
 	strcpy(chkchan, channame);
 	strcat(chkchan, "-");
 
-	for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, channame_len);
-		 chan;
-		 chan = ast_walk_channel_by_name_prefix_locked(chan, channame, channame_len)) {
-		if (!strncasecmp(chan->name, chkchan, chkchan_len) && can_pickup(chan)) {
-			return chan;
-		}
-		ast_channel_unlock(chan);
-	}
-	return NULL;
+	pickup_args.name = chkchan;
+
+	return ast_channel_callback(pickup_by_name_cb, NULL, &pickup_args, 0);
 }
 
 /*! \brief Attempt to pick up specified channel named , does not use context */
@@ -171,76 +183,82 @@
 	int res = 0;
 	struct ast_channel *target;
 
-	if (!(target = my_ast_get_channel_by_name_locked(pickup)))
-		return -1;
+	if (!(target = my_ast_get_channel_by_name_locked(pickup))) {
+		return -1;
+	}
 
 	/* Just check that we are not picking up the SAME as target */
-	if (chan->name != target->name && chan != target) {
+	if (chan != target) {
 		res = pickup_do(chan, target);
 	}
+
 	ast_channel_unlock(target);
-
-	return res;
-}
-
-struct pickup_criteria {
-	const char *exten;
-	const char *context;
-};
-
-static int find_by_exten(struct ast_channel *c, void *data)
-{
-	struct pickup_criteria *info = data;
-
-	return (!strcasecmp(c->macroexten, info->exten) || !strcasecmp(c->exten, info->exten)) &&
-		!strcasecmp(c->dialcontext, info->context) &&
-		can_pickup(c);
+	target = ast_channel_unref(target);
+
+	return res;
 }
 
 /* Attempt to pick up specified extension with context */
 static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
 {
 	struct ast_channel *target = NULL;
-	struct pickup_criteria search = {
-		.exten = exten,
-		.context = context,
-	};
-
-	target = ast_channel_search_locked(find_by_exten, &search);
+	struct ast_channel_iterator *iter;
+	int res = -1;
+
+	if (!(iter = ast_channel_iterator_by_exten_new(0, exten, context))) {
+		return -1;
+	}
+
+	while ((target = ast_channel_iterator_next(iter))) {
+		ast_channel_lock(target);
+		if (can_pickup(target)) {
+			break;
+		}
+		ast_channel_unlock(target);
+		target = ast_channel_unref(target);
+	}
 
 	if (target) {
-		int res = pickup_do(chan, target);
+		res = pickup_do(chan, target);
 		ast_channel_unlock(target);
-		target = NULL;
-		return res;
-	}
-
-	return -1;
-}
-
-static int find_by_mark(struct ast_channel *c, void *data)
-{
+		target = ast_channel_unref(target);
+	}
+
+	return res;
+}
+
+static int find_by_mark(void *obj, void *arg, void *data, int flags)
+{
+	struct ast_channel *c = obj;
 	const char *mark = data;
 	const char *tmp;
-
-	return (tmp = pbx_builtin_getvar_helper(c, PICKUPMARK)) &&
+	int res;
+
+	ast_channel_lock(c);
+
+	res = (tmp = pbx_builtin_getvar_helper(c, PICKUPMARK)) &&
 		!strcasecmp(tmp, mark) &&
 		can_pickup(c);
+
+	ast_channel_unlock(c);
+
+	return res ? CMP_MATCH | CMP_STOP : 0;
 }
 
 /* Attempt to pick up specified mark */
 static int pickup_by_mark(struct ast_channel *chan, const char *mark)
 {
-	struct ast_channel *target = ast_channel_search_locked(find_by_mark, (char *) mark);
-
-	if (target) {
-		int res = pickup_do(chan, target);
+	struct ast_channel *target;
+	int res = -1;
+
+	if ((target = ast_channel_callback(find_by_mark, NULL, (char *) mark, 0))) {
+		ast_channel_lock(target);
+		res = pickup_do(chan, target);
 		ast_channel_unlock(target);
-		target = NULL;
-		return res;
-	}
-
-	return -1;
+		target = ast_channel_unref(target);
+	}
+
+	return res;
 }
 
 /* application entry point for Pickup() */

Modified: trunk/apps/app_minivm.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/apps/app_minivm.c?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/apps/app_minivm.c (original)
+++ trunk/apps/app_minivm.c Fri Apr 24 09:04:26 2009
@@ -1254,8 +1254,9 @@
 	ast_safe_system(tmp2);
 	ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
 	ast_debug(3, "Actual command used: %s\n", tmp2);
-	if (ast)
-		ast_channel_free(ast);
+	if (ast) {
+		ast = ast_channel_release(ast);
+	}
 	return 0;
 }
 

Modified: trunk/apps/app_mixmonitor.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/apps/app_mixmonitor.c?view=diff&rev=190423&r1=190422&r2=190423
==============================================================================
--- trunk/apps/app_mixmonitor.c (original)
+++ trunk/apps/app_mixmonitor.c Fri Apr 24 09:04:26 2009
@@ -45,6 +45,7 @@
 #include "asterisk/cli.h"
 #include "asterisk/app.h"
 #include "asterisk/channel.h"
+#include "asterisk/autochan.h"
 
 /*** DOCUMENTATION
 	<application name="MixMonitor" language="en_US">
@@ -138,7 +139,7 @@
 	char *post_process;
 	char *name;
 	unsigned int flags;
-	struct mixmonitor_ds *mixmonitor_ds;
+	struct ast_autochan *autochan;
 };
 
 enum {
@@ -164,50 +165,6 @@
 	AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
 });
 
-/* This structure is used as a means of making sure that our pointer to
- * the channel we are monitoring remains valid. This is very similar to 
- * what is used in app_chanspy.c.
- */
-struct mixmonitor_ds {
-	struct ast_channel *chan;
-	/* These condition variables are used to be sure that the channel
-	 * hangup code completes before the mixmonitor thread attempts to
-	 * free this structure. The combination of a bookean flag and a
-	 * ast_cond_t ensure that no matter what order the threads run in,
-	 * we are guaranteed to never have the waiting thread block forever
-	 * in the case that the signaling thread runs first.
-	 */
-	unsigned int destruction_ok;
-	ast_cond_t destruction_condition;
-	ast_mutex_t lock;
-};
-

[... 4225 lines stripped ...]



More information about the asterisk-commits mailing list