[Asterisk-code-review] pbx: Deadlock between contexts container and context merge l... (asterisk[11.21])
Joshua Colp
asteriskteam at digium.com
Mon Jan 11 17:36:59 CST 2016
Joshua Colp has submitted this change and it was merged.
Change subject: pbx: Deadlock between contexts container and context_merge locks
......................................................................
pbx: Deadlock between contexts container and context_merge locks
Recent changes (ASTERISK-25394 commit 2bd27d12223fe33b58c453965ed5c6ed3af7c4f5)
introduced the possibility of a deadlock. Due to the mentioned modifications
ast_change_hints now needs to keep both merge/delete and state callbacks from
occurring while it executes. Unfortunately, sometimes ast_change_hints can be
called with the contexts container locked. When this happens it's possible for
another thread to grab the context_merge_lock before the thread calling into
ast_change_hints does and then try to obtain the contexts container lock. This
of course causes a deadlock between the two threads. The thread calling into
ast_change_hints waits for the other thread to release context_merge_lock and
the other thread is waiting on that one to release the contexts container lock.
Unfortunately, there is not a great way to fix this problem. When hints change,
the subsequent state callbacks cannot run at the same time as a merge/delete,
nor when the usual state callbacks do. This patch alleviates the problem by
having those particular callbacks (the ones run after a hint change) occur in a
serialized task. By moving the context_merge_lock to a task it can now safely be
attempted or held without a deadlock occurring.
ASTERISK-25640 #close
Reported by: Krzysztof Trempala
Change-Id: If2210ea241afd1585dc2594c16faff84579bf302
---
M include/asterisk/event_defs.h
M main/pbx.c
2 files changed, 287 insertions(+), 311 deletions(-)
Approvals:
Mark Michelson: Looks good to me, but someone else must approve
Anonymous Coward #1000019: Verified
Joshua Colp: Looks good to me, approved
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 10c76d0..9555cf6 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -60,8 +60,10 @@
AST_EVENT_ACL_CHANGE = 0x0b,
/*! Send out a ping for debugging distributed events */
AST_EVENT_PING = 0x0c,
+ /*! Used to alert listeners when a hint has changed. */
+ AST_EVENT_HINT_CHANGE = 0x0d,
/*! Number of event types. This should be the last event type + 1 */
- AST_EVENT_TOTAL = 0x0d,
+ AST_EVENT_TOTAL = 0x0e,
};
/*! \brief Event Information Element types */
@@ -304,8 +306,16 @@
* Payload type: UINT
*/
AST_EVENT_IE_CACHABLE = 0x003d,
+
+ /*!
+ * \brief Event hint change payload
+ * Used by: AST_EVENT_HINT_CHANGE
+ * Payload type: RAW
+ */
+ AST_EVENT_IE_HINT_CHANGE_PAYLOAD = 0x003e,
+
/*! \brief Must be the last IE value +1 */
- AST_EVENT_IE_TOTAL = 0x003e,
+ AST_EVENT_IE_TOTAL = 0x003f,
};
/*!
diff --git a/main/pbx.c b/main/pbx.c
index 0165c88..0627ab2 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -1377,6 +1377,8 @@
static struct ast_event_sub *device_state_sub;
/*! \brief Subscription for presence state change events */
static struct ast_event_sub *presence_state_sub;
+/*! \brief Subscription for hint change events */
+static struct ast_event_sub *hint_change_sub;
AST_MUTEX_DEFINE_STATIC(maxcalllock);
static int countcalls;
@@ -5300,15 +5302,113 @@
return res;
}
-static int handle_presencechange(void *datap)
+static void presence_state_notify_callbacks(enum ast_event_type type, struct ast_hint *hint,
+ struct ast_str **hint_app, struct presencechange *pc)
{
- struct ast_hint *hint;
- struct ast_str *hint_app = NULL;
- struct presencechange *pc = datap;
- struct ao2_iterator i;
struct ao2_iterator cb_iter;
+ struct ast_state_cb *state_cb;
char context_name[AST_MAX_CONTEXT];
char exten_name[AST_MAX_EXTENSION];
+
+ ao2_lock(hint);
+
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
+ return;
+ }
+
+ if (type != AST_EVENT_HINT_CHANGE) {
+ const char *app;
+ char *parse;
+
+ /* Does this hint monitor the device that changed state? */
+ app = ast_get_extension_app(hint->exten);
+ if (ast_strlen_zero(app)) {
+ /* The hint does not monitor presence at all. */
+ ao2_unlock(hint);
+ return;
+ }
+
+ ast_str_set(hint_app, 0, "%s", app);
+ parse = parse_hint_presence(*hint_app);
+ if (ast_strlen_zero(parse)) {
+ ao2_unlock(hint);
+ return;
+ }
+ if (strcasecmp(parse, pc->provider)) {
+ /* The hint does not monitor the presence provider. */
+ ao2_unlock(hint);
+ return;
+ }
+ }
+
+ /*
+ * Save off strings in case the hint extension gets destroyed
+ * while we are notifying the watchers.
+ */
+ ast_copy_string(context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(context_name));
+ ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+ sizeof(exten_name));
+ ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+
+ /* Check to see if update is necessary */
+ if ((hint->last_presence_state == pc->state) &&
+ ((hint->last_presence_subtype && pc->subtype &&
+ !strcmp(hint->last_presence_subtype, pc->subtype)) ||
+ (!hint->last_presence_subtype && !pc->subtype)) &&
+ ((hint->last_presence_message && pc->message &&
+ !strcmp(hint->last_presence_message, pc->message)) ||
+ (!hint->last_presence_message && !pc->message))) {
+ /* this update is the same as the last, do nothing */
+ ao2_unlock(hint);
+ return;
+ }
+
+ /* update new values */
+ ast_free(hint->last_presence_subtype);
+ ast_free(hint->last_presence_message);
+ hint->last_presence_state = pc->state;
+ hint->last_presence_subtype = pc->subtype ? ast_strdup(pc->subtype) : NULL;
+ hint->last_presence_message = pc->message ? ast_strdup(pc->message) : NULL;
+
+ ao2_unlock(hint);
+
+ /* For general callbacks */
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_PRESENCE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ /* For extension callbacks */
+ cb_iter = ao2_iterator_init(hint->callbacks, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_PRESENCE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+}
+
+static int handle_presencechange(void *datap)
+{
+ struct presencechange *pc = datap;
+ struct ast_hint *hint;
+ struct ast_str *hint_app = NULL;
+ struct ao2_iterator i;
int res = -1;
hint_app = ast_str_create(1024);
@@ -5319,93 +5419,8 @@
ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
i = ao2_iterator_init(hints, 0);
for (; (hint = ao2_iterator_next(&i)); ao2_ref(hint, -1)) {
- struct ast_state_cb *state_cb;
- const char *app;
- char *parse;
-
- ao2_lock(hint);
-
- if (!hint->exten) {
- /* The extension has already been destroyed */
- ao2_unlock(hint);
- continue;
- }
-
- /* Does this hint monitor the device that changed state? */
- app = ast_get_extension_app(hint->exten);
- if (ast_strlen_zero(app)) {
- /* The hint does not monitor presence at all. */
- ao2_unlock(hint);
- continue;
- }
-
- ast_str_set(&hint_app, 0, "%s", app);
- parse = parse_hint_presence(hint_app);
- if (ast_strlen_zero(parse)) {
- ao2_unlock(hint);
- continue;
- }
- if (strcasecmp(parse, pc->provider)) {
- /* The hint does not monitor the presence provider. */
- ao2_unlock(hint);
- continue;
- }
-
- /*
- * Save off strings in case the hint extension gets destroyed
- * while we are notifying the watchers.
- */
- ast_copy_string(context_name,
- ast_get_context_name(ast_get_extension_context(hint->exten)),
- sizeof(context_name));
- ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
- sizeof(exten_name));
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-
- /* Check to see if update is necessary */
- if ((hint->last_presence_state == pc->state) &&
- ((hint->last_presence_subtype && pc->subtype && !strcmp(hint->last_presence_subtype, pc->subtype)) || (!hint->last_presence_subtype && !pc->subtype)) &&
- ((hint->last_presence_message && pc->message && !strcmp(hint->last_presence_message, pc->message)) || (!hint->last_presence_message && !pc->message))) {
-
- /* this update is the same as the last, do nothing */
- ao2_unlock(hint);
- continue;
- }
-
- /* update new values */
- ast_free(hint->last_presence_subtype);
- ast_free(hint->last_presence_message);
- hint->last_presence_state = pc->state;
- hint->last_presence_subtype = pc->subtype ? ast_strdup(pc->subtype) : NULL;
- hint->last_presence_message = pc->message ? ast_strdup(pc->message) : NULL;
-
- ao2_unlock(hint);
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
-
- /* For extension callbacks */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
+ presence_state_notify_callbacks(
+ AST_EVENT_PRESENCE_STATE, hint, &hint_app, pc);
}
ao2_iterator_destroy(&i);
ast_mutex_unlock(&context_merge_lock);
@@ -5500,17 +5515,102 @@
ao2_iterator_destroy(&iter);
}
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
+{
+ struct ao2_iterator cb_iter;
+ struct ast_state_cb *state_cb;
+ int state, same_state;
+ struct ao2_container *device_state_info;
+ int first_extended_cb_call = 1;
+ char context_name[AST_MAX_CONTEXT];
+ char exten_name[AST_MAX_EXTENSION];
+
+ ao2_lock(hint);
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
+ return;
+ }
+
+ /*
+ * Save off strings in case the hint extension gets destroyed
+ * while we are notifying the watchers.
+ */
+ ast_copy_string(context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(context_name));
+ ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+ sizeof(exten_name));
+ ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+ ao2_unlock(hint);
+
+ /*
+ * Get device state for this hint.
+ *
+ * NOTE: We cannot hold any locks while determining the hint
+ * device state or notifying the watchers without causing a
+ * deadlock. (conlock, hints, and hint)
+ */
+ /* Make a container so state3 can fill it if we wish.
+ * If that failed we simply do not provide the extended state info.
+ */
+ device_state_info = alloc_device_state_info();
+
+ state = ast_extension_state3(*hint_app, device_state_info);
+ if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
+ ao2_cleanup(device_state_info);
+ return;
+ }
+
+ /* Device state changed since last check - notify the watchers. */
+ hint->laststate = state; /* record we saw the change */
+
+ /* For general callbacks */
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ /* For extension callbacks */
+ /* extended callbacks are called when the state changed or when AST_EVENT_RINGING is
+ * included. Normal callbacks are only called when the state changed.
+ */
+ cb_iter = ao2_iterator_init(hint->callbacks, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ if (state_cb->extended && first_extended_cb_call) {
+ /* Fill detailed device_state_info now that we know it is used by extd. callback */
+ first_extended_cb_call = 0;
+ get_device_state_causing_channels(device_state_info);
+ }
+ if (state_cb->extended || !same_state) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ state_cb->extended ? device_state_info : NULL);
+ }
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ ao2_cleanup(device_state_info);
+}
+
static int handle_statechange(void *datap)
{
- struct ast_hint *hint;
+ struct statechange *sc = datap;
struct ast_str *hint_app;
struct ast_hintdevice *device;
struct ast_hintdevice *cmpdevice;
- struct statechange *sc = datap;
struct ao2_iterator *dev_iter;
- struct ao2_iterator cb_iter;
- char context_name[AST_MAX_CONTEXT];
- char exten_name[AST_MAX_EXTENSION];
if (ao2_container_count(hintdevices) == 0) {
/* There are no hints monitoring devices. */
@@ -5541,94 +5641,9 @@
}
for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
- struct ast_state_cb *state_cb;
- int state;
- int same_state;
- struct ao2_container *device_state_info;
- int first_extended_cb_call = 1;
-
- if (!device->hint) {
- /* Should never happen. */
- continue;
+ if (device->hint) {
+ device_state_notify_callbacks(device->hint, &hint_app);
}
- hint = device->hint;
-
- ao2_lock(hint);
- if (!hint->exten) {
- /* The extension has already been destroyed */
- ao2_unlock(hint);
- continue;
- }
-
- /*
- * Save off strings in case the hint extension gets destroyed
- * while we are notifying the watchers.
- */
- ast_copy_string(context_name,
- ast_get_context_name(ast_get_extension_context(hint->exten)),
- sizeof(context_name));
- ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
- sizeof(exten_name));
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
- ao2_unlock(hint);
-
- /*
- * Get device state for this hint.
- *
- * NOTE: We cannot hold any locks while determining the hint
- * device state or notifying the watchers without causing a
- * deadlock. (conlock, hints, and hint)
- */
- /* Make a container so state3 can fill it if we wish.
- * If that failed we simply do not provide the extended state info.
- */
- device_state_info = alloc_device_state_info();
- state = ast_extension_state3(hint_app, device_state_info);
- if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
- ao2_cleanup(device_state_info);
- continue;
- }
-
- /* Device state changed since last check - notify the watchers. */
- hint->laststate = state; /* record we saw the change */
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
-
- /* For extension callbacks */
- /* extended callbacks are called when the state changed or when AST_EVENT_RINGING is
- * included. Normal callbacks are only called when the state changed.
- */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- if (state_cb->extended && first_extended_cb_call) {
- /* Fill detailed device_state_info now that we know it is used by extd. callback */
- first_extended_cb_call = 0;
- get_device_state_causing_channels(device_state_info);
- }
- if (state_cb->extended || !same_state) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- state_cb->extended ? device_state_info : NULL);
- }
- }
- ao2_iterator_destroy(&cb_iter);
-
- ao2_cleanup(device_state_info);
}
ast_mutex_unlock(&context_merge_lock);
@@ -5990,30 +6005,37 @@
return 0;
}
+/*! \brief Publish a hint changed event */
+static int publish_hint_change(struct ast_hint *hint, struct ast_exten *ne)
+{
+ struct ast_event *event;
+
+ /*
+ * Since hint is an ao2_object we need to pass in a pointer to the hint pointer,
+ * which gets copied by the event subsystem. The event handler will take care of
+ * de-referencing the hint.
+ */
+ ao2_ref(hint, +1);
+ if (!(event = ast_event_new(AST_EVENT_HINT_CHANGE,
+ AST_EVENT_IE_HINT_CHANGE_PAYLOAD, AST_EVENT_IE_PLTYPE_RAW, &hint,
+ sizeof(hint), /* We actually want the size of the pointer */
+ AST_EVENT_IE_END))) {
+ ao2_ref(hint, -1);
+ return -1;
+ }
+
+ ast_event_queue_and_cache(event);
+ return 0;
+}
+
/*! \brief Change hint for an extension */
static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
{
- struct ast_str *hint_app;
struct ast_hint *hint;
- int previous_device_state;
- char *previous_message = NULL;
- char *message = NULL;
- char *previous_subtype = NULL;
- char *subtype = NULL;
- int previous_presence_state;
- int presence_state;
- int presence_state_changed = 0;
if (!oe || !ne) {
return -1;
}
-
- hint_app = ast_str_create(1024);
- if (!hint_app) {
- return -1;
- }
-
- ast_mutex_lock(&context_merge_lock); /* Hold off ast_merge_contexts_and_delete and state changes */
ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
@@ -6025,7 +6047,6 @@
if (!hint) {
ao2_unlock(hints);
ast_mutex_unlock(&context_merge_lock);
- ast_free(hint_app);
return -1;
}
@@ -6035,25 +6056,6 @@
ao2_lock(hint);
hint->exten = ne;
- /* Store the previous states so we know whether we need to notify state callbacks */
- previous_device_state = hint->laststate;
- previous_presence_state = hint->last_presence_state;
- previous_message = hint->last_presence_message;
- previous_subtype = hint->last_presence_subtype;
-
- /* Update the saved device and presence state with the new extension */
- hint->laststate = ast_extension_state2(ne, NULL);
- hint->last_presence_state = AST_PRESENCE_INVALID;
- hint->last_presence_subtype = NULL;
- hint->last_presence_message = NULL;
-
- presence_state = extension_presence_state_helper(ne, &subtype, &message);
- if (presence_state > 0) {
- hint->last_presence_state = presence_state;
- hint->last_presence_subtype = subtype;
- hint->last_presence_message = message;
- }
-
ao2_unlock(hint);
ao2_link(hints, hint);
@@ -6062,103 +6064,59 @@
ast_get_extension_name(ne),
ast_get_context_name(ast_get_extension_context(ne)));
}
-
ao2_unlock(hints);
- /* Locking for state callbacks is respected here and only the context_merge_lock lock is
- * held during the state callback invocation. This will stop the normal state callback
- * thread from being able to handle incoming state changes if they occur.
- */
-
- /* Determine if presence state has changed due to the change of the hint extension */
- if ((hint->last_presence_state != previous_presence_state) ||
- strcmp(S_OR(hint->last_presence_subtype, ""), S_OR(previous_subtype, "")) ||
- strcmp(S_OR(hint->last_presence_message, ""), S_OR(previous_message, ""))) {
- presence_state_changed = 1;
- }
-
- /* Notify any existing state callbacks if the device or presence state has changed */
- if ((hint->laststate != previous_device_state) || presence_state_changed) {
- struct ao2_iterator cb_iter;
- struct ast_state_cb *state_cb;
- struct ao2_container *device_state_info;
- int first_extended_cb_call = 1;
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- /* Unlike the normal state callbacks since something has explicitly provided us this extension
- * it will remain valid and unchanged for the lifetime of this function invocation.
- */
- if (hint->laststate != previous_device_state) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- NULL);
- }
- if (presence_state_changed) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- }
- ao2_iterator_destroy(&cb_iter);
-
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(ne));
-
- device_state_info = alloc_device_state_info();
- ast_extension_state3(hint_app, device_state_info);
-
- /* For extension callbacks */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- if (hint->laststate != previous_device_state) {
- if (state_cb->extended && first_extended_cb_call) {
- /* Fill detailed device_state_info now that we know it is used by extd. callback */
- first_extended_cb_call = 0;
- get_device_state_causing_channels(device_state_info);
- }
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- state_cb->extended ? device_state_info : NULL);
- }
- if (presence_state_changed) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- }
- ao2_iterator_destroy(&cb_iter);
-
- ao2_cleanup(device_state_info);
- }
+ publish_hint_change(hint, ne);
ao2_ref(hint, -1);
-
- ast_mutex_unlock(&context_merge_lock);
-
- ast_free(hint_app);
- ast_free(previous_message);
- ast_free(previous_subtype);
return 0;
}
+static int handle_hint_change(void *data)
+{
+ struct ast_hint *hint = data;
+ struct ast_str *hint_app;
+ int state;
+ struct presencechange presence_state;
+
+ if (!(hint_app = ast_str_create(1024))) {
+ return -1;
+ }
+
+ device_state_notify_callbacks(hint, &hint_app);
+
+ state = extension_presence_state_helper(
+ hint->exten, &presence_state.subtype, &presence_state.message);
+
+ presence_state.state = state > 0 ? state : AST_PRESENCE_INVALID;
+
+ presence_state_notify_callbacks(AST_EVENT_HINT_CHANGE, hint, &hint_app, &presence_state);
+
+ ast_free(hint_app);
+ ao2_ref(hint, -1);
+
+ ast_free(presence_state.subtype);
+ ast_free(presence_state.message);
+
+ return 0;
+}
+
+static void hint_change_cb(const struct ast_event *event, void *unused)
+{
+ /* The event data is a pointer to a hint (an ao2_object) */
+ struct ast_hint **hint = (struct ast_hint **)
+ ast_event_get_ie_raw(event, AST_EVENT_IE_HINT_CHANGE_PAYLOAD);
+
+ if (!hint || !*hint) {
+ return;
+ }
+
+ /* The task processor thread is taking our reference to the hint object. */
+ if (ast_taskprocessor_push(extension_state_tps, handle_hint_change, *hint) < 0) {
+ ao2_ref(*hint, -1);
+ }
+}
/*! \brief Get hint for channel */
int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
@@ -12255,6 +12213,9 @@
{
int x;
+ if (hint_change_sub) {
+ hint_change_sub = ast_event_unsubscribe(hint_change_sub);
+ }
if (presence_state_sub) {
presence_state_sub = ast_event_unsubscribe(presence_state_sub);
}
@@ -12316,6 +12277,11 @@
return -1;
}
+ if (!(hint_change_sub = ast_event_subscribe(AST_EVENT_HINT_CHANGE, hint_change_cb, "pbx Hint Change", NULL,
+ AST_EVENT_IE_END))) {
+ return -1;
+ }
+
return 0;
}
--
To view, visit https://gerrit.asterisk.org/1964
To unsubscribe, visit https://gerrit.asterisk.org/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: If2210ea241afd1585dc2594c16faff84579bf302
Gerrit-PatchSet: 2
Gerrit-Project: asterisk
Gerrit-Branch: 11.21
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Mark Michelson <mmichelson at digium.com>
More information about the asterisk-code-review
mailing list