[asterisk-commits] russell: branch russell/ast_channel_ao2 r173855 - in /team/russell/ast_channe...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Feb 6 04:43:34 CST 2009


Author: russell
Date: Fri Feb  6 04:43:34 2009
New Revision: 173855

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=173855
Log:
Commit progress.  Still a lot of conversions to do ..

Modified:
    team/russell/ast_channel_ao2/apps/app_channelredirect.c
    team/russell/ast_channel_ao2/apps/app_minivm.c
    team/russell/ast_channel_ao2/apps/app_voicemail.c
    team/russell/ast_channel_ao2/channels/chan_agent.c
    team/russell/ast_channel_ao2/channels/chan_iax2.c
    team/russell/ast_channel_ao2/channels/chan_local.c
    team/russell/ast_channel_ao2/include/asterisk/channel.h
    team/russell/ast_channel_ao2/include/asterisk/lock.h
    team/russell/ast_channel_ao2/main/channel.c
    team/russell/ast_channel_ao2/main/cli.c
    team/russell/ast_channel_ao2/main/devicestate.c
    team/russell/ast_channel_ao2/main/features.c
    team/russell/ast_channel_ao2/main/logger.c
    team/russell/ast_channel_ao2/main/manager.c
    team/russell/ast_channel_ao2/main/pbx.c

Modified: team/russell/ast_channel_ao2/apps/app_channelredirect.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/apps/app_channelredirect.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/apps/app_channelredirect.c (original)
+++ team/russell/ast_channel_ao2/apps/app_channelredirect.c Fri Feb  6 04:43:34 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: team/russell/ast_channel_ao2/apps/app_minivm.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/apps/app_minivm.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/apps/app_minivm.c (original)
+++ team/russell/ast_channel_ao2/apps/app_minivm.c Fri Feb  6 04:43:34 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: team/russell/ast_channel_ao2/apps/app_voicemail.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/apps/app_voicemail.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/apps/app_voicemail.c (original)
+++ team/russell/ast_channel_ao2/apps/app_voicemail.c Fri Feb  6 04:43:34 2009
@@ -4035,7 +4035,7 @@
 			} else {
 				fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
 			}
-			ast_channel_free(ast);
+			ast = ast_channel_release(ast);
 		} else {
 			ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 		}
@@ -4084,7 +4084,7 @@
 			} else {
 				fprintf(p, "Subject: %s" ENDL, passdata);
 			}
-			ast_channel_free(ast);
+			ast = ast_channel_release(ast);
 		} else {
 			ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 		}
@@ -4156,7 +4156,7 @@
 			prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
 			pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
 			fprintf(p, "%s" ENDL, passdata);
-			ast_channel_free(ast);
+			ast = ast_channel_release(ast);
 		} else
 			ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 	} else if (msgnum > -1){
@@ -4300,7 +4300,7 @@
 			prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
 			pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
 			fprintf(p, "From: %s <%s>\n", passdata, who);
-			ast_channel_free(ast);
+			ast = ast_channel_release(ast);
 		} else 
 			ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 	} else
@@ -4316,7 +4316,7 @@
 			prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
 			pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
 			fprintf(p, "Subject: %s\n\n", passdata);
-			ast_channel_free(ast);
+			ast = ast_channel_release(ast);
 		} else
 			ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 	} else {
@@ -4338,7 +4338,7 @@
 			prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
 			pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
 			fprintf(p, "%s\n", passdata);
-			ast_channel_free(ast);
+			ast = ast_channel_release(ast);
 		} else
 			ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 	} else {

Modified: team/russell/ast_channel_ao2/channels/chan_agent.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/channels/chan_agent.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/channels/chan_agent.c (original)
+++ team/russell/ast_channel_ao2/channels/chan_agent.c Fri Feb  6 04:43:34 2009
@@ -463,8 +463,9 @@
 	/* Release ownership of the agent to other threads (presumably running the login app). */
 	p->app_lock_flag = 0;
 	ast_cond_signal(&p->app_complete_cond);
-	if (chan)
-		ast_channel_free(chan);
+	if (chan) {
+		chan = ast_channel_release(chan);
+	}
 	if (p->dead) {
 		ast_mutex_destroy(&p->lock);
 		ast_mutex_destroy(&p->app_lock);
@@ -1114,7 +1115,7 @@
 			p->owner = NULL;
 			tmp->tech_pvt = NULL;
 			p->app_sleep_cond = 1;
-			ast_channel_free( tmp );
+			tmp = ast_channel_release(tmp);
 			ast_mutex_unlock(&p->lock);	/* For other thread to read the condition. */
 			p->app_lock_flag = 0;
 			ast_cond_signal(&p->app_complete_cond);
@@ -1128,7 +1129,7 @@
 			p->owner = NULL;
 			tmp->tech_pvt = NULL;
 			p->app_sleep_cond = 1;
-			ast_channel_free( tmp );
+			tmp = ast_channel_release(tmp);
 			ast_mutex_unlock(&p->lock);     /* For other thread to read the condition. */
 			return NULL;
 		}	

Modified: team/russell/ast_channel_ao2/channels/chan_iax2.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/channels/chan_iax2.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/channels/chan_iax2.c (original)
+++ team/russell/ast_channel_ao2/channels/chan_iax2.c Fri Feb  6 04:43:34 2009
@@ -4383,7 +4383,7 @@
 		if (tmp) {
 			/* unlock and relock iaxsl[callno] to preserve locking order */
 			ast_mutex_unlock(&iaxsl[callno]);
-			ast_channel_free(tmp);
+			tmp = ast_channel_release(tmp);
 			ast_mutex_lock(&iaxsl[callno]);
 		}
 		return NULL;

Modified: team/russell/ast_channel_ao2/channels/chan_local.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/channels/chan_local.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/channels/chan_local.c (original)
+++ team/russell/ast_channel_ao2/channels/chan_local.c Fri Feb  6 04:43:34 2009
@@ -731,10 +731,12 @@
 		ama = 0;
 	if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum)) 
 			|| !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) {
-		if (tmp)
-			ast_channel_free(tmp);
-		if (tmp2)
-			ast_channel_free(tmp2);
+		if (tmp) {
+			tmp = ast_channel_release(tmp);
+		}
+		if (tmp2) {
+			tmp2 = ast_channel_release(tmp2);
+		}
 		ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
 		return NULL;
 	} 

Modified: team/russell/ast_channel_ao2/include/asterisk/channel.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/include/asterisk/channel.h?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/include/asterisk/channel.h (original)
+++ team/russell/ast_channel_ao2/include/asterisk/channel.h Fri Feb  6 04:43:34 2009
@@ -124,6 +124,7 @@
 #define _ASTERISK_CHANNEL_H
 
 #include "asterisk/abstract_jb.h"
+#include "asterisk/astobj2.h"
 
 #ifdef HAVE_SYS_POLL_H
 #include <sys/poll.h>
@@ -385,6 +386,57 @@
 	T38_STATE_NEGOTIATED,	/*!< T38 established */
 };
 
+/*!
+ * \page AstChannel ast_channel locking and reference tracking
+ *
+ * \par Creating Channels
+ * A channel is allocated using the ast_channel_alloc() function.  When created, it is
+ * automatically inserted into the main channels hash table that keeps track of all
+ * active channels in the system.  The hash key is based on the channel name.  Because
+ * of this, if you want to change the name, you _must_ use ast_change_name(), not change
+ * the name field directly.  When ast_channel_alloc() returns a channel pointer, you now
+ * hold a reference to that channel.  In most cases this reference is given to ast_pbx_run().
+ *
+ * \par Channel Locking
+ * There is a lock associated with every ast_channel.  It is allocated internally via astobj2.
+ * To lock or unlock a channel, you must use the ast_channel_lock() wrappers.
+ *
+ * Previously, before ast_channel was converted to astobj2, the channel lock was used in some
+ * additional ways that are no longer necessary.  Before, the only way to ensure that a channel
+ * did not disappear out from under you if you were working with a channel outside of the channel
+ * thread that owns it, was to hold the channel lock.  Now, that is no longer necessary.
+ * You simply must hold a reference to the channel to ensure it does not go away.
+ *
+ * The channel must be locked if you need to ensure that data that you reading from the channel
+ * does not change while you access it.  Further, you must hold the channel lock if you are
+ * making a non-atomic change to channel data.
+ *
+ * \par Channel References
+ * There are multiple ways to get a reference to a channel.  The first is that you hold a reference
+ * to a channel after creating it.  The other ways involve using the channel search or the channel
+ * traversal APIs.  These functions are the ast_channel_get_*() functions or ast_channel_iterator_*()
+ * functions.  Once a reference is retrieved by one of these methods, you know that the channel will
+ * not go away.  So, the channel should only get locked as needed for data access or modification.
+ * But, make sure that the reference gets released when you are done with it!
+ *
+ * There are different things you can do when you are done with a reference to a channel.  The first
+ * is to simple release the reference using ast_channel_unref().  The other option is to call
+ * ast_channel_release().  This function is generally used where ast_channel_free() was used in
+ * the past.  The release function releases a reference as well as ensures that the channel is no
+ * longer in the global channels container.  That way, the channel will get destroyed as soon as any
+ * other pending references get released.
+ *
+ * \par Exceptions to the rules
+ * Even though ast_channel is reference counted, there are some places where pointers to an ast_channel
+ * get stored, but the reference count does not reflect it.  The reason is mostly historical.
+ * The only places where this happens should be places where because of how the code works, we
+ * _know_ that the pointer to the channel will get removed before the channel goes away.  The main
+ * example of this is in channel drivers.  Channel drivers generally store a pointer to their owner
+ * ast_channel in their technology specific pvt struct.  In this case, the channel drivers _know_
+ * that this pointer to the channel will be removed in time, because the channel's hangup callback
+ * gets called before the channel goes away.
+ */
+
 /*! \brief Main Channel structure associated with a channel. 
  * This is the side of it mostly used by the pbx and call management.
  *
@@ -399,7 +451,6 @@
  *       and 8-byte fields causes 4 bytes of padding to be added before many
  *       8-byte fields.
  */
-
 struct ast_channel {
 	const struct ast_channel_tech *tech;		/*!< Technology (point to channel driver) */
 	void *tech_pvt;					/*!< Private data used by the technology driver */
@@ -444,7 +495,7 @@
 	
 	struct timeval whentohangup;        		/*!< Non-zero, set to actual time when channel is to be hung up */
 	pthread_t blocker;				/*!< If anyone is blocking, this is them */
-	ast_mutex_t lock_dont_use;			/*!< Lock a channel for some operations. See ast_channel_lock() */
+	ast_mutex_t _lock_dont_use;			/*!< Lock a channel for some operations. See ast_channel_lock() */
 	struct ast_callerid cid;			/*!< Caller ID, name, presentation etc */
 	struct ast_frame dtmff;				/*!< DTMF frame */
 	struct varshead varshead;			/*!< A linked list for channel variables. See \ref AstChanVar */
@@ -792,8 +843,15 @@
  */
 void ast_change_name(struct ast_channel *chan, char *newname);
 
-/*! \brief Free a channel structure */
-void  ast_channel_free(struct ast_channel *);
+/*!
+ * \brief Unlink and release reference to a channel
+ *
+ * This function will unlink the channel from the global channels container
+ * if it is still there and also release the current reference to the channel.
+ *
+ * \return NULL, convenient for clearing invalid pointers
+ */
+struct ast_channel *ast_channel_release(struct ast_channel *chan);
 
 /*! 
  * \brief Requests a channel 
@@ -929,6 +987,8 @@
  * \return Returns 0 if not, or 1 if hang up is requested (including time-out).
  */
 int ast_check_hangup(struct ast_channel *chan);
+
+int ast_check_hangup_locked(struct ast_channel *chan);
 
 /*! \brief Compare a offset with the settings of when to hang a channel up 
  * \param chan channel on which to check for hang up
@@ -1175,42 +1235,6 @@
  * \return the received text, or NULL to signify failure.
  */
 char *ast_recvtext(struct ast_channel *chan, int timeout);
-
-/*! \brief Browse channels in use
- * Browse the channels currently in use 
- * \param prev where you want to start in the channel list
- * \return Returns the next channel in the list, NULL on end.
- * 	If it returns a channel, that channel *has been locked*!
- */
-struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev);
-
-/*! \brief Get channel by name or uniqueid (locks channel) */
-struct ast_channel *ast_get_channel_by_name_locked(const char *chan);
-
-/*! \brief Get channel by name or uniqueid prefix (locks channel) */
-struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen);
-
-/*! \brief Get channel by name or uniqueid prefix (locks channel) */
-struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name, const int namelen);
-
-/*! \brief Get channel by exten (and optionally context) and lock it */
-struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context);
-
-/*! \brief Get next channel by exten (and optionally context) and lock it */
-struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
-						     const char *context);
-
-/*! \brief Search for a channel based on the passed channel matching callback
- * Search for a channel based on the specified is_match callback, and return the
- * first channel that we match.  When returned, the channel will be locked.  Note
- * that the is_match callback is called with the passed channel locked, and should
- * return 0 if there is no match, and non-zero if there is.
- * \param is_match callback executed on each channel until non-zero is returned, or we
- *        run out of channels to search.
- * \param data data passed to the is_match callback during each invocation.
- * \return Returns the matched channel, or NULL if no channel was matched.
- */
-struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel *, void *), void *data);
 
 /*! ! \brief Waits for a digit
  * \param c channel to wait for a digit on
@@ -1673,6 +1697,151 @@
         AST_LIST_ENTRY(ast_group_info) list;   
 };
 
+#define ast_channel_lock(chan) ao2_lock(chan)
+#define ast_channel_unlock(chan) ao2_unlock(chan)
+#define ast_channel_trylock(chan) ao2_trylock(chan)
+
+#define ast_channel_ref(c) ({ ao2_ref(c, +1); (c); })
+#define ast_channel_unref(c) ({ ao2_ref(c, -1); (NULL); })
+
+/*! Channel Iterating @{ */
+
+/*!
+ * \brief A channel iterator
+ *
+ * This is an opaque type.
+ */
+struct ast_channel_iterator;
+
+/*!
+ * \brief Destroy a channel iterator
+ *
+ * \arg i the itereator to destroy
+ *
+ * This function is used to destroy a channel iterator that was retrieved by
+ * using one of the channel_iterator_new() functions.
+ *
+ * \return NULL, for convenience to clear out the pointer to the iterator that
+ * was just destroyed.
+ */
+struct ast_channel_iterator *ast_channel_iterator_destroy(struct ast_channel_iterator *i);
+
+/*!
+ * \brief Create a new channel iterator based on extension
+ *
+ * \arg ao2_flags astobj2 iterator flags
+ * \arg exten The extension that channels must be in
+ * \arg context The context that channels must be in (optional)
+ *
+ * After creating an iterator using this function, the ast_channel_iterator_next()
+ * function can be used to iterate through all channels that are currently
+ * in the specified context and extension.
+ *
+ * \retval NULL on failure
+ * \retval a new channel iterator based on the specified parameters
+ */
+struct ast_channel_iterator *ast_channel_iterator_by_exten_new(int ao2_flags, const char *exten, 
+	const char *context);
+
+/*!
+ * \brief Create a new channel iterator based on name
+ *
+ * \arg ao2_flags astobj2 iterator flags
+ * \arg name channel name or channel uniqueid to match
+ * \arg name_len number of characters in the channel name to match on.  This
+ *      would be used to match based on name prefix.  If matching on the full
+ *      channel name is desired, then this parameter should be 0.
+ *
+ * After creating an iterator using this function, the ast_channel_iterator_next()
+ * function can be used to iterate through all channels that exist that have
+ * the specified name or name prefix.
+ *
+ * \retval NULL on failure
+ * \retval a new channel iterator based on the specified parameters
+ */
+struct ast_channel_iterator *ast_channel_iterator_by_name_new(int ao2_flags, const char *name, 
+	size_t name_len);
+
+/*!
+ * \brief Create a new channel iterator
+ *
+ * \arg ao2_flags astobj2 iterator flags
+ *
+ * After creating an iterator using this function, the ast_channel_iterator_next()
+ * function can be used to iterate through all channels that exist.
+ *
+ * \retval NULL on failure
+ * \retval a new channel iterator
+ */
+struct ast_channel_iterator *ast_channel_iterator_all_new(int ao2_flags);
+
+/*!
+ * \brief Get the next channel for a channel iterator
+ *
+ * \arg i the channel iterator that was created using one of the
+ *  channel_iterator_new() functions.
+ *
+ * This function should be used to iterate through all channels that match a
+ * specified set of parameters that were provided when the iterator was created.
+ *
+ * \retval the next channel that matches the parameters used when the iterator
+ *         was created.
+ * \retval NULL, if no more channels match the iterator parameters.
+ */
+struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i);
+
+/*! @} End channel iterator definitions. */
+
+/*!
+ * \brief Call a function with every active channel
+ *
+ * This function executes a callback one time for each active channel on the
+ * system.  The channel is provided as an argument to the function.
+ */
+struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg, 
+		void *data, int ao2_flags);
+
+/*! @{ Channel search functions */
+
+/*!
+ * \brief Find a channel by name
+ *
+ * \arg name the name or uniqueid of the channel to search for
+ *
+ * Find a channel that has the same name as the provided argument.
+ *
+ * \retval a channel with the name specified by the argument
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_name(const char *name);
+
+/*!
+ * \brief Find a channel by a name prefix
+ *
+ * \arg name The channel name or uniqueid prefix to search for
+ * \arg name_len Only search for up to this many characters from the name
+ *
+ * Find a channel that has the same name prefix as specified by the arguments.
+ *
+ * \retval a channel with the name prefix specified by the arguments
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name_len);
+
+/*!
+ * \brief Find a channel by extension and context
+ *
+ * \arg exten the extension to search for
+ * \arg context the context to search for (optional)
+ *
+ * Return a channel that is currently at the specified extension and context.
+ *
+ * \retval a channel that is at the specified extension and context
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context);
+
+/*! @} End channel search functions. */
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }

Modified: team/russell/ast_channel_ao2/include/asterisk/lock.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/include/asterisk/lock.h?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/include/asterisk/lock.h (original)
+++ team/russell/ast_channel_ao2/include/asterisk/lock.h Fri Feb  6 04:43:34 2009
@@ -271,7 +271,7 @@
 		if (__res < 0) { /* Shouldn't ever happen, but just in case... */ \
 			ast_channel_lock(chan); \
 		} else { \
-			__ast_pthread_mutex_lock(__filename, __lineno, __func, __mutex_name, &chan->lock_dont_use); \
+			_ao2_lock(chan, __filename, __func, __lineno, __mutex_name); \
 		} \
 	} while (0)
 
@@ -1781,33 +1781,4 @@
 })
 #endif
 
-#ifndef DEBUG_CHANNEL_LOCKS
-/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined 
-	in the Makefile, print relevant output for debugging */
-#define ast_channel_lock(x)		ast_mutex_lock(&x->lock_dont_use)
-/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined 
-	in the Makefile, print relevant output for debugging */
-#define ast_channel_unlock(x)		ast_mutex_unlock(&x->lock_dont_use)
-/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined 
-	in the Makefile, print relevant output for debugging */
-#define ast_channel_trylock(x)		ast_mutex_trylock(&x->lock_dont_use)
-#else
-
-#define ast_channel_lock(a) __ast_channel_lock(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int __ast_channel_lock(struct ast_channel *chan, const char *file, int lineno, const char *func);
-
-#define ast_channel_unlock(a) __ast_channel_unlock(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/*! \brief Unlock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function
-*/
-int __ast_channel_unlock(struct ast_channel *chan, const char *file, int lineno, const char *func);
-
-#define ast_channel_trylock(a) __ast_channel_trylock(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/*! \brief Lock AST channel (and print debugging output)
-\note   You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int __ast_channel_trylock(struct ast_channel *chan, const char *file, int lineno, const char *func);
-#endif
-
 #endif /* _ASTERISK_LOCK_H */

Modified: team/russell/ast_channel_ao2/main/channel.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/ast_channel_ao2/main/channel.c?view=diff&rev=173855&r1=173854&r2=173855
==============================================================================
--- team/russell/ast_channel_ao2/main/channel.c (original)
+++ team/russell/ast_channel_ao2/main/channel.c Fri Feb  6 04:43:34 2009
@@ -121,11 +121,16 @@
 #endif
 
 /*! \brief the list of registered channel types */
-static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
-
-/*! \brief the list of channels we have. Note that the lock for this list is used for
-	both the channels list and the backends list.  */
-static AST_RWLIST_HEAD_STATIC(channels, ast_channel);
+static AST_RWLIST_HEAD_STATIC(backends, chanlist);
+
+#ifdef LOW_MEMORY
+#define NUM_CHANNEL_BUCKETS 61
+#else
+#define NUM_CHANNEL_BUCKETS 1567
+#endif
+
+/*! \brief All active channels on the system */
+static struct ao2_container *channels;
 
 /*! \brief map AST_CAUSE's to readable string representations 
  *
@@ -185,8 +190,10 @@
 struct ast_variable *ast_channeltype_list(void)
 {
 	struct chanlist *cl;
-	struct ast_variable *var=NULL, *prev = NULL;
-	AST_LIST_TRAVERSE(&backends, cl, list) {
+	struct ast_variable *var = NULL, *prev = NULL;
+
+	AST_RWLIST_RDLOCK(&backends);
+	AST_RWLIST_TRAVERSE(&backends, cl, list) {
 		if (prev)  {
 			if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description, "")))
 				prev = prev->next;
@@ -195,6 +202,8 @@
 			prev = var;
 		}
 	}
+	AST_RWLIST_UNLOCK(&backends);
+
 	return var;
 }
 
@@ -223,17 +232,15 @@
 	ast_cli(a->fd, FORMAT, "Type", "Description",       "Devicestate", "Indications", "Transfer");
 	ast_cli(a->fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
 
-	AST_RWLIST_RDLOCK(&channels);
-
-	AST_LIST_TRAVERSE(&backends, cl, list) {
+	AST_RWLIST_RDLOCK(&backends);
+	AST_RWLIST_TRAVERSE(&backends, cl, list) {
 		ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
 			(cl->tech->devicestate) ? "yes" : "no",
 			(cl->tech->indicate) ? "yes" : "no",
 			(cl->tech->transfer) ? "yes" : "no");
 		count_chan++;
 	}
-
-	AST_RWLIST_UNLOCK(&channels);
+	AST_RWLIST_UNLOCK(&backends);
 
 	ast_cli(a->fd, "----------\n%d channel drivers registered.\n", count_chan);
 
@@ -254,12 +261,14 @@
 
 	wordlen = strlen(a->word);
 
-	AST_LIST_TRAVERSE(&backends, cl, list) {
+	AST_RWLIST_RDLOCK(&backends);
+	AST_RWLIST_TRAVERSE(&backends, cl, list) {
 		if (!strncasecmp(a->word, cl->tech->type, wordlen) && ++which > a->n) {
 			ret = ast_strdup(cl->tech->type);
 			break;
 		}
 	}
+	AST_RWLIST_UNLOCK(&backends);
 	
 	return ret;
 }
@@ -283,9 +292,9 @@
 	if (a->argc != 4)
 		return CLI_SHOWUSAGE;
 	
-	AST_RWLIST_RDLOCK(&channels);
-
-	AST_LIST_TRAVERSE(&backends, cl, list) {
+	AST_RWLIST_RDLOCK(&backends);
+
+	AST_RWLIST_TRAVERSE(&backends, cl, list) {
 		if (!strncasecmp(cl->tech->type, a->argv[3], strlen(cl->tech->type)))
 			break;
 	}
@@ -293,7 +302,7 @@
 
 	if (!cl) {
 		ast_cli(a->fd, "\n%s is not a registered channel driver.\n", a->argv[3]);
-		AST_RWLIST_UNLOCK(&channels);
+		AST_RWLIST_UNLOCK(&backends);
 		return CLI_FAILURE;
 	}
 
@@ -321,7 +330,8 @@
 		
 	);
 
-	AST_RWLIST_UNLOCK(&channels);
+	AST_RWLIST_UNLOCK(&backends);
+
 	return CLI_SUCCESS;
 }
 
@@ -469,7 +479,7 @@
 	return 1;
 }
 
-static int ast_check_hangup_locked(struct ast_channel *chan)
+int ast_check_hangup_locked(struct ast_channel *chan)
 {
 	int res;
 	ast_channel_lock(chan);
@@ -478,30 +488,28 @@
 	return res;
 }
 
-/*! \brief Initiate system shutdown */
+static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
+{
+	struct ast_channel *chan = obj;
+
+	ast_softhangup(chan, AST_SOFTHANGUP_SHUTDOWN);
+
+	return 0;
+}
+
 void ast_begin_shutdown(int hangup)
 {
-	struct ast_channel *c;
 	shutting_down = 1;
+
 	if (hangup) {
-		AST_RWLIST_RDLOCK(&channels);
-		AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
-			ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
-		}
-		AST_RWLIST_UNLOCK(&channels);
+		ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
 	}
 }
 
 /*! \brief returns number of active/allocated channels */
 int ast_active_channels(void)
 {
-	struct ast_channel *c;
-	int cnt = 0;
-	AST_RWLIST_RDLOCK(&channels);
-	AST_RWLIST_TRAVERSE(&channels, c, chan_list)
-		cnt++;
-	AST_RWLIST_UNLOCK(&channels);
-	return cnt;
+	return ao2_container_count(channels);
 }
 
 /*! \brief Cancel a shutdown in progress */
@@ -557,28 +565,29 @@
 {
 	struct chanlist *chan;
 
-	AST_RWLIST_WRLOCK(&channels);
-
-	AST_LIST_TRAVERSE(&backends, chan, list) {
+	AST_RWLIST_WRLOCK(&backends);
+
+	AST_RWLIST_TRAVERSE(&backends, chan, list) {
 		if (!strcasecmp(tech->type, chan->tech->type)) {
 			ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
-			AST_RWLIST_UNLOCK(&channels);
+			AST_RWLIST_UNLOCK(&backends);
 			return -1;
 		}
 	}
 	
 	if (!(chan = ast_calloc(1, sizeof(*chan)))) {
-		AST_RWLIST_UNLOCK(&channels);
+		AST_RWLIST_UNLOCK(&backends);
 		return -1;
 	}
 	chan->tech = tech;
-	AST_LIST_INSERT_HEAD(&backends, chan, list);
+	AST_RWLIST_INSERT_HEAD(&backends, chan, list);
 
 	ast_debug(1, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
 
 	ast_verb(2, "Registered channel type '%s' (%s)\n", chan->tech->type, chan->tech->description);
 
-	AST_RWLIST_UNLOCK(&channels);
+	AST_RWLIST_UNLOCK(&backends);
+
 	return 0;
 }
 
@@ -589,9 +598,9 @@
 
 	ast_debug(1, "Unregistering channel type '%s'\n", tech->type);
 
-	AST_RWLIST_WRLOCK(&channels);
-
-	AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
+	AST_RWLIST_WRLOCK(&backends);
+
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
 		if (chan->tech == tech) {
 			AST_LIST_REMOVE_CURRENT(list);
 			ast_free(chan);
@@ -601,7 +610,7 @@
 	}
 	AST_LIST_TRAVERSE_SAFE_END;
 
-	AST_RWLIST_UNLOCK(&channels);
+	AST_RWLIST_UNLOCK(&backends);
 }
 
 /*! \brief Get handle to channel driver based on name */
@@ -610,16 +619,16 @@
 	struct chanlist *chanls;
 	const struct ast_channel_tech *ret = NULL;
 
-	AST_RWLIST_RDLOCK(&channels);
-
-	AST_LIST_TRAVERSE(&backends, chanls, list) {
+	AST_RWLIST_RDLOCK(&backends);
+
+	AST_RWLIST_TRAVERSE(&backends, chanls, list) {
 		if (!strcasecmp(name, chanls->tech->type)) {
 			ret = chanls->tech;
 			break;
 		}
 	}
 
-	AST_RWLIST_UNLOCK(&channels);
+	AST_RWLIST_UNLOCK(&backends);
 	
 	return ret;
 }
@@ -764,6 +773,8 @@
 	.description = "Null channel (should not see this)",
 };
 
+static void ast_channel_destructor(void *obj);
+
 /*! \brief Create a new channel structure */
 struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...)
 {
@@ -779,8 +790,9 @@
 		return NULL;
 	}
 
-	if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+	if (!(tmp = ao2_alloc(sizeof(*tmp), ast_channel_destructor))) {
 		return NULL;
+	}
 
 	if (!(tmp->sched = sched_context_create())) {
 		ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
@@ -913,17 +925,13 @@
 	headp = &tmp->varshead;
 	AST_LIST_HEAD_INIT_NOLOCK(headp);
 	
-	ast_mutex_init(&tmp->lock_dont_use);
-	
 	AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
 	
 	ast_string_field_set(tmp, language, defaultlanguage);
 
 	tmp->tech = &null_tech;
 
-	AST_RWLIST_WRLOCK(&channels);
-	AST_RWLIST_INSERT_HEAD(&channels, tmp, chan_list);
-	AST_RWLIST_UNLOCK(&channels);
+	ao2_link(channels, tmp);
 
 	/*\!note
 	 * and now, since the channel structure is built, and has its name, let's
@@ -1102,167 +1110,144 @@
 		ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
 }
 
-/*!
- * \brief Helper function to find channels.
- *
- * It supports these modes:
- *
- * prev != NULL : get channel next in list after prev
- * name != NULL : get channel with matching name
- * name != NULL && namelen != 0 : get channel whose name starts with prefix
- * exten != NULL : get channel whose exten or macroexten matches
- * context != NULL && exten != NULL : get channel whose context or macrocontext
- *
- * It returns with the channel's lock held. If getting the individual lock fails,
- * unlock and retry quickly up to 10 times, then give up.
- *
- * \note XXX Note that this code has cost O(N) because of the need to verify
- * that the object is still on the global list.
- *
- * \note XXX also note that accessing fields (e.g. c->name in ast_log())
- * can only be done with the lock held or someone could delete the
- * object while we work on it. This causes some ugliness in the code.
- * Note that removing the first ast_log() may be harmful, as it would
- * shorten the retry period and possibly cause failures.
- * We should definitely go for a better scheme that is deadlock-free.
- */
-static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
-					       const char *name, const int namelen,
-					       const char *context, const char *exten)
-{
-	const char *msg = prev ? "deadlock" : "initial deadlock";
-	int retries;
-	struct ast_channel *c;
-	const struct ast_channel *_prev = prev;
-
-	for (retries = 0; retries < 200; retries++) {
-		int done;
-		/* Reset prev on each retry.  See note below for the reason. */
-		prev = _prev;
-		AST_RWLIST_RDLOCK(&channels);
-		AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
-			if (prev) {	/* look for last item, first, before any evaluation */
-				if (c != prev)	/* not this one */
-					continue;
-				/* found, prepare to return c->next */
-				if ((c = AST_RWLIST_NEXT(c, chan_list)) == NULL) break;
-				/*!\note
-				 * We're done searching through the list for the previous item.
-				 * Any item after this point, we want to evaluate for a match.
-				 * If we didn't set prev to NULL here, then we would only
-				 * return matches for the first matching item (since the above
-				 * "if (c != prev)" would not permit any other potential
-				 * matches to reach the additional matching logic, below).
-				 * Instead, it would just iterate until it once again found the
-				 * original match, then iterate down to the end of the list and
-				 * quit.
-				 */
-				prev = NULL;
+struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg,
+		void *data, int ao2_flags)
+{
+	return ao2_callback_data(channels, ao2_flags, cb_fn, arg, data);
+}
+
+struct ast_channel_iterator {
+	struct ao2_iterator i;
+	const char *name;
+	size_t name_len;
+	const char *exten;
+	const char *context;
+};
+
+struct ast_channel_iterator *ast_channel_iterator_destroy(struct ast_channel_iterator *i)
+{
+	if (i->name)
+		ast_free((void *) i->name);
+	if (i->exten)
+		ast_free((void *) i->exten);
+	if (i->context)
+		ast_free((void *) i->context);
+	ast_free(i);
+
+	return NULL;
+}
+
+static struct ast_channel_iterator *ast_channel_iterator_new(int ao2_flags, const char *name, 
+	size_t name_len, const char *exten, const char *context)
+{
+	struct ast_channel_iterator *i;
+
+	if (!(i = ast_calloc(1, sizeof(*i))))
+		return NULL;
+
+	if (!ast_strlen_zero(exten) && !(i->exten = ast_strdup(exten)))
+		goto return_error;
+
+	if (!ast_strlen_zero(context) && !(i->context = ast_strdup(context)))
+		goto return_error;
+
+	if (!ast_strlen_zero(name) && !(i->name = ast_strdup(name)))
+		goto return_error;
+
+	i->name_len = name_len;
+
+	i->i = ao2_iterator_init(channels, ao2_flags);
+
+	return i;
+
+return_error:
+	if (i->exten)
+		ast_free((void *) i->exten);
+	if (i->context)
+		ast_free((void *) i->context);
+	ast_free(i);
+
+	return NULL;
+}
+
+struct ast_channel_iterator *ast_channel_iterator_by_exten_new(int ao2_flags, const char *exten, 
+	const char *context)
+{
+	return ast_channel_iterator_new(ao2_flags, NULL, 0, exten, context);
+}
+
+struct ast_channel_iterator *ast_channel_iterator_by_name_new(int ao2_flags, const char *name, 
+	size_t name_len)
+{
+	return ast_channel_iterator_new(ao2_flags, name, name_len, NULL, NULL);
+}
+
+struct ast_channel_iterator *ast_channel_iterator_all_new(int ao2_flags)
+{
+	return ast_channel_iterator_new(ao2_flags, NULL, 0, NULL, NULL);
+}
+
+struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i)
+{
+	struct ast_channel *chan = NULL;
+
+	for (; (chan = ao2_iterator_next(&i->i)); ast_channel_unref(chan)) {
+		if (i->name) { /* match by name */
+			if (!i->name_len) {
+				if (strcasecmp(chan->name, i->name) && strcasecmp(chan->uniqueid, i->name))
+					continue; /* name match failed */
+			} else {
+				if (strncasecmp(chan->name, i->name, i->name_len) &&
+					strncasecmp(chan->uniqueid, i->name, i->name_len))
+					continue; /* name match failed */
 			}
-			if (name) { /* want match by name */
-				if ((!namelen && strcasecmp(c->name, name) && strcmp(c->uniqueid, name)) ||
-				    (namelen && strncasecmp(c->name, name, namelen)))
-					continue;	/* name match failed */
-			} else if (exten) {
-				if (context && strcasecmp(c->context, context) &&
-				    strcasecmp(c->macrocontext, context))
-					continue;	/* context match failed */
-				if (strcasecmp(c->exten, exten) &&
-				    strcasecmp(c->macroexten, exten))
-					continue;	/* exten match failed */
-			}
-			/* if we get here, c points to the desired record */
-			break;
-		}
-		/* exit if chan not found or mutex acquired successfully */
-		/* this is slightly unsafe, as we _should_ hold the lock to access c->name */
-		done = c == NULL || ast_channel_trylock(c) == 0;
-		if (!done) {
-			ast_debug(1, "Avoiding %s for channel '%p'\n", msg, c);
-			if (retries == 199) {
-				/* We are about to fail due to a deadlock, so report this
-				 * while we still have the list lock.
-				 */
-				ast_debug(1, "Failure, could not lock '%p' after %d retries!\n", c, retries);
-				/* As we have deadlocked, we will skip this channel and
-				 * see if there is another match.
-				 * NOTE: No point doing this for a full-name match,
-				 * as there can be no more matches.
-				 */
-				if (!(name && !namelen)) {
-					prev = c;
-					retries = -1;
-				}
-			}
-		}
-		AST_RWLIST_UNLOCK(&channels);
-		if (done)
-			return c;
-		/* If we reach this point we basically tried to lock a channel and failed. Instead of
-		 * starting from the beginning of the list we can restore our saved pointer to the previous
-		 * channel and start from there.
-		 */
-		prev = _prev;
-		usleep(1);	/* give other threads a chance before retrying */
-	}
-
-	return NULL;
-}
-
-/*! \brief Browse channels in use */
-struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
-{
-	return channel_find_locked(prev, NULL, 0, NULL, NULL);
-}
-
-/*! \brief Get channel by name and lock it */
-struct ast_channel *ast_get_channel_by_name_locked(const char *name)
-{
-	return channel_find_locked(NULL, name, 0, NULL, NULL);
-}
-
-/*! \brief Get channel by name prefix and lock it */
-struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
-{
-	return channel_find_locked(NULL, name, namelen, NULL, NULL);
-}
-
-/*! \brief Get next channel by name prefix and lock it */
-struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name,
-							   const int namelen)
-{
-	return channel_find_locked(chan, name, namelen, NULL, NULL);
-}
-

[... 1589 lines stripped ...]



More information about the asterisk-commits mailing list