diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/asterisk.c | 2 | ||||
-rw-r--r-- | main/devicestate.c | 66 | ||||
-rw-r--r-- | main/pbx.c | 95 |
3 files changed, 108 insertions, 55 deletions
diff --git a/main/asterisk.c b/main/asterisk.c index a2f9efb33..efb6403d9 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -2930,7 +2930,7 @@ int main(int argc, char *argv[]) threadstorage_init(); - if (load_modules(1)) { /* Load modules */ + if (load_modules(1)) { /* Load modules, pre-load only */ printf(term_quit()); exit(1); } diff --git a/main/devicestate.c b/main/devicestate.c index 567a52a36..a1b4c4ca3 100644 --- a/main/devicestate.c +++ b/main/devicestate.c @@ -1,7 +1,7 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 1999 - 2006, Digium, Inc. + * Copyright (C) 1999 - 2007, Digium, Inc. * * Mark Spencer <markster@digium.com> * @@ -126,6 +126,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/app.h" #include "asterisk/options.h" +#include "asterisk/event.h" /*! \brief Device state strings for printing */ static const char *devstatestring[] = { @@ -150,16 +151,6 @@ struct devstate_prov { /*! \brief A list of providers */ static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov); -/*! \brief A device state watcher (callback) */ -struct devstate_cb { - void *data; - ast_devstate_cb_type callback; /*!< Where to report when state changes */ - AST_RWLIST_ENTRY(devstate_cb) list; -}; - -/*! \brief A device state watcher list */ -static AST_RWLIST_HEAD_STATIC(devstate_cbs, devstate_cb); - struct state_change { AST_LIST_ENTRY(state_change) list; char device[1]; @@ -380,59 +371,28 @@ static int getproviderstate(const char *provider, const char *address) return res; } -/*! \brief Add device state watcher */ -int ast_devstate_add(ast_devstate_cb_type callback, void *data) -{ - struct devstate_cb *devcb; - - if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb)))) - return -1; - - devcb->data = data; - devcb->callback = callback; - - AST_RWLIST_WRLOCK(&devstate_cbs); - AST_RWLIST_INSERT_HEAD(&devstate_cbs, devcb, list); - AST_RWLIST_UNLOCK(&devstate_cbs); - - return 0; -} - -/*! \brief Remove device state watcher */ -void ast_devstate_del(ast_devstate_cb_type callback, void *data) -{ - struct devstate_cb *devcb; - - AST_RWLIST_WRLOCK(&devstate_cbs); - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) { - if ((devcb->callback == callback) && (devcb->data == data)) { - AST_RWLIST_REMOVE_CURRENT(&devstate_cbs, list); - free(devcb); - break; - } - } - AST_RWLIST_TRAVERSE_SAFE_END; - AST_RWLIST_UNLOCK(&devstate_cbs); -} - /*! \brief Notify callback watchers of change, and notify PBX core for hint updates Normally executed within a separate thread */ static void do_state_change(const char *device) { - int state; - struct devstate_cb *devcb; + enum ast_device_state state; + struct ast_event *event; state = ast_device_state(device); if (option_debug > 2) ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state)); - AST_RWLIST_RDLOCK(&devstate_cbs); - AST_RWLIST_TRAVERSE(&devstate_cbs, devcb, list) - devcb->callback(device, state, devcb->data); - AST_RWLIST_UNLOCK(&devstate_cbs); + if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device, + AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state, + AST_EVENT_IE_END))) { + return; + } - ast_hint_state_changed(device); + ast_event_queue_and_cache(event, + AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, + AST_EVENT_IE_END); } static int __ast_device_state_changed_literal(char *buf) diff --git a/main/pbx.c b/main/pbx.c index cf27f905b..49767206c 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -63,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/devicestate.h" #include "asterisk/stringfields.h" +#include "asterisk/event.h" /*! * \note I M P O R T A N T : @@ -220,6 +221,29 @@ static const struct cfextension_states { { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" } }; +struct statechange { + AST_LIST_ENTRY(statechange) entry; + char dev[0]; +}; + +/*! + * \brief Data used by the device state thread + */ +static struct { + /*! Set to 1 to stop the thread */ + unsigned int stop:1; + /*! The device state monitoring thread */ + pthread_t thread; + /*! Lock for the state change queue */ + ast_mutex_t lock; + /*! Condition for the state change queue */ + ast_cond_t cond; + /*! Queue of state changes */ + AST_LIST_HEAD_NOLOCK(, statechange) state_change_q; +} device_state = { + .thread = AST_PTHREADT_NULL, +}; + static int pbx_builtin_answer(struct ast_channel *, void *); static int pbx_builtin_goto(struct ast_channel *, void *); static int pbx_builtin_hangup(struct ast_channel *, void *); @@ -248,6 +272,9 @@ static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE; static int autofallthrough = 1; +/*! \brief Subscription for device state change events */ +static struct ast_event_sub *device_state_sub; + AST_MUTEX_DEFINE_STATIC(maxcalllock); static int countcalls; @@ -1917,7 +1944,7 @@ int ast_extension_state(struct ast_channel *c, const char *context, const char * return ast_extension_state2(e); /* Check all devices in the hint */ } -void ast_hint_state_changed(const char *device) +static void handle_statechange(const char *device) { struct ast_hint *hint; @@ -1960,6 +1987,49 @@ void ast_hint_state_changed(const char *device) AST_RWLIST_UNLOCK(&hints); } +static int statechange_queue(const char *dev) +{ + /* Avoid potential for deadlocks by spawning a new thread to handle + the event */ + struct statechange *sc; + + if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1))) + return 0; + + strcpy(sc->dev, dev); + + ast_mutex_lock(&device_state.lock); + AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry); + ast_cond_signal(&device_state.cond); + ast_mutex_unlock(&device_state.lock); + + return 0; +} + +static void *device_state_thread(void *data) +{ + struct statechange *sc; + + while (!device_state.stop) { + ast_mutex_lock(&device_state.lock); + while (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) { + ast_cond_wait(&device_state.cond, &device_state.lock); + /* Check to see if we were woken up to see the request to stop */ + if (device_state.stop) { + ast_mutex_unlock(&device_state.lock); + return NULL; + } + } + ast_mutex_unlock(&device_state.lock); + + handle_statechange(sc->dev); + + free(sc); + } + + return NULL; +} + /*! \brief 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) @@ -6032,6 +6102,19 @@ static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data) return res; } +static void device_state_cb(const struct ast_event *event, void *unused) +{ + const char *device; + + device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); + if (ast_strlen_zero(device)) { + ast_log(LOG_ERROR, "Received invalid event that had no device IE\n"); + return; + } + + statechange_queue(device); +} + int load_pbx(void) { int x; @@ -6055,6 +6138,16 @@ int load_pbx(void) /* Register manager application */ ast_manager_register2("ShowDialPlan", EVENT_FLAG_CONFIG, manager_show_dialplan, "List dialplan", mandescr_show_dialplan); + + ast_mutex_init(&device_state.lock); + ast_cond_init(&device_state.cond, NULL); + ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL); + + if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, + AST_EVENT_IE_END))) { + return -1; + } + return 0; } |