[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