[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