queue device state changes and handle them serially in a background thread

optimize device state related functions
add ast_get_channel_by_name_prefix to allow searching for matching channels in O(1) operation


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6062 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.2-netsec
Kevin P. Fleming 20 years ago
parent 0111a38e4b
commit c13d601fb4

@ -254,7 +254,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
cdr.o tdd.o acl.o rtp.o manager.o asterisk.o \
dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
utils.o config_old.o plc.o jitterbuf.o dnsmgr.o
utils.o config_old.o plc.o jitterbuf.o dnsmgr.o devicestate.o
ifeq (${OSARCH},Darwin)
OBJS+=poll.o dlfcn.o
ASTLINK=-Wl,-dynamic

@ -31,6 +31,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/devicestate.h"
static char *tdesc = "Check if channel is available";

@ -71,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#define QUEUE_STRATEGY_RINGALL 0
#define QUEUE_STRATEGY_ROUNDROBIN 1

@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/version.h"
#include "asterisk/build.h"
#include "asterisk/linkedlists.h"
#include "asterisk/devicestate.h"
#include "defaults.h"
@ -2095,6 +2096,10 @@ int main(int argc, char *argv[])
printf(term_quit());
exit(1);
}
if (ast_device_state_engine_init()) {
printf(term_quit());
exit(1);
}
ast_rtp_init();
if (ast_image_init()) {
printf(term_quit());

@ -57,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/transcap.h"
#include "asterisk/devicestate.h"
/* uncomment if you have problems with 'monitoring' synchronized files */
#if 0
@ -295,6 +296,58 @@ int ast_channel_register(const struct ast_channel_tech *tech)
return 0;
}
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
chan = backends;
while (chan) {
if (chan->tech == tech) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
return;
}
last = chan;
chan = chan->next;
}
ast_mutex_unlock(&chlock);
}
const struct ast_channel_tech *ast_get_channel_tech(const char *name)
{
struct chanlist *chanls;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
return NULL;
}
for (chanls = backends; chanls; chanls = chanls->next) {
if (strcasecmp(name, chanls->tech->type))
continue;
ast_mutex_unlock(&chlock);
return chanls->tech;
}
ast_mutex_unlock(&chlock);
return NULL;
}
/*--- ast_cause2str: Gives the string form of a given hangup cause */
const char *ast_cause2str(int cause)
{
@ -605,7 +658,7 @@ void ast_channel_undefer_dtmf(struct ast_channel *chan)
* 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 char *name, const int namelen)
{
const char *msg = prev ? "deadlock" : "initial deadlock";
int retries, done;
@ -615,8 +668,14 @@ static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
ast_mutex_lock(&chlock);
for (c = channels; c; c = c->next) {
if (prev == NULL) {
/* want either head of list or match by name */
if (name == NULL || !strcasecmp(name, c->name))
/* want head of list */
if (!name)
break;
/* want match by full name */
if (!namelen && !strcasecmp(c->name, name))
break;
/* want match by name prefix */
if (!strncasecmp(c->name, name, namelen))
break;
} else if (c == prev) { /* found, return c->next */
c = c->next;
@ -646,13 +705,19 @@ static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
/*--- ast_channel_walk_locked: Browse channels in use */
struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
{
return channel_find_locked(prev, NULL);
return channel_find_locked(prev, NULL, 0);
}
/*--- ast_get_channel_by_name_locked: 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);
return channel_find_locked(NULL, name, 0);
}
/*--- ast_get_channel_by_name_prefix_locked: 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);
}
/*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */
@ -964,37 +1029,6 @@ int ast_hangup(struct ast_channel *chan)
return res;
}
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
chan = backends;
while (chan) {
if (chan->tech == tech) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
return;
}
last = chan;
chan = chan->next;
}
ast_mutex_unlock(&chlock);
}
int ast_answer(struct ast_channel *chan)
{
int res = 0;
@ -2167,70 +2201,6 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c
return c;
}
int ast_parse_device_state(char *device)
{
char name[AST_CHANNEL_NAME] = "";
char *cut;
struct ast_channel *chan;
chan = ast_channel_walk_locked(NULL);
while (chan) {
ast_copy_string(name, chan->name, sizeof(name));
ast_mutex_unlock(&chan->lock);
cut = strchr(name,'-');
if (cut)
*cut = 0;
if (!strcmp(name, device)) {
if (chan->_state == AST_STATE_RINGING) {
return AST_DEVICE_RINGING;
} else {
return AST_DEVICE_INUSE;
}
}
chan = ast_channel_walk_locked(chan);
}
return AST_DEVICE_UNKNOWN;
}
int ast_device_state(char *device)
{
char tech[AST_MAX_EXTENSION] = "";
char *number;
struct chanlist *chanls;
int res = 0;
ast_copy_string(tech, device, sizeof(tech));
number = strchr(tech, '/');
if (!number) {
return AST_DEVICE_INVALID;
}
*number = 0;
number++;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chanls = backends;
while(chanls) {
if (!strcasecmp(tech, chanls->tech->type)) {
ast_mutex_unlock(&chlock);
if (!chanls->tech->devicestate)
return ast_parse_device_state(device);
else {
res = chanls->tech->devicestate(number);
if (res == AST_DEVICE_UNKNOWN)
return ast_parse_device_state(device);
else
return res;
}
}
chanls = chanls->next;
}
ast_mutex_unlock(&chlock);
return AST_DEVICE_INVALID;
}
int ast_call(struct ast_channel *chan, char *addr, int timeout)
{
/* Place an outgoing call, but don't wait any longer than timeout ms before returning.

@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
static const char desc[] = "Agent Proxy Channel";
static const char channeltype[] = "Agent";

@ -42,6 +42,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/localtime.h"
#include "asterisk/aes.h"
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include <sys/mman.h>
#include <arpa/inet.h>
#include <dirent.h>
@ -73,7 +75,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "iax2.h"
#include "iax2-parser.h"
#include "iax2-provision.h"
#include "asterisk.h"
/* Define NEWJB to use the new channel independent jitterbuffer,
* otherwise, use the old jitterbuffer */

@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/astobj.h"
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#ifdef OSP_SUPPORT
#include "asterisk/astosp.h"
#endif

@ -444,21 +444,6 @@ struct outgoing_helper {
/*! Do not transmit voice data */
#define AST_STATE_MUTE (1 << 16)
/*! Device is valid but channel didn't know state */
#define AST_DEVICE_UNKNOWN 0
/*! Device is not used */
#define AST_DEVICE_NOT_INUSE 1
/*! Device is in use */
#define AST_DEVICE_INUSE 2
/*! Device is busy */
#define AST_DEVICE_BUSY 3
/*! Device is invalid */
#define AST_DEVICE_INVALID 4
/*! Device is unavailable */
#define AST_DEVICE_UNAVAILABLE 5
/*! Device is ringing */
#define AST_DEVICE_RINGING 6
/*! Create a channel structure */
/*! Returns NULL on failure to allocate. New channels are
by default set to the "default" context and
@ -494,27 +479,6 @@ void ast_channel_free(struct ast_channel *);
*/
struct ast_channel *ast_request(const char *type, int format, void *data, int *status);
/*! Search the Channels by Name */
/*!
* \param device like a dialstring
* Search the Device in active channels by compare the channelname against
* the devicename. Compared are only the first chars to the first '-' char.
* Returns an AST_DEVICE_UNKNOWN if no channel found or
* AST_DEVICE_INUSE if a channel is found
*/
int ast_parse_device_state(char *device);
/*! Asks a channel for device state */
/*!
* \param device like a dialstring
* Asks a channel for device state, data is normaly a number from dialstring
* used by the low level module
* Trys the channel devicestate callback if not supported search in the
* active channels list for the device.
* Returns an AST_DEVICE_??? state -1 on failure
*/
int ast_device_state(char *device);
/*!
* \param type type of channel to request
* \param format requested channel format
@ -545,6 +509,12 @@ int ast_channel_register(const struct ast_channel_tech *tech);
*/
void ast_channel_unregister(const struct ast_channel_tech *tech);
/*! Get a channel technology structure by name
* \param name name of technology to find
* \return a pointer to the structure, or NULL if no matching technology found
*/
const struct ast_channel_tech *ast_get_channel_tech(const char *name);
/*! Hang up a channel */
/*!
* \param chan channel to hang up
@ -759,6 +729,9 @@ struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev);
/*! Get channel by name (locks channel) */
struct ast_channel *ast_get_channel_by_name_locked(const char *chan);
/*! Get channel by name prefix (locks channel) */
struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen);
/*! Waits for a digit */
/*!
* \param c channel to wait for a digit on

@ -306,11 +306,13 @@ struct { \
used to link entries of this list together.
Removes the head entry from the list, and returns a pointer to it. The
forward-link pointer in the returned entry is \b not cleared.
forward-link pointer in the returned entry is \b not cleared. This macro
is safe to call on an empty list.
*/
#define AST_LIST_REMOVE_HEAD(head, field) ({ \
typeof((head)->first) cur = (head)->first; \
(head)->first = (head)->first->field.next; \
if (cur) \
(head)->first = cur->field.next; \
cur; \
})

@ -51,8 +51,6 @@ struct ast_sw;
typedef int (*ast_state_cb_type)(char *context, char* id, int state, void *data);
typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data);
/*! Data structure associated with a custom function */
struct ast_custom_function {
char *name;
@ -253,16 +251,6 @@ int ast_unregister_application(const char *app);
*/
int ast_extension_state(struct ast_channel *c, char *context, char *exten);
/*! Tells Asterisk the State for Device is changed */
/*!
* \param fmt devicename like a dialstring with format parameters
* Asterisk polls the new extensionstates and calls the registered
* callbacks for the changed extensions
* Returns 0 on success, -1 on failure
*/
int ast_device_state_changed(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
/*! Registers a state change callback */
/*!
* \param context which context to look in
@ -275,15 +263,6 @@ int ast_device_state_changed(const char *fmt, ...)
int ast_extension_state_add(const char *context, const char *exten,
ast_state_cb_type callback, void *data);
/*! Registers a device state change callback */
/*!
* \param data to pass to callback
* The callback is called if the state for extension is changed
* Return -1 on failure, ID on success
*/
int ast_devstate_add(ast_devstate_cb_type callback, void *data);
void ast_devstate_del(ast_devstate_cb_type callback, void *data);
/*! Deletes a registered state change callback by ID */
/*!
* \param id of the callback to delete
@ -629,6 +608,8 @@ char *ast_func_read(struct ast_channel *chan, const char *in, char *workspace, s
*/
void ast_func_write(struct ast_channel *chan, const char *in, const char *value);
void ast_hint_state_changed(const char *device);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

146
pbx.c

@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/causes.h"
#include "asterisk/musiconhold.h"
#include "asterisk/app.h"
#include "asterisk/devicestate.h"
/*
* I M P O R T A N T :
@ -160,15 +161,6 @@ struct ast_state_cb {
struct ast_state_cb *next;
};
/* ast_devstate_cb: An extension state notify */
struct ast_devstate_cb {
void *data;
ast_devstate_cb_type callback;
struct ast_devstate_cb *next;
};
static struct ast_devstate_cb *devcbs;
/* Hints are pointers from an extension in the dialplan to one or more devices (tech/name) */
struct ast_hint {
struct ast_exten *exten; /* Extension */
@ -1805,130 +1797,48 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten)
return ast_extension_state2(e); /* Check all devices in the hint */
}
/*--- ast_device_state_changed: If device state in cblist is changed - then notify callback function */
int ast_device_state_changed(const char *fmt, ...)
void ast_hint_state_changed(const char *device)
{
struct ast_hint *list;
struct ast_hint *hint;
struct ast_state_cb *cblist;
struct ast_devstate_cb *devcb;
char hint[AST_MAX_EXTENSION] = "";
char device[AST_MAX_EXTENSION];
char *cur, *rest;
char buf[AST_MAX_EXTENSION];
char *parse;
char *cur;
int state;
va_list ap;
va_start(ap, fmt);
vsnprintf(device, sizeof(device), fmt, ap);
va_end(ap);
rest = strchr(device, '-');
if (rest) {
*rest = 0;
}
state = ast_device_state(device);
if (option_debug > 2)
ast_log(LOG_DEBUG, "Changing state for %s - state %d\n", device, state);
ast_mutex_lock(&hintlock);
/* First check device callbacks */
devcb = devcbs;
while(devcb) {
if (devcb->callback)
devcb->callback(device, state, devcb->data);
devcb = devcb->next;
}
/* Then check callbacks in hints */
list = hints;
while (list) {
for (hint = hints; hint; hint = hint->next) {
ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
parse = buf;
for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) {
if (strcmp(cur, device))
continue;
ast_copy_string(hint, ast_get_extension_app(list->exten), sizeof(hint));
cur = hint;
do {
rest = strchr(cur, '&');
if (rest) {
*rest = 0;
rest++;
}
/* Get device state for this hint */
state = ast_extension_state2(hint->exten);
if (!strcmp(cur, device)) { /* Is this device referred to in this hint? */
/* Get device state for this hint */
state = ast_extension_state2(list->exten);
if ((state != -1) && (state != list->laststate)) {
/* Device state changed since last check - notify the watcher */
/* For general callbacks */
cblist = statecbs;
while (cblist) {
cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
cblist = cblist->next;
}
if ((state == -1) || (state == hint->laststate))
continue;
/* For extension callbacks */
cblist = list->callbacks;
while (cblist) {
cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
cblist = cblist->next;
}
/* Device state changed since last check - notify the watchers */
list->laststate = state;
}
break;
}
cur = rest;
} while (cur);
list = list->next;
}
ast_mutex_unlock(&hintlock);
return 1;
}
/* For general callbacks */
for (cblist = statecbs; cblist; cblist = cblist->next)
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
/*--- ast_devstate_add: Add device state watcher */
int ast_devstate_add(ast_devstate_cb_type callback, void *data)
{
struct ast_devstate_cb *devcb;
devcb = malloc(sizeof(struct ast_devstate_cb));
if (devcb) {
memset(devcb, 0, sizeof(struct ast_devstate_cb));
ast_mutex_lock(&hintlock);
devcb->data = data;
devcb->callback = callback;
devcb->next = devcbs;
devcbs = devcb;
ast_mutex_unlock(&hintlock);
/* For extension callbacks */
for (cblist = hint->callbacks; cblist; cblist = cblist->next)
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
hint->laststate = state;
break;
}
}
return 0;
}
/*--- ast_devstate_del: Remove device state watcher */
void ast_devstate_del(ast_devstate_cb_type callback, void *data)
{
struct ast_devstate_cb *devcb, *prev = NULL, *next;
ast_mutex_lock(&hintlock);
devcb = devcbs;
while(devcb) {
next = devcb->next;
if ((devcb->data == data) && (devcb->callback == callback)) {
if (prev)
prev->next = next;
else
devcbs = next;
free(devcb);
} else
prev = devcb;
devcb = next;
}
ast_mutex_unlock(&hintlock);
}
/*--- ast_extension_state_add: Add watcher for extension states */
int ast_extension_state_add(const char *context, const char *exten,
ast_state_cb_type callback, void *data)

Loading…
Cancel
Save