[asterisk-commits] mmichelson: branch group/CCSS r223327 - /team/group/CCSS/main/ccss.c
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Oct 9 15:41:52 CDT 2009
Author: mmichelson
Date: Fri Oct 9 15:41:49 2009
New Revision: 223327
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=223327
Log:
More organization. Focusing this time on monitor and
link functions.
Modified:
team/group/CCSS/main/ccss.c
Modified: team/group/CCSS/main/ccss.c
URL: http://svnview.digium.com/svn/asterisk/team/group/CCSS/main/ccss.c?view=diff&rev=223327&r1=223326&r2=223327
==============================================================================
--- team/group/CCSS/main/ccss.c (original)
+++ team/group/CCSS/main/ccss.c Fri Oct 9 15:41:49 2009
@@ -60,6 +60,9 @@
static struct ast_sched_thread *cc_sched_thread;
static const int CC_CORE_INSTANCES_BUCKETS = 17;
+static struct ast_cc_monitor *root_monitor;
+static struct ast_taskprocessor *cc_core_taskprocessor;
+
/* Parsed configuration value for cc_max_requests */
static unsigned int global_cc_max_requests;
/* The actual number of CC requests in the system */
@@ -828,10 +831,429 @@
ast_free(agent_pvt);
}
-static struct ast_cc_monitor *root_monitor;
-
-static void prune_links(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *link_parent);
-
+static void destroy_link(struct ast_cc_monitor_link *link)
+{
+ ast_log(LOG_NOTICE, "Destroying link with parent %s and child %s\n",
+ link->parent->interface->name, link->child->interface->name);
+ ast_atomic_fetchadd_int(&link->child->num_requests, -1);
+ link->child = cc_unref(link->child, "Unref link's child");
+ link->parent = cc_unref(link->parent, "Unref link's parent");
+ ast_free(link);
+ return;
+}
+
+static void prune_links(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *link_parent)
+{
+ struct ast_cc_monitor_link *link_iter;
+ ast_log(LOG_NOTICE, "Prune links called for monitor %s\n", monitor->interface->name);
+#if 0
+ ao2_callback(cc_monitors, OBJ_NODATA, print_debug, NULL);
+#endif
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&monitor->child_links, link_iter, next_child) {
+ if (link_iter->core_id == core_id) {
+ prune_links(link_iter->child, core_id, link_iter);
+ AST_LIST_REMOVE_CURRENT(next_child);
+ link_iter->child->callbacks->cancel_available_timer(link_iter->child, core_id, &link_iter->child_avail_id);
+ destroy_link(link_iter);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (link_parent) {
+ ast_log(LOG_NOTICE, "Removing parent link from monitor %s\n", monitor->interface->name);
+ AST_LIST_REMOVE(&monitor->parent_links, link_parent, next_parent);
+ }
+
+ if (monitor->saved_link == link_parent) {
+ monitor->saved_link = NULL;
+ }
+
+ if (monitor->interface->monitor_class == AST_CC_ROOT_MONITOR) {
+ /* Never ever under any circumstances unlink
+ * the root monitor. Of course, since the root monitor
+ * is being acted on here, it means that a CC transaction
+ * failed or has completed. So decrease the number of
+ * CC requests in the system.
+ */
+ ast_atomic_fetchadd_int(&cc_request_count, -1);
+ ast_log(LOG_NOTICE, "Not unlinking monitor %s because it is the root\n", monitor->interface->name);
+ return;
+ }
+ if (AST_LIST_EMPTY(&monitor->parent_links) &&
+ AST_LIST_EMPTY(&monitor->child_links)) {
+ /* This should cause the monitor's destructor
+ * callback to be called
+ */
+ ast_log(LOG_NOTICE, "Unlinking monitor %s\n", monitor->interface->name);
+ ao2_t_unlink(cc_monitors, monitor, "Unlink monitor since nothing refers to it anymore");
+ }
+}
+
+static int cc_extension_monitor_init(struct ast_cc_monitor *monitor, const int core_id);
+static int cc_extension_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link);
+static int cc_extension_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id);
+static enum ast_device_state cc_extension_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id);
+static int cc_extension_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id);
+static int cc_extension_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id);
+static void cc_extension_monitor_destructor(void *monitor);
+
+static struct ast_cc_monitor_callbacks extension_monitor_cbs = {
+ .type = "extension",
+ .init = cc_extension_monitor_init,
+ .request_cc = cc_extension_monitor_request_cc,
+ .suspend = cc_extension_monitor_suspend,
+ .status_request = cc_extension_monitor_status_request,
+ .unsuspend = cc_extension_monitor_unsuspend,
+ .cancel_available_timer = cc_extension_monitor_cancel_available_timer,
+ .destructor = cc_extension_monitor_destructor,
+};
+
+static int cc_extension_monitor_init(struct ast_cc_monitor *monitor, const int core_id)
+{
+ /* Currently, extension monitors don't really need to hold onto anything. However,
+ * it may be necessary to hold onto state information received from child device
+ * monitors. It will be much clearer once I begin design of device monitors.
+ */
+ return 0;
+}
+
+static int cc_extension_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link)
+{
+ struct ast_cc_monitor_link *link_iter;
+ AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
+ if (link_iter->core_id != core_id) {
+ continue;
+ }
+ ast_assert(link_iter->child != NULL);
+ ast_assert(link_iter->child->callbacks != NULL);
+ if (link_iter->child->callbacks->request_cc(link_iter->child, core_id, link_iter)) {
+ prune_links(link_iter->child, core_id, link_iter);
+ }
+ }
+ if (AST_LIST_EMPTY(&monitor->child_links)) {
+ /* None of the child monitors successfully requested
+ * CC, and so all of the child links were pruned. If this
+ * is an extension monitor, just return -1. If this is the
+ * root monitor, then that means failure all around, so request
+ * a state change to CC_FAILED
+ */
+ if (monitor->interface->monitor_class == AST_CC_ROOT_MONITOR) {
+ ast_cc_request_state_change(CC_FAILED, core_id, "All monitors failed to request CC");
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int cc_extension_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id)
+{
+ struct ast_cc_monitor_link *link_iter;
+ AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
+ if (link_iter->core_id != core_id) {
+ continue;
+ }
+ ast_assert(link_iter->child != NULL);
+ ast_assert(link_iter->child->callbacks != NULL);
+ link_iter->is_suspended = 1;
+ link_iter->child->callbacks->suspend(link_iter->child, core_id);
+ }
+ return 0;
+}
+
+static enum ast_device_state cc_extension_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id)
+{
+ /* So, for an extension monitor, a status request involves traversing all child links that have
+ * the proper core_id. As soon as ONE of them returns AST_DEVICE_NOT_INUSE, this means that as
+ * far as CC is concerned, the entire extension monitor itself is not in use, so go ahead
+ * and return that status.
+ */
+ struct ast_cc_monitor_link *link;
+
+ AST_LIST_TRAVERSE(&monitor->child_links, link, next_child) {
+ if (link->child->callbacks->status_request(link->child, core_id) == AST_DEVICE_NOT_INUSE) {
+ return AST_DEVICE_NOT_INUSE;
+ }
+ }
+ return AST_DEVICE_INUSE;
+}
+
+static int cc_extension_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id)
+{
+ struct ast_cc_monitor_link *link_iter;
+ AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
+ if (link_iter->core_id != core_id) {
+ continue;
+ }
+ ast_assert(link_iter->child != NULL);
+ ast_assert(link_iter->child->callbacks != NULL);
+ link_iter->is_suspended = 0;
+ link_iter->child->callbacks->unsuspend(link_iter->child, core_id);
+ }
+ return 0;
+}
+
+static int cc_extension_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id)
+{
+ struct ast_cc_monitor_link *link_iter;
+ AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
+ if (link_iter->core_id != core_id) {
+ continue;
+ }
+ ast_assert(link_iter->child != NULL);
+ ast_assert(link_iter->child->callbacks != NULL);
+ link_iter->child->callbacks->cancel_available_timer(link_iter->child, core_id, &link_iter->child_avail_id);
+ }
+ return 0;
+}
+
+static void cc_extension_monitor_destructor(void *monitor)
+{
+ struct ast_cc_monitor *mon = monitor;
+ ast_log(LOG_NOTICE, "Calling destructor for monitor %s\n", mon->interface->name);
+ return;
+}
+
+static int cc_generic_monitor_init(struct ast_cc_monitor *monitor, const int core_id);
+static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link);
+static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id);
+static enum ast_device_state cc_generic_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id);
+static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id);
+static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id);
+static void cc_generic_monitor_destructor(void *monitor);
+
+static struct ast_cc_monitor_callbacks generic_monitor_cbs = {
+ .type = "generic",
+ .init = cc_generic_monitor_init,
+ .request_cc = cc_generic_monitor_request_cc,
+ .suspend = cc_generic_monitor_suspend,
+ .status_request = cc_generic_monitor_status_request,
+ .unsuspend = cc_generic_monitor_unsuspend,
+ .cancel_available_timer = cc_generic_monitor_cancel_available_timer,
+ .destructor = cc_generic_monitor_destructor,
+};
+
+/*!
+ * \brief private data for generic device monitor
+ */
+struct generic_monitor_pvt {
+ /*!
+ * Handle for event subscription to device state
+ */
+ struct ast_event_sub *sub;
+ /*!
+ * Cached device state for this monitor
+ */
+ enum ast_device_state current_state;
+};
+
+static int cc_generic_monitor_init(struct ast_cc_monitor *monitor, const int core_id)
+{
+ struct generic_monitor_pvt *gen_mon_pvt = ast_calloc(1, sizeof(*gen_mon_pvt));
+
+ if (!gen_mon_pvt) {
+ return -1;
+ }
+ ast_log(LOG_NOTICE, "Allocated a generic monitor pvt for device %s\n", monitor->interface->name);
+ monitor->private_data = gen_mon_pvt;
+ return 0;
+}
+
+struct generic_tp_cb_data {
+ struct ast_cc_monitor *monitor;
+ enum ast_device_state new_state;
+};
+
+static int generic_monitor_devstate_tp_cb(void *data)
+{
+ struct generic_tp_cb_data *gtcd = data;
+ struct ast_cc_monitor *monitor = gtcd->monitor;
+ enum ast_device_state new_state = gtcd->new_state;
+ struct generic_monitor_pvt *gen_mon_pvt = monitor->private_data;
+
+ if (gen_mon_pvt->current_state == new_state) {
+ ast_free(gtcd);
+ cc_unref(monitor, "Kill reference from generic_monitor_devstate_cb");
+ return 0;
+ }
+
+ gen_mon_pvt->current_state = new_state;
+
+ if (new_state == AST_DEVICE_NOT_INUSE) {
+ ast_cc_monitor_announce_availability(monitor);
+ }
+ cc_unref(monitor, "Kill reference from generic_monitor_devstate_cb");
+ ast_free(gtcd);
+ return 0;
+}
+
+static void generic_monitor_devstate_cb(const struct ast_event *event, void *userdata)
+{
+ /* Wow, it's cool that we've picked up on a state change, but we really want
+ * the actual work to be done in the core's taskprocessor execution thread
+ * so that all monitor operations can be serialized. Locks?! We don't need
+ * no steenkin' locks!
+ */
+ struct ast_cc_monitor *monitor = userdata;
+ struct generic_tp_cb_data *gtcd = ast_calloc(1, sizeof(*gtcd));
+
+ if (!gtcd) {
+ return;
+ }
+
+ gtcd->monitor = cc_ref(monitor, "Keep reference until taskprocessor callback executes");
+ gtcd->new_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+
+ ast_taskprocessor_push(cc_core_taskprocessor, generic_monitor_devstate_tp_cb, gtcd);
+
+ return;
+}
+
+static int internal_cc_available_timer_expire(void *data)
+{
+ struct ast_cc_monitor_link *link = data;
+ struct ast_cc_monitor *parent_monitor = link->parent;
+ struct ast_cc_monitor *child_monitor = link->child;
+ int core_id = link->core_id;
+ /* When an available_timer expires, We just need to
+ * prune the monitor from the tree. Just because one
+ * available timer expires doesn't mean we should give
+ * up monitoring other devices.
+ */
+ prune_links(child_monitor, core_id, link);
+ AST_LIST_REMOVE(&parent_monitor->child_links, link, next_child);
+ /* We need to set the scheduler ID to -1 here so that
+ * the cancel_available_timer callback will know not to
+ * try to delete the scheduler entry while the scheduler
+ * callback is running.
+ */
+ link->child_avail_id = -1;
+ destroy_link(link);
+
+ /* Of course, if this link is the only remaining child
+ * of a parent extension monitor, this means that all the
+ * other device monitors' available timers have also expired,
+ * which means we need to request a change to CC_FAILED.
+ */
+ if (AST_LIST_EMPTY(&parent_monitor->child_links)) {
+ ast_cc_request_state_change(CC_FAILED, core_id, "All available timers have expired");
+ }
+ return 0;
+}
+
+int ast_cc_available_timer_expire(const void *data)
+{
+ /* It is essential that the link-pruning and destruction process happens in
+ * the core CC thread, so we need to hand this off to the taskprocessor
+ */
+ ast_taskprocessor_push(cc_core_taskprocessor, internal_cc_available_timer_expire, (void *) data);
+ return 0;
+}
+
+
+static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link)
+{
+ struct generic_monitor_pvt *gen_mon_pvt = monitor->private_data;
+ enum ast_cc_service_type service = parent_link->service;
+ int when;
+
+ gen_mon_pvt->current_state = monitor->callbacks->status_request(monitor, core_id);
+
+ /* We can monitor a device for one of two services, CCBS or CCNR.
+ *
+ * In the case of CCBS, When the state of the device changes to "not in use" then
+ * this means that it is ready to be called back. Also, with CCBS, it is important that
+ * we check the current device state in case it changed to "not in use" between when
+ * the initial call failed and now.
+ *
+ * In the case of CCNR, the state of the device will likely already be "not in use." However,
+ * the next time that the device state *changes* to "not in use" that will be indication that
+ * the phone has been used and that we may try ringing the phone again.
+ */
+
+ /* XXX Is there a race condition here? If we happen to see that the status is not "not in use" but then
+ * the device state changes to "not in use" before we are able to subscribe to the device state event, then
+ * will that mean a missed opportunity?
+ */
+ if (service == AST_CC_CCBS && gen_mon_pvt->current_state == AST_DEVICE_NOT_INUSE) {
+ ast_cc_request_state_change(CC_ACTIVE, core_id, "Formality. Device is already available but we have to go through the motions\n");
+ return ast_cc_monitor_announce_availability(monitor);
+ }
+
+ when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
+ ast_get_ccnr_available_timer(monitor->interface->config_params);
+
+ parent_link->child_avail_id = ast_sched_thread_add(cc_sched_thread, when * 1000,
+ ast_cc_available_timer_expire, parent_link);
+
+ if (!gen_mon_pvt->sub && !(gen_mon_pvt->sub = ast_event_subscribe(
+ AST_EVENT_DEVICE_STATE, generic_monitor_devstate_cb, "Requesting CC", monitor,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, monitor->interface->name,
+ AST_EVENT_IE_END))) {
+ return -1;
+ }
+ ast_cc_request_state_change(CC_ACTIVE, core_id, "Generic monitor subscribed to device state.\n");
+ return 0;
+}
+
+static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id)
+{
+ if (monitor->saved_link && monitor->saved_link->core_id == core_id) {
+ /* The link we signaled availability on has become suspended. We need
+ * to NULL out our saved link in this case no matter what, and if the device
+ * is available, we need call the function to announce availability in case
+ * there is another link on which we may announce the availability.
+ */
+ monitor->saved_link = NULL;
+ if (monitor->callbacks->status_request(monitor, core_id) == AST_DEVICE_NOT_INUSE) {
+ ast_cc_monitor_announce_availability(monitor);
+ }
+ }
+ return 0;
+}
+
+static enum ast_device_state cc_generic_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id)
+{
+ return ast_device_state(monitor->interface->name);
+}
+
+static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id)
+{
+ /* When a generic monitor becomes unsuspended, we should do a status request
+ * to see if the device is available. That way, if it is, we can trigger an
+ * immediate state change
+ */
+ if (monitor->callbacks->status_request(monitor, core_id) == AST_DEVICE_NOT_INUSE) {
+ ast_cc_monitor_announce_availability(monitor);
+ }
+ return 0;
+}
+
+
+static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id)
+{
+ ast_assert(sched_id != NULL);
+
+ if (*sched_id == -1) {
+ return 0;
+ }
+
+ ast_log(LOG_NOTICE, "Canceling generic monitor available timer for monitor %s\n", monitor->interface->name);
+ ast_sched_thread_del(cc_sched_thread, *sched_id);
+ return 0;
+}
+
+static void cc_generic_monitor_destructor(void *monitor)
+{
+ struct ast_cc_monitor *mon = monitor;
+ struct generic_monitor_pvt *gen_mon_pvt = mon->private_data;
+
+ ast_log(LOG_NOTICE, "Destroying generic monitor private\n");
+ if (gen_mon_pvt->sub) {
+ gen_mon_pvt->sub = ast_event_unsubscribe(gen_mon_pvt->sub);
+ }
+ ast_free(gen_mon_pvt);
+ return;
+}
#if 0
static int print_debug(void *obj, void *args, int flags)
{
@@ -840,8 +1262,6 @@
return 0;
}
#endif
-
-struct ast_taskprocessor *cc_core_taskprocessor;
void ast_cc_interface_destroy(void *data)
{
@@ -1665,64 +2085,6 @@
int core_id;
char debug[1];
};
-
-static void destroy_link(struct ast_cc_monitor_link *link)
-{
- ast_log(LOG_NOTICE, "Destroying link with parent %s and child %s\n",
- link->parent->interface->name, link->child->interface->name);
- ast_atomic_fetchadd_int(&link->child->num_requests, -1);
- link->child = cc_unref(link->child, "Unref link's child");
- link->parent = cc_unref(link->parent, "Unref link's parent");
- ast_free(link);
- return;
-}
-
-static void prune_links(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *link_parent)
-{
- struct ast_cc_monitor_link *link_iter;
- ast_log(LOG_NOTICE, "Prune links called for monitor %s\n", monitor->interface->name);
-#if 0
- ao2_callback(cc_monitors, OBJ_NODATA, print_debug, NULL);
-#endif
- AST_LIST_TRAVERSE_SAFE_BEGIN(&monitor->child_links, link_iter, next_child) {
- if (link_iter->core_id == core_id) {
- prune_links(link_iter->child, core_id, link_iter);
- AST_LIST_REMOVE_CURRENT(next_child);
- link_iter->child->callbacks->cancel_available_timer(link_iter->child, core_id, &link_iter->child_avail_id);
- destroy_link(link_iter);
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- if (link_parent) {
- ast_log(LOG_NOTICE, "Removing parent link from monitor %s\n", monitor->interface->name);
- AST_LIST_REMOVE(&monitor->parent_links, link_parent, next_parent);
- }
-
- if (monitor->saved_link == link_parent) {
- monitor->saved_link = NULL;
- }
-
- if (monitor->interface->monitor_class == AST_CC_ROOT_MONITOR) {
- /* Never ever under any circumstances unlink
- * the root monitor. Of course, since the root monitor
- * is being acted on here, it means that a CC transaction
- * failed or has completed. So decrease the number of
- * CC requests in the system.
- */
- ast_atomic_fetchadd_int(&cc_request_count, -1);
- ast_log(LOG_NOTICE, "Not unlinking monitor %s because it is the root\n", monitor->interface->name);
- return;
- }
- if (AST_LIST_EMPTY(&monitor->parent_links) &&
- AST_LIST_EMPTY(&monitor->child_links)) {
- /* This should cause the monitor's destructor
- * callback to be called
- */
- ast_log(LOG_NOTICE, "Unlinking monitor %s\n", monitor->interface->name);
- ao2_t_unlink(cc_monitors, monitor, "Unlink monitor since nothing refers to it anymore");
- }
-}
/*!
* \brief pass a state change up the monitor tree
@@ -2056,371 +2418,6 @@
return res;
}
-static int cc_extension_monitor_init(struct ast_cc_monitor *monitor, const int core_id);
-static int cc_extension_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link);
-static int cc_extension_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id);
-static enum ast_device_state cc_extension_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id);
-static int cc_extension_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id);
-static int cc_extension_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id);
-static void cc_extension_monitor_destructor(void *monitor);
-
-static struct ast_cc_monitor_callbacks extension_monitor_cbs = {
- .type = "extension",
- .init = cc_extension_monitor_init,
- .request_cc = cc_extension_monitor_request_cc,
- .suspend = cc_extension_monitor_suspend,
- .status_request = cc_extension_monitor_status_request,
- .unsuspend = cc_extension_monitor_unsuspend,
- .cancel_available_timer = cc_extension_monitor_cancel_available_timer,
- .destructor = cc_extension_monitor_destructor,
-};
-
-static int cc_extension_monitor_init(struct ast_cc_monitor *monitor, const int core_id)
-{
- /* Currently, extension monitors don't really need to hold onto anything. However,
- * it may be necessary to hold onto state information received from child device
- * monitors. It will be much clearer once I begin design of device monitors.
- */
- return 0;
-}
-
-static int cc_extension_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link)
-{
- struct ast_cc_monitor_link *link_iter;
- AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
- if (link_iter->core_id != core_id) {
- continue;
- }
- ast_assert(link_iter->child != NULL);
- ast_assert(link_iter->child->callbacks != NULL);
- if (link_iter->child->callbacks->request_cc(link_iter->child, core_id, link_iter)) {
- prune_links(link_iter->child, core_id, link_iter);
- }
- }
- if (AST_LIST_EMPTY(&monitor->child_links)) {
- /* None of the child monitors successfully requested
- * CC, and so all of the child links were pruned. If this
- * is an extension monitor, just return -1. If this is the
- * root monitor, then that means failure all around, so request
- * a state change to CC_FAILED
- */
- if (monitor->interface->monitor_class == AST_CC_ROOT_MONITOR) {
- ast_cc_request_state_change(CC_FAILED, core_id, "All monitors failed to request CC");
- }
- return -1;
- }
- return 0;
-}
-
-static int cc_extension_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id)
-{
- struct ast_cc_monitor_link *link_iter;
- AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
- if (link_iter->core_id != core_id) {
- continue;
- }
- ast_assert(link_iter->child != NULL);
- ast_assert(link_iter->child->callbacks != NULL);
- link_iter->is_suspended = 1;
- link_iter->child->callbacks->suspend(link_iter->child, core_id);
- }
- return 0;
-}
-
-static enum ast_device_state cc_extension_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id)
-{
- /* So, for an extension monitor, a status request involves traversing all child links that have
- * the proper core_id. As soon as ONE of them returns AST_DEVICE_NOT_INUSE, this means that as
- * far as CC is concerned, the entire extension monitor itself is not in use, so go ahead
- * and return that status.
- */
- struct ast_cc_monitor_link *link;
-
- AST_LIST_TRAVERSE(&monitor->child_links, link, next_child) {
- if (link->child->callbacks->status_request(link->child, core_id) == AST_DEVICE_NOT_INUSE) {
- return AST_DEVICE_NOT_INUSE;
- }
- }
- return AST_DEVICE_INUSE;
-}
-
-static int cc_extension_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id)
-{
- struct ast_cc_monitor_link *link_iter;
- AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
- if (link_iter->core_id != core_id) {
- continue;
- }
- ast_assert(link_iter->child != NULL);
- ast_assert(link_iter->child->callbacks != NULL);
- link_iter->is_suspended = 0;
- link_iter->child->callbacks->unsuspend(link_iter->child, core_id);
- }
- return 0;
-}
-
-static int cc_extension_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id)
-{
- struct ast_cc_monitor_link *link_iter;
- AST_LIST_TRAVERSE(&monitor->child_links, link_iter, next_child) {
- if (link_iter->core_id != core_id) {
- continue;
- }
- ast_assert(link_iter->child != NULL);
- ast_assert(link_iter->child->callbacks != NULL);
- link_iter->child->callbacks->cancel_available_timer(link_iter->child, core_id, &link_iter->child_avail_id);
- }
- return 0;
-}
-
-static void cc_extension_monitor_destructor(void *monitor)
-{
- struct ast_cc_monitor *mon = monitor;
- ast_log(LOG_NOTICE, "Calling destructor for monitor %s\n", mon->interface->name);
- return;
-}
-
-static int cc_generic_monitor_init(struct ast_cc_monitor *monitor, const int core_id);
-static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link);
-static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id);
-static enum ast_device_state cc_generic_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id);
-static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id);
-static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id);
-static void cc_generic_monitor_destructor(void *monitor);
-
-static struct ast_cc_monitor_callbacks generic_monitor_cbs = {
- .type = "generic",
- .init = cc_generic_monitor_init,
- .request_cc = cc_generic_monitor_request_cc,
- .suspend = cc_generic_monitor_suspend,
- .status_request = cc_generic_monitor_status_request,
- .unsuspend = cc_generic_monitor_unsuspend,
- .cancel_available_timer = cc_generic_monitor_cancel_available_timer,
- .destructor = cc_generic_monitor_destructor,
-};
-
-/*!
- * \brief private data for generic device monitor
- */
-struct generic_monitor_pvt {
- /*!
- * Handle for event subscription to device state
- */
- struct ast_event_sub *sub;
- /*!
- * Cached device state for this monitor
- */
- enum ast_device_state current_state;
-};
-
-static int cc_generic_monitor_init(struct ast_cc_monitor *monitor, const int core_id)
-{
- struct generic_monitor_pvt *gen_mon_pvt = ast_calloc(1, sizeof(*gen_mon_pvt));
-
- if (!gen_mon_pvt) {
- return -1;
- }
- ast_log(LOG_NOTICE, "Allocated a generic monitor pvt for device %s\n", monitor->interface->name);
- monitor->private_data = gen_mon_pvt;
- return 0;
-}
-
-struct generic_tp_cb_data {
- struct ast_cc_monitor *monitor;
- enum ast_device_state new_state;
-};
-
-static int generic_monitor_devstate_tp_cb(void *data)
-{
- struct generic_tp_cb_data *gtcd = data;
- struct ast_cc_monitor *monitor = gtcd->monitor;
- enum ast_device_state new_state = gtcd->new_state;
- struct generic_monitor_pvt *gen_mon_pvt = monitor->private_data;
-
- if (gen_mon_pvt->current_state == new_state) {
- ast_free(gtcd);
- cc_unref(monitor, "Kill reference from generic_monitor_devstate_cb");
- return 0;
- }
-
- gen_mon_pvt->current_state = new_state;
-
- if (new_state == AST_DEVICE_NOT_INUSE) {
- ast_cc_monitor_announce_availability(monitor);
- }
- cc_unref(monitor, "Kill reference from generic_monitor_devstate_cb");
- ast_free(gtcd);
- return 0;
-}
-
-static void generic_monitor_devstate_cb(const struct ast_event *event, void *userdata)
-{
- /* Wow, it's cool that we've picked up on a state change, but we really want
- * the actual work to be done in the core's taskprocessor execution thread
- * so that all monitor operations can be serialized. Locks?! We don't need
- * no steenkin' locks!
- */
- struct ast_cc_monitor *monitor = userdata;
- struct generic_tp_cb_data *gtcd = ast_calloc(1, sizeof(*gtcd));
-
- if (!gtcd) {
- return;
- }
-
- gtcd->monitor = cc_ref(monitor, "Keep reference until taskprocessor callback executes");
- gtcd->new_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
-
- ast_taskprocessor_push(cc_core_taskprocessor, generic_monitor_devstate_tp_cb, gtcd);
-
- return;
-}
-
-static int internal_cc_available_timer_expire(void *data)
-{
- struct ast_cc_monitor_link *link = data;
- struct ast_cc_monitor *parent_monitor = link->parent;
- struct ast_cc_monitor *child_monitor = link->child;
- int core_id = link->core_id;
- /* When an available_timer expires, We just need to
- * prune the monitor from the tree. Just because one
- * available timer expires doesn't mean we should give
- * up monitoring other devices.
- */
- prune_links(child_monitor, core_id, link);
- AST_LIST_REMOVE(&parent_monitor->child_links, link, next_child);
- /* We need to set the scheduler ID to -1 here so that
- * the cancel_available_timer callback will know not to
- * try to delete the scheduler entry while the scheduler
- * callback is running.
- */
- link->child_avail_id = -1;
- destroy_link(link);
-
- /* Of course, if this link is the only remaining child
- * of a parent extension monitor, this means that all the
- * other device monitors' available timers have also expired,
- * which means we need to request a change to CC_FAILED.
- */
- if (AST_LIST_EMPTY(&parent_monitor->child_links)) {
- ast_cc_request_state_change(CC_FAILED, core_id, "All available timers have expired");
- }
- return 0;
-}
-
-int ast_cc_available_timer_expire(const void *data)
-{
- /* It is essential that the link-pruning and destruction process happens in
- * the core CC thread, so we need to hand this off to the taskprocessor
- */
- ast_taskprocessor_push(cc_core_taskprocessor, internal_cc_available_timer_expire, (void *) data);
- return 0;
-}
-
-
-static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, const int core_id, struct ast_cc_monitor_link *parent_link)
-{
- struct generic_monitor_pvt *gen_mon_pvt = monitor->private_data;
- enum ast_cc_service_type service = parent_link->service;
- int when;
-
- gen_mon_pvt->current_state = monitor->callbacks->status_request(monitor, core_id);
-
- /* We can monitor a device for one of two services, CCBS or CCNR.
- *
- * In the case of CCBS, When the state of the device changes to "not in use" then
- * this means that it is ready to be called back. Also, with CCBS, it is important that
- * we check the current device state in case it changed to "not in use" between when
- * the initial call failed and now.
- *
- * In the case of CCNR, the state of the device will likely already be "not in use." However,
- * the next time that the device state *changes* to "not in use" that will be indication that
- * the phone has been used and that we may try ringing the phone again.
- */
-
- /* XXX Is there a race condition here? If we happen to see that the status is not "not in use" but then
- * the device state changes to "not in use" before we are able to subscribe to the device state event, then
- * will that mean a missed opportunity?
- */
- if (service == AST_CC_CCBS && gen_mon_pvt->current_state == AST_DEVICE_NOT_INUSE) {
- ast_cc_request_state_change(CC_ACTIVE, core_id, "Formality. Device is already available but we have to go through the motions\n");
- return ast_cc_monitor_announce_availability(monitor);
- }
-
- when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
- ast_get_ccnr_available_timer(monitor->interface->config_params);
-
- parent_link->child_avail_id = ast_sched_thread_add(cc_sched_thread, when * 1000,
- ast_cc_available_timer_expire, parent_link);
-
- if (!gen_mon_pvt->sub && !(gen_mon_pvt->sub = ast_event_subscribe(
- AST_EVENT_DEVICE_STATE, generic_monitor_devstate_cb, "Requesting CC", monitor,
- AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, monitor->interface->name,
- AST_EVENT_IE_END))) {
- return -1;
- }
- ast_cc_request_state_change(CC_ACTIVE, core_id, "Generic monitor subscribed to device state.\n");
- return 0;
-}
-
-static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor, const int core_id)
-{
- if (monitor->saved_link && monitor->saved_link->core_id == core_id) {
- /* The link we signaled availability on has become suspended. We need
- * to NULL out our saved link in this case no matter what, and if the device
- * is available, we need call the function to announce availability in case
- * there is another link on which we may announce the availability.
- */
- monitor->saved_link = NULL;
- if (monitor->callbacks->status_request(monitor, core_id) == AST_DEVICE_NOT_INUSE) {
- ast_cc_monitor_announce_availability(monitor);
- }
- }
- return 0;
-}
-
-static enum ast_device_state cc_generic_monitor_status_request(struct ast_cc_monitor *monitor, const int core_id)
-{
- return ast_device_state(monitor->interface->name);
-}
-
-static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor, const int core_id)
-{
- /* When a generic monitor becomes unsuspended, we should do a status request
- * to see if the device is available. That way, if it is, we can trigger an
- * immediate state change
- */
- if (monitor->callbacks->status_request(monitor, core_id) == AST_DEVICE_NOT_INUSE) {
- ast_cc_monitor_announce_availability(monitor);
- }
- return 0;
-}
-
-
-static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, const int core_id, int *sched_id)
-{
- ast_assert(sched_id != NULL);
-
- if (*sched_id == -1) {
- return 0;
- }
-
- ast_log(LOG_NOTICE, "Canceling generic monitor available timer for monitor %s\n", monitor->interface->name);
- ast_sched_thread_del(cc_sched_thread, *sched_id);
- return 0;
-}
-
-static void cc_generic_monitor_destructor(void *monitor)
-{
- struct ast_cc_monitor *mon = monitor;
- struct generic_monitor_pvt *gen_mon_pvt = mon->private_data;
-
- ast_log(LOG_NOTICE, "Destroying generic monitor private\n");
- if (gen_mon_pvt->sub) {
- gen_mon_pvt->sub = ast_event_unsubscribe(gen_mon_pvt->sub);
- }
- ast_free(gen_mon_pvt);
- return;
-}
int ast_cc_monitor_count(const char * const name, const char * const type)
{
More information about the asterisk-commits
mailing list