[asterisk-commits] russell: branch 1.8 r393928 - in /branches/1.8: apps/ configs/ include/asterisk/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jul 9 20:41:45 CDT 2013


Author: russell
Date: Tue Jul  9 20:41:42 2013
New Revision: 393928

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393928
Log:
astobj2-ify the SLA code

The SLA code within app_meetme was written before asotbj2 had been
merged into Asterisk.  Worse, support for reloads did not exist at first
and was added later as a bolt-on feature.  I knew at the time that
reloading was not safe at all while SLA was in use, so the reload would
be queued up to execute when the system was idle.  Unfortunately, this
approach was still prone to errors beyond the fact that this was the
only place in Asterisk where configuration was not reloaded
instantly when requested.

This patch converts various SLA objects to be reference counted objects
using astobj2.  This allows reloads to be processed while the system is
in use.  The code ensures that the objects will not disappear while one
of the other threads is using them.  However, they will be immediately
removed from the global trunk and station containers so no new calls
will use them if removed from configuration.

Review: https://reviewboard.asterisk.org/r/2581/

Modified:
    branches/1.8/apps/app_meetme.c
    branches/1.8/configs/sla.conf.sample
    branches/1.8/include/asterisk/utils.h

Modified: branches/1.8/apps/app_meetme.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8/apps/app_meetme.c?view=diff&rev=393928&r1=393927&r2=393928
==============================================================================
--- branches/1.8/apps/app_meetme.c (original)
+++ branches/1.8/apps/app_meetme.c Tue Jul  9 20:41:42 2013
@@ -832,17 +832,26 @@
 	/*! This option uses the values in the sla_hold_access enum and sets the
 	 * access control type for hold on this station. */
 	unsigned int hold_access:1;
-	/*! Use count for inside sla_station_exec */
-	unsigned int ref_count;
+	/*! Mark used during reload processing */
+	unsigned int mark:1;
 };
 
+/*!
+ * \brief A reference to a station
+ *
+ * This struct looks near useless at first glance.  However, its existence
+ * in the list of stations in sla_trunk means that this station references
+ * that trunk.  We use the mark to keep track of whether it needs to be
+ * removed from the sla_trunk's list of stations during a reload.
+ */
 struct sla_station_ref {
 	AST_LIST_ENTRY(sla_station_ref) entry;
 	struct sla_station *station;
+	/*! Mark used during reload processing */
+	unsigned int mark:1;
 };
 
 struct sla_trunk {
-	AST_RWLIST_ENTRY(sla_trunk) entry;
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(name);
 		AST_STRING_FIELD(device);
@@ -866,10 +875,16 @@
 	/*! Whether this trunk is currently on hold, meaning that once a station
 	 *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
 	unsigned int on_hold:1;
-	/*! Use count for inside sla_trunk_exec */
-	unsigned int ref_count;
+	/*! Mark used during reload processing */
+	unsigned int mark:1;
 };
 
+/*!
+ * \brief A station's reference to a trunk
+ *
+ * An sla_station keeps a list of trunk_refs.  This holds metadata about the
+ * stations usage of the trunk.
+ */
 struct sla_trunk_ref {
 	AST_LIST_ENTRY(sla_trunk_ref) entry;
 	struct sla_trunk *trunk;
@@ -883,10 +898,12 @@
 	 *  station.  This takes higher priority than a ring delay set at
 	 *  the station level. */
 	unsigned int ring_delay;
+	/*! Mark used during reload processing */
+	unsigned int mark:1;
 };
 
-static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
-static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
+static struct ao2_container *sla_stations;
+static struct ao2_container *sla_trunks;
 
 static const char sla_registrar[] = "SLA";
 
@@ -898,10 +915,6 @@
 	SLA_EVENT_DIAL_STATE,
 	/*! The state of a ringing trunk has changed */
 	SLA_EVENT_RINGING_TRUNK,
-	/*! A reload of configuration has been requested */
-	SLA_EVENT_RELOAD,
-	/*! Poke the SLA thread so it can check if it can perform a reload */
-	SLA_EVENT_CHECK_RELOAD,
 };
 
 struct sla_event {
@@ -957,8 +970,6 @@
 	/*! Attempt to handle CallerID, even though it is known not to work
 	 *  properly in some situations. */
 	unsigned int attempt_callerid:1;
-	/*! A reload has been requested */
-	unsigned int reload:1;
 } sla = {
 	.thread = AST_PTHREADT_NULL,
 };
@@ -1719,7 +1730,8 @@
 
 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	const struct sla_trunk *trunk;
+	struct ao2_iterator i;
+	struct sla_trunk *trunk;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -1737,12 +1749,17 @@
 	            "=== Configured SLA Trunks ===================================\n"
 	            "=============================================================\n"
 	            "===\n");
-	AST_RWLIST_RDLOCK(&sla_trunks);
-	AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+	i = ao2_iterator_init(sla_trunks, 0);
+	for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
 		struct sla_station_ref *station_ref;
 		char ring_timeout[16] = "(none)";
-		if (trunk->ring_timeout)
+
+		ao2_lock(trunk);
+
+		if (trunk->ring_timeout) {
 			snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
+		}
+
 		ast_cli(a->fd, "=== ---------------------------------------------------------\n"
 		            "=== Trunk Name:       %s\n"
 		            "=== ==> Device:       %s\n"
@@ -1756,13 +1773,16 @@
 		            ring_timeout,
 		            trunk->barge_disabled ? "No" : "Yes",
 		            sla_hold_str(trunk->hold_access));
-		AST_RWLIST_RDLOCK(&sla_stations);
-		AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
+
+		AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
 			ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
-		AST_RWLIST_UNLOCK(&sla_stations);
+		}
+
 		ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
-	}
-	AST_RWLIST_UNLOCK(&sla_trunks);
+
+		ao2_unlock(trunk);
+	}
+	ao2_iterator_destroy(&i);
 	ast_cli(a->fd, "=============================================================\n\n");
 
 	return CLI_SUCCESS;
@@ -1784,7 +1804,8 @@
 
 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	const struct sla_station *station;
+	struct ao2_iterator i;
+	struct sla_station *station;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -1802,11 +1823,14 @@
 	            "=== Configured SLA Stations =================================\n"
 	            "=============================================================\n"
 	            "===\n");
-	AST_RWLIST_RDLOCK(&sla_stations);
-	AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+	i = ao2_iterator_init(sla_stations, 0);
+	for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
 		struct sla_trunk_ref *trunk_ref;
 		char ring_timeout[16] = "(none)";
 		char ring_delay[16] = "(none)";
+
+		ao2_lock(station);
+
 		if (station->ring_timeout) {
 			snprintf(ring_timeout, sizeof(ring_timeout), 
 				"%u", station->ring_timeout);
@@ -1827,7 +1851,6 @@
 		            S_OR(station->autocontext, "(none)"), 
 		            ring_timeout, ring_delay,
 		            sla_hold_str(station->hold_access));
-		AST_RWLIST_RDLOCK(&sla_trunks);
 		AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
 			if (trunk_ref->ring_timeout) {
 				snprintf(ring_timeout, sizeof(ring_timeout),
@@ -1847,11 +1870,12 @@
 			            trunkstate2str(trunk_ref->state),
 			            ring_timeout, ring_delay);
 		}
-		AST_RWLIST_UNLOCK(&sla_trunks);
 		ast_cli(a->fd, "=== ---------------------------------------------------------\n"
 		            "===\n");
-	}
-	AST_RWLIST_UNLOCK(&sla_stations);
+
+		ao2_unlock(station);
+	}
+	ao2_iterator_destroy(&i);
 	ast_cli(a->fd, "============================================================\n"
 	            "\n");
 
@@ -1991,11 +2015,16 @@
 	struct sla_event *event;
 
 	if (sla.thread == AST_PTHREADT_NULL) {
+		ao2_ref(station, -1);
+		ao2_ref(trunk_ref, -1);
 		return;
 	}
 
-	if (!(event = ast_calloc(1, sizeof(*event))))
+	if (!(event = ast_calloc(1, sizeof(*event)))) {
+		ao2_ref(station, -1);
+		ao2_ref(trunk_ref, -1);
 		return;
+	}
 
 	event->type = type;
 	event->trunk_ref = trunk_ref;
@@ -2029,6 +2058,7 @@
 	struct sla_station *station;
 	struct sla_trunk_ref *trunk_ref = NULL;
 	char *trunk_name;
+	struct ao2_iterator i;
 
 	trunk_name = ast_strdupa(conf->confno);
 	strsep(&trunk_name, "_");
@@ -2037,16 +2067,23 @@
 		return;
 	}
 
-	AST_RWLIST_RDLOCK(&sla_stations);
-	AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+	i = ao2_iterator_init(sla_stations, 0);
+	while ((station = ao2_iterator_next(&i))) {
+		ao2_lock(station);
 		AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
-			if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
+			if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
+				ao2_ref(trunk_ref, 1);
 				break;
-		}
-		if (trunk_ref)
+			}
+		}
+		ao2_unlock(station);
+		if (trunk_ref) {
+			/* station reference given to sla_queue_event_full() */
 			break;
-	}
-	AST_RWLIST_UNLOCK(&sla_stations);
+		}
+		ao2_ref(station, -1);
+	}
+	ao2_iterator_destroy(&i);
 
 	if (!trunk_ref) {
 		ast_debug(1, "Trunk not found for event!\n");
@@ -5292,34 +5329,41 @@
 	ast_config_destroy(cfg);
 }
 
-/*! \brief Find an SLA trunk by name
- * \note This must be called with the sla_trunks container locked
+/*!
+ * \private
+ * \brief helper for RAII_VAR
+ */
+static void unref_obj(void *obj)
+{
+	if (obj) {
+		ao2_ref(obj, -1);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Find an SLA trunk by name
  */
 static struct sla_trunk *sla_find_trunk(const char *name)
 {
-	struct sla_trunk *trunk = NULL;
-
-	AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
-		if (!strcasecmp(trunk->name, name))
-			break;
-	}
-
-	return trunk;
-}
-
-/*! \brief Find an SLA station by name
- * \note This must be called with the sla_stations container locked
+	struct sla_trunk tmp_trunk = {
+		.name = name,
+	};
+
+	return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
+}
+
+/*!
+ * \internal
+ * \brief Find an SLA station by name
  */
 static struct sla_station *sla_find_station(const char *name)
 {
-	struct sla_station *station = NULL;
-
-	AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
-		if (!strcasecmp(station->name, name))
-			break;
-	}
-
-	return station;
+	struct sla_station tmp_station = {
+		.name = name,
+	};
+
+	return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
 }
 
 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
@@ -5343,9 +5387,11 @@
 	return 0;
 }
 
-/*! \brief Find a trunk reference on a station by name
+/*!
+ * \brief Find a trunk reference on a station by name
  * \param station the station
  * \param name the trunk's name
+ * \pre sla_station is locked
  * \return a pointer to the station's trunk reference.  If the trunk
  *         is not found, it is not idle and barge is disabled, or if
  *         it is on hold and private hold is set, then NULL will be returned.
@@ -5372,16 +5418,32 @@
 		break;
 	}
 
+	if (trunk_ref) {
+		ao2_ref(trunk_ref, 1);
+	}
+
 	return trunk_ref;
 }
 
+static void sla_station_ref_destructor(void *obj)
+{
+	struct sla_station_ref *station_ref = obj;
+
+	if (station_ref->station) {
+		ao2_ref(station_ref->station, -1);
+		station_ref->station = NULL;
+	}
+}
+
 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
 {
 	struct sla_station_ref *station_ref;
 
-	if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
+	if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
 		return NULL;
-
+	}
+
+	ao2_ref(station, 1);
 	station_ref->station = station;
 
 	return station_ref;
@@ -5394,10 +5456,46 @@
 	if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
 		return NULL;
 
+	ao2_ref(station, 1);
 	ringing_station->station = station;
 	ringing_station->ring_begin = ast_tvnow();
 
 	return ringing_station;
+}
+
+static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
+{
+	if (ringing_station->station) {
+		ao2_ref(ringing_station->station, -1);
+		ringing_station->station = NULL;
+	}
+
+	ast_free(ringing_station);
+}
+
+static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
+{
+	struct sla_failed_station *failed_station;
+
+	if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
+		return NULL;
+	}
+
+	ao2_ref(station, 1);
+	failed_station->station = station;
+	failed_station->last_try = ast_tvnow();
+
+	return failed_station;
+}
+
+static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
+{
+	if (failed_station->station) {
+		ao2_ref(failed_station->station, -1);
+		failed_station->station = NULL;
+	}
+
+	ast_free(failed_station);
 }
 
 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
@@ -5422,18 +5520,25 @@
 {
 	struct sla_station *station;
 	struct sla_trunk_ref *trunk_ref;
-
-	AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+	struct ao2_iterator i;
+
+	i = ao2_iterator_init(sla_stations, 0);
+	while ((station = ao2_iterator_next(&i))) {
+		ao2_lock(station);
 		AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
 			if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
-				|| trunk_ref == exclude)
+					|| trunk_ref == exclude) {
 				continue;
+			}
 			trunk_ref->state = state;
 			ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
 					     "SLA:%s_%s", station->name, trunk->name);
 			break;
 		}
-	}
+		ao2_unlock(station);
+		ao2_ref(station, -1);
+	}
+	ao2_iterator_destroy(&i);
 }
 
 struct run_station_args {
@@ -5451,8 +5556,8 @@
 
 static void *run_station(void *data)
 {
-	struct sla_station *station;
-	struct sla_trunk_ref *trunk_ref;
+	RAII_VAR(struct sla_station *, station, NULL, unref_obj);
+	RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
 	struct ast_str *conf_name = ast_str_create(16);
 	struct ast_flags64 conf_flags = { 0 };
 	struct ast_conference *conf;
@@ -5495,6 +5600,8 @@
 	return NULL;
 }
 
+static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
+
 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
 {
 	char buf[80];
@@ -5504,10 +5611,11 @@
 	admin_exec(NULL, buf);
 	sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
 
-	while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
-		ast_free(station_ref);
-
-	ast_free(ringing_trunk);
+	while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
+		ao2_ref(station_ref, -1);
+	}
+
+	sla_ringing_trunk_destroy(ringing_trunk);
 }
 
 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
@@ -5542,7 +5650,7 @@
 	}
 
 done:
-	ast_free(ringing_station);
+	sla_ringing_station_destroy(ringing_station);
 }
 
 static void sla_dial_state_callback(struct ast_dial *dial)
@@ -5594,8 +5702,10 @@
 			if (rm)
 				AST_LIST_REMOVE_CURRENT(entry);
 
-			if (trunk_ref)
+			if (trunk_ref) {
+				ao2_ref(s_trunk_ref, 1);
 				*trunk_ref = s_trunk_ref;
+			}
 
 			break;
 		}
@@ -5613,7 +5723,7 @@
 	struct sla_ringing_station *ringing_station;
 
 	AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
-		struct sla_trunk_ref *s_trunk_ref = NULL;
+		RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, unref_obj);
 		struct sla_ringing_trunk *ringing_trunk = NULL;
 		struct run_station_args args;
 		enum ast_dial_result dial_res;
@@ -5646,7 +5756,7 @@
 				ast_dial_join(ringing_station->station->dial);
 				ast_dial_destroy(ringing_station->station->dial);
 				ringing_station->station->dial = NULL;
-				ast_free(ringing_station);
+				sla_ringing_station_destroy(ringing_station);
 				break;
 			}
 			/* Track the channel that answered this trunk */
@@ -5657,12 +5767,14 @@
 			/* Now, start a thread that will connect this station to the trunk.  The rest of
 			 * the code here sets up the thread and ensures that it is able to save the arguments
 			 * before they are no longer valid since they are allocated on the stack. */
+			ao2_ref(s_trunk_ref, 1);
 			args.trunk_ref = s_trunk_ref;
+			ao2_ref(ringing_station->station, 1);
 			args.station = ringing_station->station;
 			args.cond = &cond;
 			args.cond_lock = &cond_lock;
-			ast_free(ringing_trunk);
-			ast_free(ringing_station);
+			sla_ringing_trunk_destroy(ringing_trunk);
+			sla_ringing_station_destroy(ringing_station);
 			ast_mutex_init(&cond_lock);
 			ast_cond_init(&cond, NULL);
 			ast_mutex_lock(&cond_lock);
@@ -5716,7 +5828,7 @@
 			continue;
 		if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
 			AST_LIST_REMOVE_CURRENT(entry);
-			ast_free(failed_station);
+			sla_failed_station_destroy(failed_station);
 			break;
 		}
 		res = 1;
@@ -5769,11 +5881,9 @@
 	if (res != AST_DIAL_RESULT_TRYING) {
 		struct sla_failed_station *failed_station;
 		ast_dial_destroy(dial);
-		if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
-			return -1;
-		failed_station->station = station;
-		failed_station->last_try = ast_tvnow();
-		AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+		if ((failed_station = sla_create_failed_station(station))) {
+			AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+		}
 		return -1;
 	}
 	if (!(ringing_station = sla_create_ringing_station(station))) {
@@ -5812,6 +5922,8 @@
 		if (trunk_ref->trunk == trunk)
 			break;
 	}
+
+	ao2_ref(trunk_ref, 1);
 
 	return trunk_ref;
 }
@@ -5824,7 +5936,7 @@
 static int sla_check_station_delay(struct sla_station *station, 
 	struct sla_ringing_trunk *ringing_trunk)
 {
-	struct sla_trunk_ref *trunk_ref;
+	RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
 	unsigned int delay = UINT_MAX;
 	int time_left, time_elapsed;
 
@@ -5917,7 +6029,7 @@
 			ast_dial_join(ringing_station->station->dial);
 			ast_dial_destroy(ringing_station->station->dial);
 			ringing_station->station->dial = NULL;
-			ast_free(ringing_station);
+			sla_ringing_station_destroy(ringing_station);
 		}
 	}
 	AST_LIST_TRAVERSE_SAFE_END
@@ -6074,8 +6186,10 @@
 {
 	struct sla_station *station;
 	int res = 0;
-
-	AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+	struct ao2_iterator i;
+
+	i = ao2_iterator_init(sla_stations, 0);
+	for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
 		struct sla_ringing_trunk *ringing_trunk;
 		int time_left;
 
@@ -6105,6 +6219,7 @@
 		if (time_left < *timeout)
 			*timeout = time_left;
 	}
+	ao2_iterator_destroy(&i);
 
 	return res;
 }
@@ -6146,50 +6261,19 @@
 	return 1;
 }
 
-static int sla_load_config(int reload);
-
-/*! \brief Check if we can do a reload of SLA, and do it if we can */
-static void sla_check_reload(void)
-{
-	struct sla_station *station;
-	struct sla_trunk *trunk;
-
-	ast_mutex_lock(&sla.lock);
-
-	if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
-		|| !AST_LIST_EMPTY(&sla.ringing_stations) || !AST_LIST_EMPTY(&sla.failed_stations)) {
-		ast_mutex_unlock(&sla.lock);
-		return;
-	}
-
-	AST_RWLIST_RDLOCK(&sla_stations);
-	AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
-		if (station->ref_count)
-			break;
-	}
-	AST_RWLIST_UNLOCK(&sla_stations);
-	if (station) {
-		ast_mutex_unlock(&sla.lock);
-		return;
-	}
-
-	AST_RWLIST_RDLOCK(&sla_trunks);
-	AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
-		if (trunk->ref_count || trunk->chan || trunk->active_stations || trunk->hold_stations) {
-			break;
-		}
-	}
-	AST_RWLIST_UNLOCK(&sla_trunks);
-	if (trunk) {
-		ast_mutex_unlock(&sla.lock);
-		return;
-	}
-
-	/* yay */
-	sla_load_config(1);
-	sla.reload = 0;
-
-	ast_mutex_unlock(&sla.lock);
+static void sla_event_destroy(struct sla_event *event)
+{
+	if (event->trunk_ref) {
+		ao2_ref(event->trunk_ref, -1);
+		event->trunk_ref = NULL;
+	}
+
+	if (event->station) {
+		ao2_ref(event->station, -1);
+		event->station = NULL;
+	}
+
+	ast_free(event);
 }
 
 static void *sla_thread(void *data)
@@ -6228,27 +6312,21 @@
 			case SLA_EVENT_RINGING_TRUNK:
 				sla_handle_ringing_trunk_event();
 				break;
-			case SLA_EVENT_RELOAD:
-				sla.reload = 1;
-			case SLA_EVENT_CHECK_RELOAD:
-				break;
-			}
-			ast_free(event);
+			}
+			sla_event_destroy(event);
 			ast_mutex_lock(&sla.lock);
 		}
-
-		if (sla.reload) {
-			sla_check_reload();
-		}
 	}
 
 	ast_mutex_unlock(&sla.lock);
 
-	while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
-		ast_free(ringing_station);
-
-	while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
-		ast_free(failed_station);
+	while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
+		sla_ringing_station_destroy(ringing_station);
+	}
+
+	while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
+		sla_failed_station_destroy(failed_station);
+	}
 
 	return NULL;
 }
@@ -6269,7 +6347,8 @@
 	char conf_name[MAX_CONFNUM];
 	struct ast_conference *conf;
 	struct ast_flags64 conf_flags = { 0 };
-	struct sla_trunk_ref *trunk_ref = args->trunk_ref;
+	RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, unref_obj);
+	RAII_VAR(struct sla_station *, station, args->station, unref_obj);
 	int caller_is_saved;
 	struct ast_party_caller caller;
 	int last_state = 0;
@@ -6341,8 +6420,8 @@
 			break;
 
 		/* check that SLA station that originated trunk call is still alive */
-		if (args->station && ast_device_state(args->station->device) == AST_DEVICE_NOT_INUSE) {
-			ast_debug(3, "Originating station device %s no longer active\n", args->station->device);
+		if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
+			ast_debug(3, "Originating station device %s no longer active\n", station->device);
 			trunk_ref->trunk->chan = NULL;
 			break;
 		}
@@ -6395,15 +6474,19 @@
 	return NULL;
 }
 
-/*! \brief For a given station, choose the highest priority idle trunk
+/*!
+ * \brief For a given station, choose the highest priority idle trunk
+ * \pre sla_station is locked
  */
 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
 {
 	struct sla_trunk_ref *trunk_ref = NULL;
 
 	AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
-		if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
+		if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
+			ao2_ref(trunk_ref, 1);
 			break;
+		}
 	}
 
 	return trunk_ref;
@@ -6412,8 +6495,8 @@
 static int sla_station_exec(struct ast_channel *chan, const char *data)
 {
 	char *station_name, *trunk_name;
-	struct sla_station *station;
-	struct sla_trunk_ref *trunk_ref = NULL;
+	RAII_VAR(struct sla_station *, station, NULL, unref_obj);
+	RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
 	char conf_name[MAX_CONFNUM];
 	struct ast_flags64 conf_flags = { 0 };
 	struct ast_conference *conf;
@@ -6433,25 +6516,21 @@
 		return 0;
 	}
 
-	AST_RWLIST_WRLOCK(&sla_stations);
 	station = sla_find_station(station_name);
-	if (station)
-		ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
-	AST_RWLIST_UNLOCK(&sla_stations);
 
 	if (!station) {
 		ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
 		pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
-		sla_queue_event(SLA_EVENT_CHECK_RELOAD);
 		return 0;
 	}
 
-	AST_RWLIST_RDLOCK(&sla_trunks);
+	ao2_lock(station);
 	if (!ast_strlen_zero(trunk_name)) {
 		trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
-	} else
+	} else {
 		trunk_ref = sla_choose_idle_trunk(station);
-	AST_RWLIST_UNLOCK(&sla_trunks);
+	}
+	ao2_unlock(station);
 
 	if (!trunk_ref) {
 		if (ast_strlen_zero(trunk_name))
@@ -6461,8 +6540,6 @@
 				"'%s' due to access controls.\n", trunk_name);
 		}
 		pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
-		ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
-		sla_queue_event(SLA_EVENT_CHECK_RELOAD);
 		return 0;
 	}
 
@@ -6491,7 +6568,7 @@
 			answer_trunk_chan(ringing_trunk->trunk->chan);
 			sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
 
-			free(ringing_trunk);
+			sla_ringing_trunk_destroy(ringing_trunk);
 
 			/* Queue up reprocessing ringing trunks, and then ringing stations again */
 			sla_queue_event(SLA_EVENT_RINGING_TRUNK);
@@ -6511,6 +6588,8 @@
 			.cond_lock = &cond_lock,
 			.cond = &cond,
 		};
+		ao2_ref(trunk_ref, 1);
+		ao2_ref(station, 1);
 		sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
 		/* Create a thread to dial the trunk and dump it into the conference.
 		 * However, we want to wait until the trunk has been dialed and the
@@ -6530,8 +6609,6 @@
 			pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
 			sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
 			trunk_ref->chan = NULL;
-			ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
-			sla_queue_event(SLA_EVENT_CHECK_RELOAD);
 			return 0;
 		}
 	}
@@ -6564,19 +6641,28 @@
 	
 	pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
 
-	ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
-	sla_queue_event(SLA_EVENT_CHECK_RELOAD);
-
 	return 0;
 }
 
+static void sla_trunk_ref_destructor(void *obj)
+{
+	struct sla_trunk_ref *trunk_ref = obj;
+
+	if (trunk_ref->trunk) {
+		ao2_ref(trunk_ref->trunk, -1);
+		trunk_ref->trunk = NULL;
+	}
+}
+
 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
 {
 	struct sla_trunk_ref *trunk_ref;
 
-	if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
+	if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
 		return NULL;
-
+	}
+
+	ao2_ref(trunk, 1);
 	trunk_ref->trunk = trunk;
 
 	return trunk_ref;
@@ -6586,9 +6672,11 @@
 {
 	struct sla_ringing_trunk *ringing_trunk;
 
-	if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
+	if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
 		return NULL;
-	
+	}
+
+	ao2_ref(trunk, 1);
 	ringing_trunk->trunk = trunk;
 	ringing_trunk->ring_begin = ast_tvnow();
 
@@ -6601,6 +6689,16 @@
 	sla_queue_event(SLA_EVENT_RINGING_TRUNK);
 
 	return ringing_trunk;
+}
+
+static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
+{
+	if (ringing_trunk->trunk) {
+		ao2_ref(ringing_trunk->trunk, -1);
+		ringing_trunk->trunk = NULL;
+	}
+
+	ast_free(ringing_trunk);
 }
 
 enum {
@@ -6621,7 +6719,7 @@
 	char conf_name[MAX_CONFNUM];
 	struct ast_conference *conf;
 	struct ast_flags64 conf_flags = { 0 };
-	struct sla_trunk *trunk;
+	RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
 	struct sla_ringing_trunk *ringing_trunk;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(trunk_name);
@@ -6645,16 +6743,11 @@
 		}
 	}
 
-	AST_RWLIST_WRLOCK(&sla_trunks);
 	trunk = sla_find_trunk(args.trunk_name);
-	if (trunk)
-		ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
-	AST_RWLIST_UNLOCK(&sla_trunks);
 
 	if (!trunk) {
 		ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
 		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-		sla_queue_event(SLA_EVENT_CHECK_RELOAD);	
 		return 0;
 	}
 
@@ -6662,8 +6755,6 @@
 		ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
 			args.trunk_name);
 		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-		ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-		sla_queue_event(SLA_EVENT_CHECK_RELOAD);	
 		return 0;
 	}
 
@@ -6671,8 +6762,6 @@
 
 	if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
 		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-		ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-		sla_queue_event(SLA_EVENT_CHECK_RELOAD);	
 		return 0;
 	}
 
@@ -6680,8 +6769,6 @@
 	conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
 	if (!conf) {
 		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
-		ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-		sla_queue_event(SLA_EVENT_CHECK_RELOAD);	
 		return 0;
 	}
 	ast_set_flag64(&conf_flags, 
@@ -6715,46 +6802,37 @@
 	AST_LIST_TRAVERSE_SAFE_END;
 	ast_mutex_unlock(&sla.lock);
 	if (ringing_trunk) {
-		ast_free(ringing_trunk);
+		sla_ringing_trunk_destroy(ringing_trunk);
 		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
 		/* Queue reprocessing of ringing trunks to make stations stop ringing
 		 * that shouldn't be ringing after this trunk stopped. */
 		sla_queue_event(SLA_EVENT_RINGING_TRUNK);
 	}
 
-	ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
-	sla_queue_event(SLA_EVENT_CHECK_RELOAD);	
-
 	return 0;
 }
 
 static enum ast_device_state sla_state(const char *data)
 {
 	char *buf, *station_name, *trunk_name;
-	struct sla_station *station;
+	RAII_VAR(struct sla_station *, station, NULL, unref_obj);
 	struct sla_trunk_ref *trunk_ref;
 	enum ast_device_state res = AST_DEVICE_INVALID;
 
 	trunk_name = buf = ast_strdupa(data);
 	station_name = strsep(&trunk_name, "_");
 
-	AST_RWLIST_RDLOCK(&sla_stations);
-	AST_LIST_TRAVERSE(&sla_stations, station, entry) {
-		if (strcasecmp(station_name, station->name))
-			continue;
-		AST_RWLIST_RDLOCK(&sla_trunks);
+	station = sla_find_station(station_name);
+	if (station) {
+		ao2_lock(station);
 		AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
-			if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
+			if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
+				res = sla_state_to_devstate(trunk_ref->state);
 				break;
-		}
-		if (!trunk_ref) {
-			AST_RWLIST_UNLOCK(&sla_trunks);
-			break;
-		}
-		res = sla_state_to_devstate(trunk_ref->state);
-		AST_RWLIST_UNLOCK(&sla_trunks);
-	}
-	AST_RWLIST_UNLOCK(&sla_stations);
+			}
+		}
+		ao2_unlock(station);
+	}
 
 	if (res == AST_DEVICE_INVALID) {
 		ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
@@ -6764,26 +6842,39 @@
 	return res;
 }
 
-static void destroy_trunk(struct sla_trunk *trunk)
-{
+static int sla_trunk_release_refs(void *obj, void *arg, int flags)
+{
+	struct sla_trunk *trunk = obj;
 	struct sla_station_ref *station_ref;
 
-	if (!ast_strlen_zero(trunk->autocontext))
-		ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
-
-	while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
-		ast_free(station_ref);
-
-	ast_string_field_free_memory(trunk);
-	ast_free(trunk);
-}
-
-static void destroy_station(struct sla_station *station)
-{
+	while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
+		ao2_ref(station_ref, -1);
+	}
+
+	return 0;
+}
+
+static int sla_station_release_refs(void *obj, void *arg, int flags)
+{
+	struct sla_station *station = obj;
 	struct sla_trunk_ref *trunk_ref;
 
+	while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
+		ao2_ref(trunk_ref, -1);
+	}
+
+	return 0;
+}
+
+static void sla_station_destructor(void *obj)
+{
+	struct sla_station *station = obj;
+
+	ast_debug(1, "sla_station destructor for '%s'\n", station->name);
+
 	if (!ast_strlen_zero(station->autocontext)) {
-		AST_RWLIST_RDLOCK(&sla_trunks);
+		struct sla_trunk_ref *trunk_ref;
+
 		AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
 			char exten[AST_MAX_EXTENSION];
 			char hint[AST_MAX_APP];
@@ -6794,31 +6885,43 @@
 			ast_context_remove_extension(station->autocontext, hint, 
 				PRIORITY_HINT, sla_registrar);
 		}
-		AST_RWLIST_UNLOCK(&sla_trunks);
-	}
-
-	while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
-		ast_free(trunk_ref);
+	}
+
+	sla_station_release_refs(station, NULL, 0);
 
 	ast_string_field_free_memory(station);
-	ast_free(station);
+}
+
+static int sla_trunk_hash(const void *obj, const int flags)
+{
+	const struct sla_trunk *trunk = obj;
+
+	return ast_str_case_hash(trunk->name);
+}
+
+static int sla_trunk_cmp(void *obj, void *arg, int flags)
+{
+	struct sla_trunk *trunk = obj, *trunk2 = arg;
+
+	return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int sla_station_hash(const void *obj, const int flags)
+{
+	const struct sla_station *station = obj;
+
+	return ast_str_case_hash(station->name);
+}
+
+static int sla_station_cmp(void *obj, void *arg, int flags)
+{
+	struct sla_station *station = obj, *station2 = arg;
+
+	return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 static void sla_destroy(void)
 {
-	struct sla_trunk *trunk;
-	struct sla_station *station;
-
-	AST_RWLIST_WRLOCK(&sla_trunks);
-	while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
-		destroy_trunk(trunk);
-	AST_RWLIST_UNLOCK(&sla_trunks);
-
-	AST_RWLIST_WRLOCK(&sla_stations);
-	while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
-		destroy_station(station);
-	AST_RWLIST_UNLOCK(&sla_stations);
-
 	if (sla.thread != AST_PTHREADT_NULL) {
 		ast_mutex_lock(&sla.lock);
 		sla.stop = 1;
@@ -6832,6 +6935,15 @@
 
 	ast_mutex_destroy(&sla.lock);
 	ast_cond_destroy(&sla.cond);
+
+	ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
+	ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
+
+	ao2_ref(sla_trunks, -1);
+	sla_trunks = NULL;
+
+	ao2_ref(sla_stations, -1);
+	sla_stations = NULL;
 }
 
 static int sla_check_device(const char *device)
@@ -6847,11 +6959,27 @@
 	return 0;
 }
 
+static void sla_trunk_destructor(void *obj)
+{
+	struct sla_trunk *trunk = obj;
+
+	ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
+
+	if (!ast_strlen_zero(trunk->autocontext)) {
+		ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
+	}
+
+	sla_trunk_release_refs(trunk, NULL, 0);
+
+	ast_string_field_free_memory(trunk);
+}
+
 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
 {
-	struct sla_trunk *trunk;
+	RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
 	struct ast_variable *var;
 	const char *dev;
+	int existing_trunk = 0;
 
 	if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
 		ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
@@ -6859,16 +6987,25 @@
 	}
 
 	if (sla_check_device(dev)) {
-		ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
+		ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
 			cat, dev);
 		return -1;
 	}
 
-	if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
+	if ((trunk = sla_find_trunk(cat))) {
+		trunk->mark = 0;
+		existing_trunk = 1;
+	} else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
+		if (ast_string_field_init(trunk, 32)) {
+			return -1;
+		}
+		ast_string_field_set(trunk, name, cat);
+	} else {
 		return -1;
 	}
 
-	ast_string_field_set(trunk, name, cat);
+	ao2_lock(trunk);
+
 	ast_string_field_set(trunk, device, dev);
 
 	for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
@@ -6897,54 +7034,65 @@
 		}
 	}
 
+	ao2_unlock(trunk);
+
 	if (!ast_strlen_zero(trunk->autocontext)) {
 		struct ast_context *context;
 		context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
 		if (!context) {
 			ast_log(LOG_ERROR, "Failed to automatically find or create "
 				"context '%s' for SLA!\n", trunk->autocontext);
-			destroy_trunk(trunk);
 			return -1;
 		}
 		if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
 			NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
 			ast_log(LOG_ERROR, "Failed to automatically create extension "
 				"for trunk '%s'!\n", trunk->name);
-			destroy_trunk(trunk);
 			return -1;
 		}
 	}
 
-	AST_RWLIST_WRLOCK(&sla_trunks);
-	AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
-	AST_RWLIST_UNLOCK(&sla_trunks);
+	if (!existing_trunk) {
+		ao2_link(sla_trunks, trunk);
+	}
 
 	return 0;
 }
 
+/*!
+ * \internal
+ * \pre station is not locked
+ */
 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
 {
-	struct sla_trunk *trunk;
-	struct sla_trunk_ref *trunk_ref;
+	RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
+	struct sla_trunk_ref *trunk_ref = NULL;
 	struct sla_station_ref *station_ref;
 	char *trunk_name, *options, *cur;
+	int existing_trunk_ref = 0;
+	int existing_station_ref = 0;
 
 	options = ast_strdupa(var->value);
 	trunk_name = strsep(&options, ",");
-	
-	AST_RWLIST_RDLOCK(&sla_trunks);
-	AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
-		if (!strcasecmp(trunk->name, trunk_name))
-			break;
-	}
-
-	AST_RWLIST_UNLOCK(&sla_trunks);
+
+	trunk = sla_find_trunk(trunk_name);
 	if (!trunk) {
 		ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
 		return;
 	}
-	if (!(trunk_ref = create_trunk_ref(trunk)))
+
+	AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+		if (trunk_ref->trunk == trunk) {
+			trunk_ref->mark = 0;
+			existing_trunk_ref = 1;
+			break;
+		}
+	}
+
+	if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
 		return;
+	}
+
 	trunk_ref->state = SLA_TRUNK_STATE_IDLE;
 
 	while ((cur = strsep(&options, ","))) {
@@ -6968,41 +7116,73 @@
 		}
 	}
 
-	if (!(station_ref = sla_create_station_ref(station))) {
-		ast_free(trunk_ref);
+	AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+		if (station_ref->station == station) {
+			station_ref->mark = 0;
+			existing_station_ref = 1;
+			break;
+		}
+	}
+
+	if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
+		if (!existing_trunk_ref) {
+			ao2_ref(trunk_ref, -1);
+		} else {
+			trunk_ref->mark = 1;
+		}
 		return;
 	}
-	ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
-	AST_RWLIST_WRLOCK(&sla_trunks);
-	AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
-	AST_RWLIST_UNLOCK(&sla_trunks);
-	AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+
+	if (!existing_station_ref) {
+		ao2_lock(trunk);
+		AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
+		ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
+		ao2_unlock(trunk);
+	}
+
+	if (!existing_trunk_ref) {
+		ao2_lock(station);
+		AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+		ao2_unlock(station);
+	}
 }
 
 static int sla_build_station(struct ast_config *cfg, const char *cat)
 {
-	struct sla_station *station;
+	RAII_VAR(struct sla_station *, station, NULL, unref_obj);
 	struct ast_variable *var;
 	const char *dev;
+	int existing_station = 0;
 
 	if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
 		ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
 		return -1;
 	}
 
-	if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
+	if ((station = sla_find_station(cat))) {
+		station->mark = 0;
+		existing_station = 1;
+	} else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
+		if (ast_string_field_init(station, 32)) {
+			return -1;
+		}
+		ast_string_field_set(station, name, cat);
+	} else {
 		return -1;
 	}
 
-	ast_string_field_set(station, name, cat);
+	ao2_lock(station);
+
 	ast_string_field_set(station, device, dev);
 
 	for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
-		if (!strcasecmp(var->name, "trunk"))
+		if (!strcasecmp(var->name, "trunk")) {
+			ao2_unlock(station);
 			sla_add_trunk_to_station(station, var);
-		else if (!strcasecmp(var->name, "autocontext"))
+			ao2_lock(station);
+		} else if (!strcasecmp(var->name, "autocontext")) {
 			ast_string_field_set(station, autocontext, var->value);
-		else if (!strcasecmp(var->name, "ringtimeout")) {
+		} else if (!strcasecmp(var->name, "ringtimeout")) {
 			if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
 				ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
 					var->value, station->name);
@@ -7030,6 +7210,8 @@
 		}
 	}
 
+	ao2_unlock(station);
+
 	if (!ast_strlen_zero(station->autocontext)) {
 		struct ast_context *context;
 		struct sla_trunk_ref *trunk_ref;
@@ -7037,7 +7219,6 @@
 		if (!context) {
 			ast_log(LOG_ERROR, "Failed to automatically find or create "
 				"context '%s' for SLA!\n", station->autocontext);
-			destroy_station(station);
 			return -1;
 		}

[... 305 lines stripped ...]



More information about the asterisk-commits mailing list