astobj2-ify the SLA code
authorRussell Bryant <russell@russellbryant.com>
Wed, 10 Jul 2013 01:56:15 +0000 (01:56 +0000)
committerRussell Bryant <russell@russellbryant.com>
Wed, 10 Jul 2013 01:56:15 +0000 (01:56 +0000)
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/
........

Merged revisions 393928 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 393929 from http://svn.asterisk.org/svn/asterisk/branches/11

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393930 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_meetme.c
configs/sla.conf.sample

index b79ab63..7071960 100644 (file)
@@ -961,17 +961,26 @@ struct sla_station {
        /*! 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);
@@ -995,10 +1004,16 @@ struct sla_trunk {
        /*! 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;
@@ -1012,10 +1027,12 @@ struct sla_trunk_ref {
         *  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";
 
@@ -1027,10 +1044,6 @@ enum sla_event_type {
        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 {
@@ -1086,8 +1099,6 @@ static struct {
        /*! 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,
 };
@@ -2117,7 +2128,8 @@ static const char *sla_hold_str(unsigned int hold_access)
 
 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:
@@ -2135,12 +2147,17 @@ static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
                    "=== 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"
@@ -2154,13 +2171,16 @@ static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
                            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");
+
+               ao2_unlock(trunk);
        }
-       AST_RWLIST_UNLOCK(&sla_trunks);
+       ao2_iterator_destroy(&i);
        ast_cli(a->fd, "=============================================================\n\n");
 
        return CLI_SUCCESS;
@@ -2182,7 +2202,8 @@ static const char *trunkstate2str(enum sla_trunk_state state)
 
 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:
@@ -2200,11 +2221,14 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
                    "=== 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);
@@ -2225,7 +2249,6 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
                            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),
@@ -2245,11 +2268,12 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
                                    trunkstate2str(trunk_ref->state),
                                    ring_timeout, ring_delay);
                }
-               AST_RWLIST_UNLOCK(&sla_trunks);
                ast_cli(a->fd, "=== ---------------------------------------------------------\n"
                            "===\n");
+
+               ao2_unlock(station);
        }
-       AST_RWLIST_UNLOCK(&sla_stations);
+       ao2_iterator_destroy(&i);
        ast_cli(a->fd, "============================================================\n"
                    "\n");
 
@@ -2393,11 +2417,16 @@ static void sla_queue_event_full(enum sla_event_type type,
        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;
@@ -2431,6 +2460,7 @@ static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *c
        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, "_");
@@ -2439,16 +2469,23 @@ static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *c
                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;
+               }
+               ao2_ref(station, -1);
        }
-       AST_RWLIST_UNLOCK(&sla_stations);
+       ao2_iterator_destroy(&i);
 
        if (!trunk_ref) {
                ast_debug(1, "Trunk not found for event!\n");
@@ -5776,34 +5813,30 @@ static void load_config_meetme(void)
        ast_config_destroy(cfg);
 }
 
-/*! \brief Find an SLA trunk by name
- * \note This must be called with the sla_trunks container locked
+/*!
+ * \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;
-       }
+       struct sla_trunk tmp_trunk = {
+               .name = name,
+       };
 
-       return trunk;
+       return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
 }
 
-/*! \brief Find an SLA station by name
- * \note This must be called with the sla_stations container locked
+/*!
+ * \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;
-       }
+       struct sla_station tmp_station = {
+               .name = name,
+       };
 
-       return station;
+       return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
 }
 
 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
@@ -5827,9 +5860,11 @@ static int sla_check_station_hold_access(const struct sla_trunk *trunk,
        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.
@@ -5856,16 +5891,32 @@ static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station
                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;
@@ -5878,12 +5929,48 @@ static struct sla_ringing_station *sla_create_ringing_station(struct sla_station
        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)
 {
        switch (state) {
@@ -5906,18 +5993,25 @@ static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk
 {
        struct sla_station *station;
        struct sla_trunk_ref *trunk_ref;
+       struct ao2_iterator i;
 
-       AST_LIST_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->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 {
@@ -5935,8 +6029,8 @@ static void answer_trunk_chan(struct ast_channel *chan)
 
 static void *run_station(void *data)
 {
-       struct sla_station *station;
-       struct sla_trunk_ref *trunk_ref;
+       RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
+       RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
        struct ast_str *conf_name = ast_str_create(16);
        struct ast_flags64 conf_flags = { 0 };
        struct ast_conference *conf;
@@ -5979,6 +6073,8 @@ static void *run_station(void *data)
        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];
@@ -5988,10 +6084,11 @@ static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
        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);
+       while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
+               ao2_ref(station_ref, -1);
+       }
 
-       ast_free(ringing_trunk);
+       sla_ringing_trunk_destroy(ringing_trunk);
 }
 
 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
@@ -6026,7 +6123,7 @@ static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station
        }
 
 done:
-       ast_free(ringing_station);
+       sla_ringing_station_destroy(ringing_station);
 }
 
 static void sla_dial_state_callback(struct ast_dial *dial)
@@ -6078,8 +6175,10 @@ static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *st
                        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;
                }
@@ -6097,7 +6196,7 @@ static void sla_handle_dial_state_event(void)
        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, ao2_cleanup);
                struct sla_ringing_trunk *ringing_trunk = NULL;
                struct run_station_args args;
                enum ast_dial_result dial_res;
@@ -6130,7 +6229,7 @@ static void sla_handle_dial_state_event(void)
                                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 */
@@ -6141,12 +6240,14 @@ static void sla_handle_dial_state_event(void)
                        /* 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);
@@ -6200,7 +6301,7 @@ static int sla_check_failed_station(const struct sla_station *station)
                        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;
@@ -6253,11 +6354,9 @@ static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_
        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))) {
@@ -6297,6 +6396,8 @@ static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *statio
                        break;
        }
 
+       ao2_ref(trunk_ref, 1);
+
        return trunk_ref;
 }
 
@@ -6308,7 +6409,7 @@ static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *statio
 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, ao2_cleanup);
        unsigned int delay = UINT_MAX;
        int time_left, time_elapsed;
 
@@ -6401,7 +6502,7 @@ static void sla_hangup_stations(void)
                        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
@@ -6558,8 +6659,10 @@ static int sla_calc_station_delays(unsigned int *timeout)
 {
        struct sla_station *station;
        int res = 0;
+       struct ao2_iterator i;
 
-       AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+       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;
 
@@ -6589,6 +6692,7 @@ static int sla_calc_station_delays(unsigned int *timeout)
                if (time_left < *timeout)
                        *timeout = time_left;
        }
+       ao2_iterator_destroy(&i);
 
        return res;
 }
@@ -6630,47 +6734,19 @@ static int sla_process_timers(struct timespec *ts)
        return 1;
 }
 
-static int sla_load_config(int reload);
-
-/*!
- * \internal
- * \brief Check if we can do a reload of SLA, and do it if we can
- * \pre sla.lock is locked.
- */
-static void sla_check_reload(void)
+static void sla_event_destroy(struct sla_event *event)
 {
-       struct sla_station *station;
-       struct sla_trunk *trunk;
-
-       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)) {
-               return;
+       if (event->trunk_ref) {
+               ao2_ref(event->trunk_ref, -1);
+               event->trunk_ref = NULL;
        }
 
-       AST_RWLIST_RDLOCK(&sla_stations);
-       AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
-               if (station->ref_count)
-                       break;
-       }
-       AST_RWLIST_UNLOCK(&sla_stations);
-       if (station) {
-               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) {
-               return;
+       if (event->station) {
+               ao2_ref(event->station, -1);
+               event->station = NULL;
        }
 
-       /* yay */
-       sla_load_config(1);
-       sla.reload = 0;
+       ast_free(event);
 }
 
 static void *sla_thread(void *data)
@@ -6709,27 +6785,21 @@ static void *sla_thread(void *data)
                        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 ((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)))
-               ast_free(failed_station);
+       while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
+               sla_failed_station_destroy(failed_station);
+       }
 
        return NULL;
 }
@@ -6750,7 +6820,8 @@ static void *dial_trunk(void *data)
        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, ao2_cleanup);
+       RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
        int caller_is_saved;
        struct ast_party_caller caller;
        int last_state = 0;
@@ -6822,8 +6893,8 @@ static void *dial_trunk(void *data)
                        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;
                }
@@ -6876,15 +6947,19 @@ static void *dial_trunk(void *data)
        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;
@@ -6893,8 +6968,8 @@ static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *sta
 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, ao2_cleanup);
+       RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
        char conf_name[MAX_CONFNUM];
        struct ast_flags64 conf_flags = { 0 };
        struct ast_conference *conf;
@@ -6914,25 +6989,21 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                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))
@@ -6942,8 +7013,6 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                                "'%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;
        }
 
@@ -6972,7 +7041,7 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        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);
@@ -6992,6 +7061,8 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        .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
@@ -7011,8 +7082,6 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
                        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;
                }
        }
@@ -7045,19 +7114,28 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
        
        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;
@@ -7067,9 +7145,11 @@ static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
 {
        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();
 
@@ -7084,6 +7164,16 @@ static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *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 {
        SLA_TRUNK_OPT_MOH = (1 << 0),
 };
@@ -7102,7 +7192,7 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
        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, ao2_cleanup);
        struct sla_ringing_trunk *ringing_trunk;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(trunk_name);
@@ -7126,16 +7216,11 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
                }
        }
 
-       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;
        }
 
@@ -7143,8 +7228,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
                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;
        }
 
@@ -7152,8 +7235,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
 
        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;
        }
 
@@ -7161,8 +7242,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
        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, 
@@ -7196,46 +7275,37 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
        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, ao2_cleanup);
        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);
+               ao2_unlock(station);
        }
-       AST_RWLIST_UNLOCK(&sla_stations);
 
        if (res == AST_DEVICE_INVALID) {
                ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
@@ -7245,26 +7315,39 @@ static enum ast_device_state sla_state(const char *data)
        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);
+       while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
+               ao2_ref(station_ref, -1);
+       }
 
-       ast_string_field_free_memory(trunk);
-       ast_free(trunk);
+       return 0;
 }
 
-static void destroy_station(struct sla_station *station)
+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];
@@ -7275,31 +7358,43 @@ static void destroy_station(struct sla_station *station)
                        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 void sla_destroy(void)
+static int sla_trunk_hash(const void *obj, const int flags)
 {
-       struct sla_trunk *trunk;
-       struct sla_station *station;
+       const struct sla_trunk *trunk = obj;
+
+       return ast_str_case_hash(trunk->name);
+}
 
-       AST_RWLIST_WRLOCK(&sla_trunks);
-       while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
-               destroy_trunk(trunk);
-       AST_RWLIST_UNLOCK(&sla_trunks);
+static int sla_trunk_cmp(void *obj, void *arg, int flags)
+{
+       struct sla_trunk *trunk = obj, *trunk2 = arg;
 
-       AST_RWLIST_WRLOCK(&sla_stations);
-       while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
-               destroy_station(station);
-       AST_RWLIST_UNLOCK(&sla_stations);
+       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)
+{
        if (sla.thread != AST_PTHREADT_NULL) {
                ast_mutex_lock(&sla.lock);
                sla.stop = 1;
@@ -7313,6 +7408,15 @@ static void sla_destroy(void)
 
        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)
@@ -7328,11 +7432,27 @@ static int sla_check_device(const char *device)
        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, ao2_cleanup);
        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);
@@ -7340,16 +7460,25 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
        }
 
        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) {
@@ -7378,54 +7507,65 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
                }
        }
 
+       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, ao2_cleanup);
+       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, ","))) {
@@ -7449,41 +7589,73 @@ static void sla_add_trunk_to_station(struct sla_station *station, struct ast_var
                }
        }
 
-       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, ao2_cleanup);
        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);
@@ -7511,6 +7683,8 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                }
        }
 
+       ao2_unlock(station);
+
        if (!ast_strlen_zero(station->autocontext)) {
                struct ast_context *context;
                struct sla_trunk_ref *trunk_ref;
@@ -7518,7 +7692,6 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                if (!context) {
                        ast_log(LOG_ERROR, "Failed to automatically find or create "
                                "context '%s' for SLA!\n", station->autocontext);
-                       destroy_station(station);
                        return -1;
                }
                /* The extension for when the handset goes off-hook.
@@ -7527,10 +7700,8 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                        NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically create extension "
                                "for trunk '%s'!\n", station->name);
-                       destroy_station(station);
                        return -1;
                }
-               AST_RWLIST_RDLOCK(&sla_trunks);
                AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
                        char exten[AST_MAX_EXTENSION];
                        char hint[AST_MAX_APP];
@@ -7542,7 +7713,6 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                                NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
                                ast_log(LOG_ERROR, "Failed to automatically create extension "
                                        "for trunk '%s'!\n", station->name);
-                               destroy_station(station);
                                return -1;
                        }
                        /* Hint for this line button 
@@ -7551,20 +7721,115 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                                NULL, NULL, hint, NULL, NULL, sla_registrar)) {
                                ast_log(LOG_ERROR, "Failed to automatically create hint "
                                        "for trunk '%s'!\n", station->name);
-                               destroy_station(station);
                                return -1;
                        }
                }
-               AST_RWLIST_UNLOCK(&sla_trunks);
        }
 
-       AST_RWLIST_WRLOCK(&sla_stations);
-       AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
-       AST_RWLIST_UNLOCK(&sla_stations);
+       if (!existing_station) {
+               ao2_link(sla_stations, station);
+       }
+
+       return 0;
+}
+
+static int sla_trunk_mark(void *obj, void *arg, int flags)
+{
+       struct sla_trunk *trunk = obj;
+       struct sla_station_ref *station_ref;
+
+       ao2_lock(trunk);
+
+       trunk->mark = 1;
+
+       AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+               station_ref->mark = 1;
+       }
+
+       ao2_unlock(trunk);
+
+       return 0;
+}
+
+static int sla_station_mark(void *obj, void *arg, int flags)
+{
+       struct sla_station *station = obj;
+       struct sla_trunk_ref *trunk_ref;
+
+       ao2_lock(station);
+
+       station->mark = 1;
+
+       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+               trunk_ref->mark = 1;
+       }
+
+       ao2_unlock(station);
 
        return 0;
 }
 
+static int sla_trunk_is_marked(void *obj, void *arg, int flags)
+{
+       struct sla_trunk *trunk = obj;
+
+       ao2_lock(trunk);
+
+       if (trunk->mark) {
+               /* Only remove all of the station references if the trunk itself is going away */
+               sla_trunk_release_refs(trunk, NULL, 0);
+       } else {
+               struct sla_station_ref *station_ref;
+
+               /* Otherwise only remove references to stations no longer in the config */
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
+                       if (!station_ref->mark) {
+                               continue;
+                       }
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       ao2_ref(station_ref, -1);
+               }
+               AST_LIST_TRAVERSE_SAFE_END
+       }
+
+       ao2_unlock(trunk);
+
+       return trunk->mark ? CMP_MATCH : 0;
+}
+
+static int sla_station_is_marked(void *obj, void *arg, int flags)
+{
+       struct sla_station *station = obj;
+
+       ao2_lock(station);
+
+       if (station->mark) {
+               /* Only remove all of the trunk references if the station itself is going away */
+               sla_station_release_refs(station, NULL, 0);
+       } else {
+               struct sla_trunk_ref *trunk_ref;
+
+               /* Otherwise only remove references to trunks no longer in the config */
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
+                       if (!trunk_ref->mark) {
+                               continue;
+                       }
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       ao2_ref(trunk_ref, -1);
+               }
+               AST_LIST_TRAVERSE_SAFE_END
+       }
+
+       ao2_unlock(station);
+
+       return station->mark ? CMP_MATCH : 0;
+}
+
+static int sla_in_use(void)
+{
+       return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
+}
+
 static int sla_load_config(int reload)
 {
        struct ast_config *cfg;
@@ -7576,6 +7841,8 @@ static int sla_load_config(int reload)
        if (!reload) {
                ast_mutex_init(&sla.lock);
                ast_cond_init(&sla.cond, NULL);
+               sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
+               sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
        }
 
        if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
@@ -7588,21 +7855,8 @@ static int sla_load_config(int reload)
        }
 
        if (reload) {
-               struct sla_station *station;
-               struct sla_trunk *trunk;
-
-               /* We need to actually delete the previous versions of trunks and stations now */
-               AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
-                       AST_RWLIST_REMOVE_CURRENT(entry);
-                       ast_free(station);
-               }
-               AST_RWLIST_TRAVERSE_SAFE_END;
-
-               AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
-                       AST_RWLIST_REMOVE_CURRENT(entry);
-                       ast_free(trunk);
-               }
-               AST_RWLIST_TRAVERSE_SAFE_END;
+               ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
+               ao2_callback(sla_stations, 0, sla_station_mark, NULL);
        }
 
        if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
@@ -7629,9 +7883,13 @@ static int sla_load_config(int reload)
 
        ast_config_destroy(cfg);
 
-       /* Even if we don't have any stations, we may after a reload and we need to
-        * be able to process the SLA_EVENT_RELOAD event in that case */
-       if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
+       if (reload) {
+               ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
+               ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
+       }
+
+       /* Start SLA event processing thread once SLA has been configured. */
+       if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
                ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
        }
 
@@ -7716,15 +7974,7 @@ static struct ast_custom_function meetme_info_acf = {
 static int load_config(int reload)
 {
        load_config_meetme();
-
-       if (reload && sla.thread != AST_PTHREADT_NULL) {
-               sla_queue_event(SLA_EVENT_RELOAD);
-               ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
-                       "and will be completed when the system is idle.\n");
-               return 0;
-       }
-       
-       return sla_load_config(0);
+       return sla_load_config(reload);
 }
 
 #define MEETME_DATA_EXPORT(MEMBER)                                     \
index 5027009..b5e1ef6 100644 (file)
 ; -------------------------------------
 
 
+; ********************************
+; **** Configuration Ordering ****
+; ********************************
+
+; Note that SLA configuration processing assumes that *all* trunk declarations are
+; listed in the configuration file before any stations.
+
+; ********************************
+; ********************************
+
+
 ; ---- Trunk Declarations -------------
 ;
 ;[line1]                    ; Provide a name for this trunk.