<p>Joshua Colp has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/10478">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">stasis: Use an implementation specific channel snapshot cache.<br><br>Channels no longer use the Stasis cache for channel snapshots. Instead<br>they are stored in a hash table in stasis_channels which reduces the<br>number of Stasis messages created and allows better storage.<br><br>As a result the following APIs are no longer available since the stasis<br>cache is no longer used:<br>ast_channel_topic_cached()<br>ast_channel_topic_all_cached()<br><br>The ast_channel_cache_all() and ast_channel_cache_by_name() functions<br>now return an ao2_container of ast_channel_snapshots rather than<br>a container of stasis_messages therefore you can't (and don't need<br>to) call stasis_cache functions on it.<br><br>The ast_channel_topic_all() function now returns a normal topic not<br>a cached one so you can't use stasis cache functions on it either.<br><br>The ast_channel_snapshot_type() stasis message now has the<br>ast_channel_snapshot_update structure as it's data. It contains the<br>last snapshot and the new one.<br><br>ast_channel_snapshot_get_latest() still returns the latest snapshot.<br><br>The latest snapshot is now stored on the channel itself to eliminate<br>cache hits when Stasis messages that have the snapshot as a payload<br>are created.<br><br>ASTERISK-28102<br><br>Change-Id: I9334febff60a82d7c39703e49059fa3a68825786<br>---<br>M CHANGES<br>M UPGRADE.txt<br>M apps/app_agent_pool.c<br>M apps/confbridge/confbridge_manager.c<br>M channels/chan_pjsip.c<br>M channels/pjsip/cli_commands.c<br>M include/asterisk/channel.h<br>M include/asterisk/stasis_channels.h<br>M main/aoc.c<br>M main/app.c<br>M main/bridge.c<br>M main/cdr.c<br>M main/cel.c<br>M main/channel.c<br>M main/channel_internal_api.c<br>M main/cli.c<br>M main/endpoints.c<br>M main/manager.c<br>M main/manager_bridges.c<br>M main/manager_channels.c<br>M main/stasis_channels.c<br>M res/ari/resource_channels.c<br>M res/res_agi.c<br>M res/res_chan_stats.c<br>M res/stasis/app.c<br>M res/stasis/control.c<br>M tests/test_cel.c<br>27 files changed, 457 insertions(+), 517 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/78/10478/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/CHANGES b/CHANGES</span><br><span>index bd1a7a7..7f15590 100644</span><br><span>--- a/CHANGES</span><br><span>+++ b/CHANGES</span><br><span>@@ -9,6 +9,26 @@</span><br><span> ==============================================================================</span><br><span> </span><br><span> ------------------------------------------------------------------------------</span><br><span style="color: hsl(120, 100%, 40%);">+--- Functionality changes from Asterisk 16 to Asterisk 17 --------------------</span><br><span style="color: hsl(120, 100%, 40%);">+------------------------------------------------------------------------------</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Channels</span><br><span style="color: hsl(120, 100%, 40%);">+------------------</span><br><span style="color: hsl(120, 100%, 40%);">+ * The core no longer uses the stasis cache for channels snapshots.</span><br><span style="color: hsl(120, 100%, 40%);">+   The following APIs are no longer available:</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_topic_cached()</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_topic_all_cached()</span><br><span style="color: hsl(120, 100%, 40%);">+   The ast_channel_cache_all() and ast_channel_cache_by_name() functions</span><br><span style="color: hsl(120, 100%, 40%);">+   now returns an ao2_container of ast_channel_snapshots rather than a</span><br><span style="color: hsl(120, 100%, 40%);">+   container of stasis_messages therefore you can't call stasis_cache</span><br><span style="color: hsl(120, 100%, 40%);">+   functions on it.</span><br><span style="color: hsl(120, 100%, 40%);">+   The ast_channel_topic_all() function now returns a normal topic,</span><br><span style="color: hsl(120, 100%, 40%);">+   not a cached one so you can't use stasis cache functions on it either.</span><br><span style="color: hsl(120, 100%, 40%);">+   The ast_channel_snapshot_type() stasis message now has the</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_snapshot_update structure as it's data.</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_snapshot_get_latest() still returns the latest snapshot.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+------------------------------------------------------------------------------</span><br><span> --- Functionality changes from Asterisk 16.0.0 to Asterisk 16.1.0 ------------</span><br><span> ------------------------------------------------------------------------------</span><br><span> </span><br><span>diff --git a/UPGRADE.txt b/UPGRADE.txt</span><br><span>index b7bbf3b..f9e1925 100644</span><br><span>--- a/UPGRADE.txt</span><br><span>+++ b/UPGRADE.txt</span><br><span>@@ -25,3 +25,20 @@</span><br><span> === UPGRADE-16.txt  -- Upgrade info for 15 to 16</span><br><span> ===========================================================</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+New in 17.0.0:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Channels</span><br><span style="color: hsl(120, 100%, 40%);">+------------------</span><br><span style="color: hsl(120, 100%, 40%);">+ * The core no longer uses the stasis cache for channels snapshots.</span><br><span style="color: hsl(120, 100%, 40%);">+   The following APIs are no longer available:</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_topic_cached()</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_topic_all_cached()</span><br><span style="color: hsl(120, 100%, 40%);">+   The ast_channel_cache_all() and ast_channel_cache_by_name() functions</span><br><span style="color: hsl(120, 100%, 40%);">+   now returns an ao2_container of ast_channel_snapshots rather than a</span><br><span style="color: hsl(120, 100%, 40%);">+   container of stasis_messages therefore you can't call stasis_cache</span><br><span style="color: hsl(120, 100%, 40%);">+   functions on it.</span><br><span style="color: hsl(120, 100%, 40%);">+   The ast_channel_topic_all() function now returns a normal topic,</span><br><span style="color: hsl(120, 100%, 40%);">+   not a cached one so you can't use stasis cache functions on it either.</span><br><span style="color: hsl(120, 100%, 40%);">+   The ast_channel_snapshot_type() stasis message now has the</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_snapshot_update structure as it's data.</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_snapshot_get_latest() still returns the latest snapshot.</span><br><span>diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c</span><br><span>index 805c403..5bd6a4d 100644</span><br><span>--- a/apps/app_agent_pool.c</span><br><span>+++ b/apps/app_agent_pool.c</span><br><span>@@ -1448,7 +1448,7 @@</span><br><span>          return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_channel_publish_cached_blob(chan, ast_channel_agent_login_type(), blob);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_channel_publish_blob(chan, ast_channel_agent_login_type(), blob);</span><br><span> }</span><br><span> </span><br><span> static void send_agent_logoff(struct ast_channel *chan, const char *agent, long logintime)</span><br><span>@@ -1464,7 +1464,7 @@</span><br><span>                 return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_channel_publish_cached_blob(chan, ast_channel_agent_logoff_type(), blob);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_publish_blob(chan, ast_channel_agent_logoff_type(), blob);</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span>diff --git a/apps/confbridge/confbridge_manager.c b/apps/confbridge/confbridge_manager.c</span><br><span>index a7f2fce..a32f79f 100644</span><br><span>--- a/apps/confbridge/confbridge_manager.c</span><br><span>+++ b/apps/confbridge/confbridge_manager.c</span><br><span>@@ -836,7 +836,7 @@</span><br><span>    }</span><br><span> </span><br><span>        channel_state_router = stasis_message_router_create(</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_channel_topic_all_cached());</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_channel_topic_all());</span><br><span> </span><br><span>        if (!channel_state_router) {</span><br><span>                 manager_confbridge_shutdown();</span><br><span>diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c</span><br><span>index 21941eb..ca48b56 100644</span><br><span>--- a/channels/chan_pjsip.c</span><br><span>+++ b/channels/chan_pjsip.c</span><br><span>@@ -1135,7 +1135,6 @@</span><br><span>      RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", data), ao2_cleanup);</span><br><span>   enum ast_device_state state = AST_DEVICE_UNKNOWN;</span><br><span>    RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);</span><br><span>   struct ast_devstate_aggregate aggregate;</span><br><span>     int num, inuse = 0;</span><br><span> </span><br><span>@@ -1156,27 +1155,20 @@</span><br><span>            state = AST_DEVICE_NOT_INUSE;</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!endpoint_snapshot->num_channels || !(cache = ast_channel_cache())) {</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!endpoint_snapshot->num_channels) {</span><br><span>           return state;</span><br><span>        }</span><br><span> </span><br><span>        ast_devstate_aggregate_init(&aggregate);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        ao2_ref(cache, +1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>  for (num = 0; num < endpoint_snapshot->num_channels; num++) {</span><br><span style="color: hsl(0, 100%, 40%);">-             RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);</span><br><span>           struct ast_channel_snapshot *snapshot;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-              msg = stasis_cache_get(cache, ast_channel_snapshot_type(),</span><br><span style="color: hsl(0, 100%, 40%);">-                      endpoint_snapshot->channel_ids[num]);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           snapshot = ast_channel_snapshot_get_latest(endpoint_snapshot->channel_ids[num]);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!snapshot) {</span><br><span>                     continue;</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           snapshot = stasis_message_data(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>                 if (chan_pjsip_get_hold(snapshot->uniqueid)) {</span><br><span>                    ast_devstate_aggregate_add(&aggregate, AST_DEVICE_ONHOLD);</span><br><span>               } else {</span><br><span>@@ -1187,6 +1179,8 @@</span><br><span>                     (snapshot->state == AST_STATE_BUSY)) {</span><br><span>                    inuse++;</span><br><span>             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_ref(snapshot, -1);</span><br><span>       }</span><br><span> </span><br><span>        if (endpoint->devicestate_busy_at && (inuse == endpoint->devicestate_busy_at)) {</span><br><span>diff --git a/channels/pjsip/cli_commands.c b/channels/pjsip/cli_commands.c</span><br><span>index 33d0e02..9a8dc29 100644</span><br><span>--- a/channels/pjsip/cli_commands.c</span><br><span>+++ b/channels/pjsip/cli_commands.c</span><br><span>@@ -169,9 +169,8 @@</span><br><span> </span><br><span> static int cli_message_to_snapshot(void *obj, void *arg, int flags)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   struct stasis_message *message = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel_snapshot *snapshot = obj;</span><br><span>         struct ao2_container *snapshots = arg;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct ast_channel_snapshot *snapshot = stasis_message_data(message);</span><br><span> </span><br><span>    if (!strcmp(snapshot->type, "PJSIP")) {</span><br><span>                 ao2_link(snapshots, snapshot);</span><br><span>@@ -198,8 +197,7 @@</span><br><span> {</span><br><span>    struct ao2_container *child_container;</span><br><span>       regex_t regexbuf;</span><br><span style="color: hsl(0, 100%, 40%);">-       RAII_VAR(struct ao2_container *, parent_container,</span><br><span style="color: hsl(0, 100%, 40%);">-              stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type()), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+    RAII_VAR(struct ao2_container *, parent_container, ast_channel_cache_by_name(), ao2_cleanup);</span><br><span> </span><br><span>    if (!parent_container) {</span><br><span>             return NULL;</span><br><span>diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h</span><br><span>index 3f22cdd..9627ae2 100644</span><br><span>--- a/include/asterisk/channel.h</span><br><span>+++ b/include/asterisk/channel.h</span><br><span>@@ -148,6 +148,15 @@</span><br><span> #define AST_MAX_PUBLIC_UNIQUEID 149</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * The number of buckets to store channels or channel information</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef LOW_MEMORY</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_NUM_CHANNEL_BUCKETS 61</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_NUM_CHANNEL_BUCKETS 1567</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * Maximum size of an internal Asterisk channel unique ID.</span><br><span>  *</span><br><span>  * \details</span><br><span>@@ -2650,6 +2659,17 @@</span><br><span> void ast_channel_internal_swap_topics(struct ast_channel *a, struct ast_channel *b);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Swap snapshots beteween two channels</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param a First channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param b Second channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return void</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is used in masquerade to exchange snapshots</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_channel_internal_swap_snapshots(struct ast_channel *a, struct ast_channel *b);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Set uniqueid and linkedid string value only (not time)</span><br><span>  * \param chan The channel to set the uniqueid to</span><br><span>  * \param uniqueid The uniqueid to set</span><br><span>@@ -4236,6 +4256,8 @@</span><br><span> void ast_channel_adsicpe_set(struct ast_channel *chan, enum ast_channel_adsicpe value);</span><br><span> enum ast_channel_state ast_channel_state(const struct ast_channel *chan);</span><br><span> ast_callid ast_channel_callid(const struct ast_channel *chan);</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_channel_snapshot *ast_channel_snapshot(const struct ast_channel *chan);</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_channel_snapshot_set(struct ast_channel *chan, struct ast_channel_snapshot *snapshot);</span><br><span> </span><br><span> /*!</span><br><span>  * \pre chan is locked</span><br><span>@@ -4562,21 +4584,6 @@</span><br><span> struct stasis_topic *ast_channel_topic(struct ast_channel *chan);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 12</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief A topic which publishes the events for a particular channel.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \ref ast_channel_snapshot messages are replaced with \ref stasis_cache_update</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * If the given \a chan is \c NULL, ast_channel_topic_all_cached() is returned.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param chan Channel, or \c NULL.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval Topic for channel's events.</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval ast_channel_topic_all() if \a chan is \c NULL.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_topic *ast_channel_topic_cached(struct ast_channel *chan);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span>  * \brief Get the bridge associated with a channel</span><br><span>  * \since 12.0.0</span><br><span>  *</span><br><span>diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h</span><br><span>index 4843617..2aeff6f 100644</span><br><span>--- a/include/asterisk/stasis_channels.h</span><br><span>+++ b/include/asterisk/stasis_channels.h</span><br><span>@@ -76,6 +76,23 @@</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Structure representing a change of snapshot of channel state.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * While not enforced programmatically, this object is shared across multiple</span><br><span style="color: hsl(120, 100%, 40%);">+ * threads, and should be treated as an immutable object.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This structure will not have a transition of an old snapshot with no</span><br><span style="color: hsl(120, 100%, 40%);">+ * new snapshot to indicate that a channel has gone away. A new snapshot will</span><br><span style="color: hsl(120, 100%, 40%);">+ * always exist and a channel going away can be determined by checking for the</span><br><span style="color: hsl(120, 100%, 40%);">+ * AST_FLAG_DEAD flag on the new snapshot.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_channel_snapshot_update {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_channel_snapshot *old_snapshot; /*!< The old channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_channel_snapshot *new_snapshot; /*!< The new channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \since 12</span><br><span>  * \brief Blob of data associated with a channel.</span><br><span>  *</span><br><span>@@ -94,7 +111,7 @@</span><br><span>  */</span><br><span> struct ast_multi_channel_blob;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_cp_all *ast_channel_cache_all(void);</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *ast_channel_cache_all(void);</span><br><span> </span><br><span> /*!</span><br><span>  * \since 12</span><br><span>@@ -105,34 +122,17 @@</span><br><span> </span><br><span> /*!</span><br><span>  * \since 12</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief A caching topic which caches \ref ast_channel_snapshot messages from</span><br><span style="color: hsl(0, 100%, 40%);">- * ast_channel_events_all(void).</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval Topic for all channel events.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_topic *ast_channel_topic_all_cached(void);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 12</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Primary channel cache, indexed by Uniqueid.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval Cache of \ref ast_channel_snapshot.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_cache *ast_channel_cache(void);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 12</span><br><span>  * \brief Secondary channel cache, indexed by name.</span><br><span>  *</span><br><span>  * \retval Cache of \ref ast_channel_snapshot.</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_cache *ast_channel_cache_by_name(void);</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *ast_channel_cache_by_name(void);</span><br><span> </span><br><span> /*!</span><br><span>  * \since 12</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Message type for \ref ast_channel_snapshot.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Message type for \ref ast_channel_snapshot_update.</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval Message type for \ref ast_channel_snapshot.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval Message type for \ref ast_channel_snapshot_update.</span><br><span>  */</span><br><span> struct stasis_message_type *ast_channel_snapshot_type(void);</span><br><span> </span><br><span>@@ -176,6 +176,18 @@</span><br><span> struct ast_channel_snapshot *ast_channel_snapshot_get_latest_by_name(const char *name);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send the final channel snapshot for a channel, thus removing it from cache</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \pre chan is locked</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan The channel to send the final channel snapshot for</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This will also remove the cached snapshot from the channel itself</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_channel_publish_final_snapshot(struct ast_channel *chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \since 12</span><br><span>  * \brief Creates a \ref ast_channel_blob message.</span><br><span>  *</span><br><span>@@ -303,6 +315,8 @@</span><br><span>  * \param type Type of stasis message.</span><br><span>  * \param blob The blob being published. (NULL if no blob)</span><br><span>  *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This will use the current snapshot on the channel and will not generate a new one.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span>  * \return Nothing</span><br><span>  */</span><br><span> void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type,</span><br><span>@@ -557,17 +571,6 @@</span><br><span>                const char *dialstatus,</span><br><span>              const char *forward);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \since 12</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Publish in the \ref ast_channel_topic a \ref ast_channel_snapshot</span><br><span style="color: hsl(0, 100%, 40%);">- * message indicating a change in channel state</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \pre chan is locked</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param chan The channel whose state has changed</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-void ast_publish_channel_state(struct ast_channel *chan);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! @} */</span><br><span> </span><br><span> /*!</span><br><span>diff --git a/main/aoc.c b/main/aoc.c</span><br><span>index 253c745..b8cf301 100644</span><br><span>--- a/main/aoc.c</span><br><span>+++ b/main/aoc.c</span><br><span>@@ -1849,7 +1849,9 @@</span><br><span>      }</span><br><span> </span><br><span>        if (chan) {</span><br><span style="color: hsl(0, 100%, 40%);">-             aoc_event->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+               aoc_event->snapshot = ao2_bump(ast_channel_snapshot(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_channel_unlock(chan);</span><br><span>            if (!aoc_event->snapshot) {</span><br><span>                       ao2_ref(aoc_event, -1);</span><br><span>                      return;</span><br><span>diff --git a/main/app.c b/main/app.c</span><br><span>index 953b77d..ec74490 100644</span><br><span>--- a/main/app.c</span><br><span>+++ b/main/app.c</span><br><span>@@ -3244,15 +3244,7 @@</span><br><span>        mwi_state->old_msgs = old_msgs;</span><br><span> </span><br><span>       if (!ast_strlen_zero(channel_id)) {</span><br><span style="color: hsl(0, 100%, 40%);">-             struct stasis_message *chan_message;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-            chan_message = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(),</span><br><span style="color: hsl(0, 100%, 40%);">-                       channel_id);</span><br><span style="color: hsl(0, 100%, 40%);">-            if (chan_message) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     mwi_state->snapshot = stasis_message_data(chan_message);</span><br><span style="color: hsl(0, 100%, 40%);">-                     ao2_ref(mwi_state->snapshot, +1);</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-               ao2_cleanup(chan_message);</span><br><span style="color: hsl(120, 100%, 40%);">+            mwi_state->snapshot = ast_channel_snapshot_get_latest(channel_id);</span><br><span>        }</span><br><span> </span><br><span>        if (eid) {</span><br><span>diff --git a/main/bridge.c b/main/bridge.c</span><br><span>index 2b347fd..2454857 100644</span><br><span>--- a/main/bridge.c</span><br><span>+++ b/main/bridge.c</span><br><span>@@ -5143,16 +5143,15 @@</span><br><span> {</span><br><span>   const char *uniqueid = obj;</span><br><span>  struct ast_cli_args *a = arg;</span><br><span style="color: hsl(0, 100%, 40%);">-   RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);</span><br><span>   struct ast_channel_snapshot *snapshot;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(), uniqueid);</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+   snapshot = ast_channel_snapshot_get_latest(uniqueid);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!snapshot) {</span><br><span>             return 0;</span><br><span>    }</span><br><span style="color: hsl(0, 100%, 40%);">-       snapshot = stasis_message_data(msg);</span><br><span> </span><br><span>     ast_cli(a->fd, "Channel: %s\n", snapshot->name);</span><br><span style="color: hsl(120, 100%, 40%);">+      ao2_ref(snapshot, -1);</span><br><span> </span><br><span>   return 0;</span><br><span> }</span><br><span>diff --git a/main/cdr.c b/main/cdr.c</span><br><span>index 1c47e24..e321c22 100644</span><br><span>--- a/main/cdr.c</span><br><span>+++ b/main/cdr.c</span><br><span>@@ -186,14 +186,6 @@</span><br><span>   </configInfo></span><br><span>  ***/</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* The prime here should be similar in size to the channel container. */</span><br><span style="color: hsl(0, 100%, 40%);">-#ifdef LOW_MEMORY</span><br><span style="color: hsl(0, 100%, 40%);">-#define NUM_CDR_BUCKETS 61</span><br><span style="color: hsl(0, 100%, 40%);">-#else</span><br><span style="color: hsl(0, 100%, 40%);">-#define NUM_CDR_BUCKETS 769</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> #define DEFAULT_ENABLED "1"</span><br><span> #define DEFAULT_BATCHMODE "0"</span><br><span> #define DEFAULT_UNANSWERED "0"</span><br><span>@@ -2056,9 +2048,9 @@</span><br><span> </span><br><span> /*!</span><br><span>  * \internal</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Filter a channel cache update</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Filter a channel snapshot update</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-static int filter_channel_cache_message(struct ast_channel_snapshot *old_snapshot,</span><br><span style="color: hsl(120, 100%, 40%);">+static int filter_channel_snapshot_message(struct ast_channel_snapshot *old_snapshot,</span><br><span>              struct ast_channel_snapshot *new_snapshot)</span><br><span> {</span><br><span>      int ret = 0;</span><br><span>@@ -2256,52 +2248,38 @@</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Handler for Stasis-Core channel cache update messages</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Handler for channel snapshot update messages</span><br><span>  * \param data Passed on</span><br><span>  * \param sub The stasis subscription for this message callback</span><br><span>  * \param topic The topic this message was published for</span><br><span>  * \param message The message</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-static void handle_channel_cache_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_channel_snapshot_update_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)</span><br><span> {</span><br><span>  struct cdr_object *cdr;</span><br><span style="color: hsl(0, 100%, 40%);">- struct stasis_cache_update *update = stasis_message_data(message);</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_channel_snapshot *old_snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_channel_snapshot *new_snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_channel_snapshot_update *update = stasis_message_data(message);</span><br><span>   struct cdr_object *it_cdr;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  ast_assert(update != NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-     ast_assert(ast_channel_snapshot_type() == update->type);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     old_snapshot = stasis_message_data(update->old_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-    new_snapshot = stasis_message_data(update->new_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if (filter_channel_cache_message(old_snapshot, new_snapshot)) {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (filter_channel_snapshot_message(update->old_snapshot, update->new_snapshot)) {</span><br><span>             return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (new_snapshot && !old_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-            cdr = cdr_object_alloc(new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (update->new_snapshot && !update->old_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+            cdr = cdr_object_alloc(update->new_snapshot);</span><br><span>             if (!cdr) {</span><br><span>                  return;</span><br><span>              }</span><br><span>            cdr->is_root = 1;</span><br><span>                 ao2_link(active_cdrs_master, cdr);</span><br><span>   } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                const char *uniqueid;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-           uniqueid = new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid;</span><br><span style="color: hsl(0, 100%, 40%);">-                cdr = ao2_find(active_cdrs_master, uniqueid, OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+         cdr = ao2_find(active_cdrs_master, update->new_snapshot->uniqueid, OBJ_SEARCH_KEY);</span><br><span>    }</span><br><span> </span><br><span>        /* Handle Party A */</span><br><span>         if (!cdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-             const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               name = new_snapshot ? new_snapshot->name : old_snapshot->name;</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", update->new_snapshot->name);</span><br><span>             ast_assert(0);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+    } else {</span><br><span>             int all_reject = 1;</span><br><span> </span><br><span>              ao2_lock(cdr);</span><br><span>@@ -2309,21 +2287,23 @@</span><br><span>                     if (!it_cdr->fn_table->process_party_a) {</span><br><span>                              continue;</span><br><span>                    }</span><br><span style="color: hsl(0, 100%, 40%);">-                       all_reject &= it_cdr->fn_table->process_party_a(it_cdr, new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+                      all_reject &= it_cdr->fn_table->process_party_a(it_cdr, update->new_snapshot);</span><br><span>          }</span><br><span style="color: hsl(0, 100%, 40%);">-               if (all_reject && check_new_cdr_needed(old_snapshot, new_snapshot)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (all_reject && check_new_cdr_needed(update->old_snapshot, update->new_snapshot)) {</span><br><span>                  /* We're not hung up and we have a new snapshot - we need a new CDR */</span><br><span>                   struct cdr_object *new_cdr;</span><br><span> </span><br><span>                      new_cdr = cdr_object_create_and_append(cdr);</span><br><span>                         if (new_cdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                          new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+                              new_cdr->fn_table->process_party_a(new_cdr, update->new_snapshot);</span><br><span>                  }</span><br><span>            }</span><br><span>            ao2_unlock(cdr);</span><br><span style="color: hsl(0, 100%, 40%);">-        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {</span><br><span>          ao2_lock(cdr);</span><br><span style="color: hsl(0, 100%, 40%);">-          CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);</span><br><span style="color: hsl(120, 100%, 40%);">+         CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, update->old_snapshot->name);</span><br><span>             for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {</span><br><span>                       cdr_object_finalize(it_cdr);</span><br><span>                 }</span><br><span>@@ -2335,12 +2315,14 @@</span><br><span>  }</span><br><span> </span><br><span>        /* Handle Party B */</span><br><span style="color: hsl(0, 100%, 40%);">-    if (new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+   if (update->new_snapshot) {</span><br><span>               ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,</span><br><span style="color: hsl(0, 100%, 40%);">-                  cdr_object_update_party_b, (char *) new_snapshot->name, new_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      cdr_object_update_party_b, (char *) update->new_snapshot->name, update->new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {</span><br><span>          ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,</span><br><span style="color: hsl(0, 100%, 40%);">-                  cdr_object_finalize_party_b, (char *) old_snapshot->name, old_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+                   cdr_object_finalize_party_b, (char *) update->new_snapshot->name, update->new_snapshot);</span><br><span>    }</span><br><span> </span><br><span>        ao2_cleanup(cdr);</span><br><span>@@ -4302,7 +4284,7 @@</span><br><span>            return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   channel_subscription = stasis_forward_all(ast_channel_topic_all_cached(), cdr_topic);</span><br><span style="color: hsl(120, 100%, 40%);">+ channel_subscription = stasis_forward_all(ast_channel_topic_all(), cdr_topic);</span><br><span>       if (!channel_subscription) {</span><br><span>                 return -1;</span><br><span>   }</span><br><span>@@ -4522,7 +4504,7 @@</span><br><span>            return AST_MODULE_LOAD_FAILURE;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   stasis_message_router_add_cache_update(stasis_router, ast_channel_snapshot_type(), handle_channel_cache_message, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       stasis_message_router_add(stasis_router, ast_channel_snapshot_type(), handle_channel_snapshot_update_message, NULL);</span><br><span>         stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);</span><br><span>        stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);</span><br><span>      stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);</span><br><span>@@ -4530,14 +4512,14 @@</span><br><span>       stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL);</span><br><span> </span><br><span>        active_cdrs_master = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,</span><br><span style="color: hsl(0, 100%, 40%);">-              NUM_CDR_BUCKETS, cdr_master_hash_fn, NULL, cdr_master_cmp_fn);</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_NUM_CHANNEL_BUCKETS, cdr_master_hash_fn, NULL, cdr_master_cmp_fn);</span><br><span>       if (!active_cdrs_master) {</span><br><span>           return AST_MODULE_LOAD_FAILURE;</span><br><span>      }</span><br><span>    ao2_container_register("cdrs_master", active_cdrs_master, cdr_master_print_fn);</span><br><span> </span><br><span>        active_cdrs_all = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,</span><br><span style="color: hsl(0, 100%, 40%);">-         NUM_CDR_BUCKETS, cdr_all_hash_fn, NULL, cdr_all_cmp_fn);</span><br><span style="color: hsl(120, 100%, 40%);">+              AST_NUM_CHANNEL_BUCKETS, cdr_all_hash_fn, NULL, cdr_all_cmp_fn);</span><br><span>     if (!active_cdrs_all) {</span><br><span>              return AST_MODULE_LOAD_FAILURE;</span><br><span>      }</span><br><span>diff --git a/main/cel.c b/main/cel.c</span><br><span>index 0ec728e..5f4de78 100644</span><br><span>--- a/main/cel.c</span><br><span>+++ b/main/cel.c</span><br><span>@@ -888,14 +888,6 @@</span><br><span> {</span><br><span>   int is_hungup, was_hungup;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (!new_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-            cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-          if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        check_retire_linkedid(old_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    if (!old_snapshot) {</span><br><span>                 cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);</span><br><span>             return;</span><br><span>@@ -915,6 +907,11 @@</span><br><span>               cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);</span><br><span>           ast_json_unref(extra);</span><br><span>               ao2_cleanup(dialstatus);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            cel_report_event(new_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      check_retire_linkedid(new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span>            return;</span><br><span>      }</span><br><span> </span><br><span>@@ -928,7 +925,7 @@</span><br><span>  struct ast_channel_snapshot *old_snapshot,</span><br><span>   struct ast_channel_snapshot *new_snapshot)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!old_snapshot) {</span><br><span>                 return;</span><br><span>      }</span><br><span> </span><br><span>@@ -946,8 +943,7 @@</span><br><span>  struct ast_channel_snapshot *old_snapshot,</span><br><span>   struct ast_channel_snapshot *new_snapshot)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- if (new_snapshot && old_snapshot</span><br><span style="color: hsl(0, 100%, 40%);">-                && !strcmp(old_snapshot->appl, new_snapshot->appl)) {</span><br><span style="color: hsl(120, 100%, 40%);">+   if (old_snapshot && !strcmp(old_snapshot->appl, new_snapshot->appl)) {</span><br><span>                 return;</span><br><span>      }</span><br><span> </span><br><span>@@ -957,7 +953,7 @@</span><br><span>  }</span><br><span> </span><br><span>        /* new snapshot has an application, start it */</span><br><span style="color: hsl(0, 100%, 40%);">- if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!ast_strlen_zero(new_snapshot->appl)) {</span><br><span>               cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);</span><br><span>         }</span><br><span> }</span><br><span>@@ -984,22 +980,15 @@</span><br><span> static void cel_snapshot_update_cb(void *data, struct stasis_subscription *sub,</span><br><span>    struct stasis_message *message)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct stasis_cache_update *update = stasis_message_data(message);</span><br><span style="color: hsl(0, 100%, 40%);">-      if (ast_channel_snapshot_type() == update->type) {</span><br><span style="color: hsl(0, 100%, 40%);">-           struct ast_channel_snapshot *old_snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-              struct ast_channel_snapshot *new_snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-              size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_channel_snapshot_update *update = stasis_message_data(message);</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t i;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           old_snapshot = stasis_message_data(update->old_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-            new_snapshot = stasis_message_data(update->new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (cel_filter_channel_snapshot(update->old_snapshot) || cel_filter_channel_snapshot(update->new_snapshot)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (cel_filter_channel_snapshot(old_snapshot) || cel_filter_channel_snapshot(new_snapshot)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   return;</span><br><span style="color: hsl(0, 100%, 40%);">-         }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {</span><br><span style="color: hsl(0, 100%, 40%);">-                      cel_channel_monitors[i](old_snapshot, new_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+            cel_channel_monitors[i](update->old_snapshot, update->new_snapshot);</span><br><span>   }</span><br><span> }</span><br><span> </span><br><span>@@ -1453,7 +1442,7 @@</span><br><span>   }</span><br><span> </span><br><span>        cel_channel_forwarder = stasis_forward_all(</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_channel_topic_all_cached(),</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_channel_topic_all(),</span><br><span>             cel_aggregation_topic);</span><br><span>      if (!cel_channel_forwarder) {</span><br><span>                return -1;</span><br><span>@@ -1498,7 +1487,7 @@</span><br><span>           6 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);</span><br><span> </span><br><span>         ret |= stasis_message_router_add(cel_state_router,</span><br><span style="color: hsl(0, 100%, 40%);">-              stasis_cache_update_type(),</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_channel_snapshot_type(),</span><br><span>                 cel_snapshot_update_cb,</span><br><span>              NULL);</span><br><span> </span><br><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index 2bacfa4..92d6f7e 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -116,12 +116,6 @@</span><br><span> /*! \brief the list of registered channel types */</span><br><span> static AST_RWLIST_HEAD_STATIC(backends, chanlist);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#ifdef LOW_MEMORY</span><br><span style="color: hsl(0, 100%, 40%);">-#define NUM_CHANNEL_BUCKETS 61</span><br><span style="color: hsl(0, 100%, 40%);">-#else</span><br><span style="color: hsl(0, 100%, 40%);">-#define NUM_CHANNEL_BUCKETS 1567</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! \brief All active channels on the system */</span><br><span> static struct ao2_container *channels;</span><br><span> </span><br><span>@@ -635,38 +629,6 @@</span><br><span>     return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct stasis_message *create_channel_snapshot_message(struct ast_channel *channel)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!ast_channel_snapshot_type()) {</span><br><span style="color: hsl(0, 100%, 40%);">-             return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_channel_lock(channel);</span><br><span style="color: hsl(0, 100%, 40%);">-      snapshot = ast_channel_snapshot_create(channel);</span><br><span style="color: hsl(0, 100%, 40%);">-        ast_channel_unlock(channel);</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-                return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return stasis_message_create(ast_channel_snapshot_type(), snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void publish_cache_clear(struct ast_channel *chan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-  RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        clear_msg = create_channel_snapshot_message(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-      if (!clear_msg) {</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       message = stasis_cache_clear_create(clear_msg);</span><br><span style="color: hsl(0, 100%, 40%);">- stasis_publish(ast_channel_topic(chan), message);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! \brief Gives the string form of a given channel state.</span><br><span>  *</span><br><span>  * \note This function is not reentrant.</span><br><span>@@ -1236,7 +1198,9 @@</span><br><span>                                "musicclass", musicclass);</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_channel_publish_cached_blob(chan, ast_channel_hold_type(), blob);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_publish_blob(chan, ast_channel_hold_type(), blob);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_channel_unlock(chan);</span><br><span> </span><br><span>        res = ast_queue_frame(chan, &f);</span><br><span> </span><br><span>@@ -1250,7 +1214,9 @@</span><br><span>     struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_UNHOLD };</span><br><span>  int res;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    ast_channel_publish_cached_blob(chan, ast_channel_unhold_type(), NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_channel_publish_blob(chan, ast_channel_unhold_type(), NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_channel_unlock(chan);</span><br><span> </span><br><span>        res = ast_queue_frame(chan, &f);</span><br><span> </span><br><span>@@ -2230,9 +2196,8 @@</span><br><span>             ast_assert(!ast_test_flag(ast_channel_flags(chan), AST_FLAG_SNAPSHOT_STAGE));</span><br><span> </span><br><span>            ast_channel_lock(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-         ast_channel_publish_snapshot(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_channel_publish_final_snapshot(chan);</span><br><span>            ast_channel_unlock(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-               publish_cache_clear(chan);</span><br><span>   }</span><br><span> </span><br><span>        ast_channel_lock(chan);</span><br><span>@@ -3344,7 +3309,7 @@</span><br><span>              return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_channel_publish_cached_blob(chan, ast_channel_dtmf_begin_type(), blob);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_channel_publish_blob(chan, ast_channel_dtmf_begin_type(), blob);</span><br><span> }</span><br><span> </span><br><span> static void send_dtmf_end_event(struct ast_channel *chan,</span><br><span>@@ -3361,7 +3326,7 @@</span><br><span>           return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   ast_channel_publish_cached_blob(chan, ast_channel_dtmf_end_type(), blob);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_channel_publish_blob(chan, ast_channel_dtmf_end_type(), blob);</span><br><span> }</span><br><span> </span><br><span> static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f)</span><br><span>@@ -6819,6 +6784,9 @@</span><br><span>         /* Make sure the Stasis topic on the channel is updated appropriately */</span><br><span>     ast_channel_internal_swap_topics(clonechan, original);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    /* The old snapshots need to follow the channels so the snapshot update is correct */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_internal_swap_snapshots(clonechan, original);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* Swap channel names. This uses ast_channel_name_set directly, so we</span><br><span>         * don't get any spurious rename events.</span><br><span>          */</span><br><span>@@ -7246,7 +7214,7 @@</span><br><span> </span><br><span>      ast_channel_state_set(chan, state);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_publish_channel_state(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_channel_publish_snapshot(chan);</span><br><span> </span><br><span>      /* We have to pass AST_DEVICE_UNKNOWN here because it is entirely possible that the channel driver</span><br><span>    * for this channel is using the callback method for device state. If we pass in an actual state here</span><br><span>@@ -7856,7 +7824,7 @@</span><br><span> </span><br><span> int ast_channels_init(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   channels = ao2_container_alloc(NUM_CHANNEL_BUCKETS,</span><br><span style="color: hsl(120, 100%, 40%);">+   channels = ao2_container_alloc(AST_NUM_CHANNEL_BUCKETS,</span><br><span>                      ast_channel_hash_cb, ast_channel_cmp_cb);</span><br><span>    if (!channels) {</span><br><span>             return -1;</span><br><span>diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c</span><br><span>index b926514..517ddab 100644</span><br><span>--- a/main/channel_internal_api.c</span><br><span>+++ b/main/channel_internal_api.c</span><br><span>@@ -42,7 +42,6 @@</span><br><span> #include "asterisk/channel_internal.h"</span><br><span> #include "asterisk/endpoints.h"</span><br><span> #include "asterisk/indications.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/stasis_cache_pattern.h"</span><br><span> #include "asterisk/stasis_channels.h"</span><br><span> #include "asterisk/stasis_endpoints.h"</span><br><span> #include "asterisk/stringfields.h"</span><br><span>@@ -215,12 +214,14 @@</span><br><span>   char dtmf_digit_to_emulate;                     /*!< Digit being emulated */</span><br><span>      char sending_dtmf_digit;                        /*!< Digit this channel is currently sending out. (zero if not sending) */</span><br><span>        struct timeval sending_dtmf_tv;         /*!< The time this channel started sending the current digit. (Invalid if sending_dtmf_digit is zero.) */</span><br><span style="color: hsl(0, 100%, 40%);">-    struct stasis_cp_single *topics;                /*!< Topic for all channel's events */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic;             /*!< Topic for trhis channel */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct stasis_forward *channel_forward; /*!< Subscription for event forwarding to all channel topic */</span><br><span>    struct stasis_forward *endpoint_forward;        /*!< Subscription for event forwarding to endpoint's topic */</span><br><span>         struct stasis_forward *endpoint_cache_forward; /*!< Subscription for cache updates to endpoint's topic */</span><br><span>     struct ast_stream_topology *stream_topology; /*!< Stream topology */</span><br><span>      void *stream_topology_change_source; /*!< Source that initiated a stream topology change */</span><br><span>       struct ast_stream *default_streams[AST_MEDIA_TYPE_END]; /*!< Default streams indexed by media type */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_channel_snapshot *snapshot; /*!< The current up to date snapshot of the channel */</span><br><span> };</span><br><span> </span><br><span> /*! \brief The monotonically increasing integer counter for channel uniqueids */</span><br><span>@@ -1379,11 +1380,25 @@</span><br><span> </span><br><span> void ast_channel_internal_swap_topics(struct ast_channel *a, struct ast_channel *b)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        struct stasis_cp_single *temp;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct stasis_topic *topic;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct stasis_forward *forward;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     temp = a->topics;</span><br><span style="color: hsl(0, 100%, 40%);">-    a->topics = b->topics;</span><br><span style="color: hsl(0, 100%, 40%);">-    b->topics = temp;</span><br><span style="color: hsl(120, 100%, 40%);">+  topic = a->topic;</span><br><span style="color: hsl(120, 100%, 40%);">+  a->topic = b->topic;</span><br><span style="color: hsl(120, 100%, 40%);">+    b->topic = topic;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        forward = a->channel_forward;</span><br><span style="color: hsl(120, 100%, 40%);">+      a->channel_forward = b->channel_forward;</span><br><span style="color: hsl(120, 100%, 40%);">+        b->channel_forward = forward;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_channel_internal_swap_snapshots(struct ast_channel *a, struct ast_channel *b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      snapshot = a->snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+    a->snapshot = b->snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+      b->snapshot = snapshot;</span><br><span> }</span><br><span> </span><br><span> void ast_channel_internal_set_fake_ids(struct ast_channel *chan, const char *uniqueid, const char *linkedid)</span><br><span>@@ -1402,11 +1417,12 @@</span><br><span> </span><br><span>    ast_string_field_free_memory(chan);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       chan->channel_forward = stasis_forward_cancel(chan->channel_forward);</span><br><span>  chan->endpoint_forward = stasis_forward_cancel(chan->endpoint_forward);</span><br><span>        chan->endpoint_cache_forward = stasis_forward_cancel(chan->endpoint_cache_forward);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   stasis_cp_single_unsubscribe(chan->topics);</span><br><span style="color: hsl(0, 100%, 40%);">-  chan->topics = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_cleanup(chan->topic);</span><br><span style="color: hsl(120, 100%, 40%);">+  chan->topic = NULL;</span><br><span> </span><br><span>   ast_channel_internal_set_stream_topology(chan, NULL);</span><br><span> </span><br><span>@@ -1429,16 +1445,7 @@</span><br><span>           return ast_channel_topic_all();</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   return stasis_cp_single_topic(chan->topics);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_topic *ast_channel_topic_cached(struct ast_channel *chan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!chan) {</span><br><span style="color: hsl(0, 100%, 40%);">-            return ast_channel_topic_all_cached();</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return stasis_cp_single_topic_cached(chan->topics);</span><br><span style="color: hsl(120, 100%, 40%);">+        return chan->topic;</span><br><span> }</span><br><span> </span><br><span> int ast_channel_forward_endpoint(struct ast_channel *chan,</span><br><span>@@ -1454,7 +1461,7 @@</span><br><span>                return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   chan->endpoint_cache_forward = stasis_forward_all(ast_channel_topic_cached(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+  chan->endpoint_cache_forward = stasis_forward_all(ast_channel_topic(chan),</span><br><span>                ast_endpoint_topic(endpoint));</span><br><span>       if (!chan->endpoint_cache_forward) {</span><br><span>              chan->endpoint_forward = stasis_forward_cancel(chan->endpoint_forward);</span><br><span>@@ -1467,15 +1474,22 @@</span><br><span> int ast_channel_internal_setup_topics(struct ast_channel *chan)</span><br><span> {</span><br><span>      const char *topic_name = chan->uniqueid.unique_id;</span><br><span style="color: hsl(0, 100%, 40%);">-   ast_assert(chan->topics == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_assert(chan->topic == NULL);</span><br><span> </span><br><span>      if (ast_strlen_zero(topic_name)) {</span><br><span>           topic_name = "<dummy-channel>";</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   chan->topics = stasis_cp_single_create(</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_channel_cache_all(), topic_name);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!chan->topics) {</span><br><span style="color: hsl(120, 100%, 40%);">+       chan->topic = stasis_topic_create(topic_name);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!chan->topic) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   chan->channel_forward = stasis_forward_all(ast_channel_topic(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_channel_topic_all());</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!chan->channel_forward) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(chan->topic, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+          chan->topic = NULL;</span><br><span>               return -1;</span><br><span>   }</span><br><span> </span><br><span>@@ -1566,3 +1580,14 @@</span><br><span> {</span><br><span>  return (chan->tech && chan->tech->read_stream && chan->tech->write_stream);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_channel_snapshot *ast_channel_snapshot(const struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return chan->snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_channel_snapshot_set(struct ast_channel *chan, struct ast_channel_snapshot *snapshot)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_cleanup(chan->snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+       chan->snapshot = snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/main/cli.c b/main/cli.c</span><br><span>index cf51d0d..5484e47 100644</span><br><span>--- a/main/cli.c</span><br><span>+++ b/main/cli.c</span><br><span>@@ -956,7 +956,7 @@</span><br><span> </span><br><span>      struct ao2_container *channels;</span><br><span>      struct ao2_iterator it_chans;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct stasis_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_channel_snapshot *cs;</span><br><span>     int numchans = 0, concise = 0, verbose = 0, count = 0;</span><br><span> </span><br><span>   switch (cmd) {</span><br><span>@@ -989,11 +989,7 @@</span><br><span>        } else if (a->argc != e->args - 1)</span><br><span>             return CLI_SHOWUSAGE;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!(channels = stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type()))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_cli(a->fd, "Failed to retrieve cached channels\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            return CLI_SUCCESS;</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(120, 100%, 40%);">+     channels = ast_channel_cache_by_name();</span><br><span> </span><br><span>  if (!count) {</span><br><span>                if (!concise && !verbose)</span><br><span>@@ -1004,8 +1000,7 @@</span><br><span>    }</span><br><span> </span><br><span>        it_chans = ao2_iterator_init(channels, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-      for (; (msg = ao2_iterator_next(&it_chans)); ao2_ref(msg, -1)) {</span><br><span style="color: hsl(0, 100%, 40%);">-            struct ast_channel_snapshot *cs = stasis_message_data(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+   for (; (cs = ao2_iterator_next(&it_chans)); ao2_ref(cs, -1)) {</span><br><span>           char durbuf[16] = "-";</span><br><span> </span><br><span>                 if (!count) {</span><br><span>@@ -1679,29 +1674,25 @@</span><br><span>      struct ao2_container *cached_channels;</span><br><span>       char *ret = NULL;</span><br><span>    struct ao2_iterator iter;</span><br><span style="color: hsl(0, 100%, 40%);">-       struct stasis_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_channel_snapshot *snapshot;</span><br><span> </span><br><span>   if (pos != rpos) {</span><br><span>           return NULL;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!(cached_channels = stasis_cache_dump(ast_channel_cache(), ast_channel_snapshot_type()))) {</span><br><span style="color: hsl(0, 100%, 40%);">-         return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(120, 100%, 40%);">+     cached_channels = ast_channel_cache_all();</span><br><span> </span><br><span>       iter = ao2_iterator_init(cached_channels, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-   for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                struct ast_channel_snapshot *snapshot = stasis_message_data(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+     for (; (snapshot = ao2_iterator_next(&iter)); ao2_ref(snapshot, -1)) {</span><br><span>           if (!strncasecmp(word, snapshot->name, wordlen) && (++which > state)) {</span><br><span>                        if (state != -1) {</span><br><span>                           ret = ast_strdup(snapshot->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                            ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                             ao2_ref(snapshot, -1);</span><br><span>                               break;</span><br><span>                       }</span><br><span> </span><br><span>                        if (ast_cli_completion_add(ast_strdup(snapshot->name))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            ao2_ref(msg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                             ao2_ref(snapshot, -1);</span><br><span>                               break;</span><br><span>                       }</span><br><span>            }</span><br><span>diff --git a/main/endpoints.c b/main/endpoints.c</span><br><span>index f1608f3..992da1f 100644</span><br><span>--- a/main/endpoints.c</span><br><span>+++ b/main/endpoints.c</span><br><span>@@ -179,25 +179,23 @@</span><br><span>       return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! \brief Handler for channel snapshot cache clears */</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Handler for channel snapshot update */</span><br><span> static void endpoint_cache_clear(void *data,</span><br><span>         struct stasis_subscription *sub,</span><br><span>     struct stasis_message *message)</span><br><span> {</span><br><span>         struct ast_endpoint *endpoint = data;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct stasis_message *clear_msg = stasis_message_data(message);</span><br><span style="color: hsl(0, 100%, 40%);">-        struct ast_channel_snapshot *clear_snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_channel_snapshot_update *update = stasis_message_data(message);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (stasis_message_type(clear_msg) != ast_channel_snapshot_type()) {</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Only when the channel is dead do we remove it */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {</span><br><span>                 return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   clear_snapshot = stasis_message_data(clear_msg);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     ast_assert(endpoint != NULL);</span><br><span> </span><br><span>    ao2_lock(endpoint);</span><br><span style="color: hsl(0, 100%, 40%);">-     ast_str_container_remove(endpoint->channel_ids, clear_snapshot->uniqueid);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_str_container_remove(endpoint->channel_ids, update->new_snapshot->uniqueid);</span><br><span>    ao2_unlock(endpoint);</span><br><span>        endpoint_publish_snapshot(endpoint);</span><br><span> }</span><br><span>@@ -269,7 +267,7 @@</span><br><span>                      return NULL;</span><br><span>                 }</span><br><span>            r |= stasis_message_router_add(endpoint->router,</span><br><span style="color: hsl(0, 100%, 40%);">-                     stasis_cache_clear_type(), endpoint_cache_clear,</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_channel_snapshot_type(), endpoint_cache_clear,</span><br><span>                   endpoint);</span><br><span>           r |= stasis_message_router_set_default(endpoint->router,</span><br><span>                  endpoint_default, endpoint);</span><br><span>diff --git a/main/manager.c b/main/manager.c</span><br><span>index 7accaa1..9d67e0c 100644</span><br><span>--- a/main/manager.c</span><br><span>+++ b/main/manager.c</span><br><span>@@ -6248,7 +6248,7 @@</span><br><span>    int numchans = 0;</span><br><span>    struct ao2_container *channels;</span><br><span>      struct ao2_iterator it_chans;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct stasis_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_channel_snapshot *cs;</span><br><span> </span><br><span>         if (!ast_strlen_zero(actionid)) {</span><br><span>            snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);</span><br><span>@@ -6256,17 +6256,12 @@</span><br><span>          idText[0] = '\0';</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   channels = stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type());</span><br><span style="color: hsl(0, 100%, 40%);">- if (!channels) {</span><br><span style="color: hsl(0, 100%, 40%);">-                astman_send_error(s, m, "Could not get cached channels");</span><br><span style="color: hsl(0, 100%, 40%);">-             return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(120, 100%, 40%);">+     channels = ast_channel_cache_by_name();</span><br><span> </span><br><span>  astman_send_listack(s, m, "Channels will follow", "start");</span><br><span> </span><br><span>  it_chans = ao2_iterator_init(channels, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-      for (; (msg = ao2_iterator_next(&it_chans)); ao2_ref(msg, -1)) {</span><br><span style="color: hsl(0, 100%, 40%);">-            struct ast_channel_snapshot *cs = stasis_message_data(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+   for (; (cs = ao2_iterator_next(&it_chans)); ao2_ref(cs, -1)) {</span><br><span>           struct ast_str *built = ast_manager_build_channel_state_string_prefix(cs, "");</span><br><span>             char durbuf[16] = "";</span><br><span> </span><br><span>diff --git a/main/manager_bridges.c b/main/manager_bridges.c</span><br><span>index b7059f4..1b57049 100644</span><br><span>--- a/main/manager_bridges.c</span><br><span>+++ b/main/manager_bridges.c</span><br><span>@@ -528,17 +528,14 @@</span><br><span>     char *uniqueid = obj;</span><br><span>        struct mansession *s = arg;</span><br><span>  struct bridge_list_data *list_data = data;</span><br><span style="color: hsl(0, 100%, 40%);">-      RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+        RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);</span><br><span>        RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   msg = stasis_cache_get(ast_channel_cache(),</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_channel_snapshot_type(), uniqueid);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+   snapshot = ast_channel_snapshot_get_latest(uniqueid);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!snapshot) {</span><br><span>             return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   snapshot = stasis_message_data(msg);</span><br><span>         if (snapshot->tech_properties & AST_CHAN_TP_INTERNAL) {</span><br><span>               return 0;</span><br><span>    }</span><br><span>diff --git a/main/manager_channels.c b/main/manager_channels.c</span><br><span>index ac09d42..887f77e 100644</span><br><span>--- a/main/manager_channels.c</span><br><span>+++ b/main/manager_channels.c</span><br><span>@@ -576,11 +576,6 @@</span><br><span> {</span><br><span>       int is_hungup, was_hungup;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  if (!new_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-            /* Ignore cache clearing events; we'll see the hangup first */</span><br><span style="color: hsl(0, 100%, 40%);">-              return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* The Newchannel, Newstate and Hangup events are closely related, in</span><br><span>         * in that they are mutually exclusive, basically different flavors</span><br><span>   * of a new channel state event.</span><br><span>@@ -616,11 +611,6 @@</span><br><span>      struct ast_channel_snapshot *old_snapshot,</span><br><span>   struct ast_channel_snapshot *new_snapshot)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- /* No Newexten event on cache clear */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!new_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-            return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* Empty application is not valid for a Newexten event */</span><br><span>    if (ast_strlen_zero(new_snapshot->appl)) {</span><br><span>                return NULL;</span><br><span>@@ -654,8 +644,8 @@</span><br><span>   struct ast_manager_event_blob *res;</span><br><span>  char *callerid;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     /* No NewCallerid event on cache clear or first event */</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No NewCallerid event on first channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!old_snapshot) {</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span>@@ -682,8 +672,8 @@</span><br><span>  struct ast_channel_snapshot *old_snapshot,</span><br><span>   struct ast_channel_snapshot *new_snapshot)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- /* No NewConnectedLine event on cache clear or first event */</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No NewConnectedLine event on first channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!old_snapshot) {</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span>@@ -699,7 +689,7 @@</span><br><span>  struct ast_channel_snapshot *old_snapshot,</span><br><span>   struct ast_channel_snapshot *new_snapshot)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!old_snapshot) {</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span>@@ -724,21 +714,14 @@</span><br><span>                                    struct stasis_message *message)</span><br><span> {</span><br><span>     RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);</span><br><span style="color: hsl(0, 100%, 40%);">-       struct stasis_cache_update *update;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_channel_snapshot *old_snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_channel_snapshot *new_snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_channel_snapshot_update *update;</span><br><span>  size_t i;</span><br><span> </span><br><span>        update = stasis_message_data(message);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      ast_assert(ast_channel_snapshot_type() == update->type);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     old_snapshot = stasis_message_data(update->old_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-    new_snapshot = stasis_message_data(update->new_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>         for (i = 0; i < ARRAY_LEN(channel_monitors); ++i) {</span><br><span>               RAII_VAR(struct ast_manager_event_blob *, ev, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-               ev = channel_monitors[i](old_snapshot, new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+         ev = channel_monitors[i](update->old_snapshot, update->new_snapshot);</span><br><span> </span><br><span>              if (!ev) {</span><br><span>                   continue;</span><br><span>@@ -747,7 +730,7 @@</span><br><span>              /* If we haven't already, build the channel event string */</span><br><span>              if (!channel_event_string) {</span><br><span>                         channel_event_string =</span><br><span style="color: hsl(0, 100%, 40%);">-                          ast_manager_build_channel_state_string(new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_manager_build_channel_state_string(update->new_snapshot);</span><br><span>                     if (!channel_event_string) {</span><br><span>                                 return;</span><br><span>                      }</span><br><span>@@ -1260,7 +1243,7 @@</span><br><span>    if (!message_router) {</span><br><span>               return -1;</span><br><span>   }</span><br><span style="color: hsl(0, 100%, 40%);">-       channel_topic = ast_channel_topic_all_cached();</span><br><span style="color: hsl(120, 100%, 40%);">+       channel_topic = ast_channel_topic_all();</span><br><span>     if (!channel_topic) {</span><br><span>                return -1;</span><br><span>   }</span><br><span>@@ -1272,7 +1255,7 @@</span><br><span> </span><br><span>        ast_register_cleanup(manager_channels_shutdown);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    ret |= stasis_message_router_add_cache_update(message_router,</span><br><span style="color: hsl(120, 100%, 40%);">+ ret |= stasis_message_router_add(message_router,</span><br><span>             ast_channel_snapshot_type(), channel_snapshot_update, NULL);</span><br><span> </span><br><span>     ret |= stasis_message_router_add(message_router,</span><br><span>diff --git a/main/stasis_channels.c b/main/stasis_channels.c</span><br><span>index 8041c8e..69eecd2 100644</span><br><span>--- a/main/stasis_channels.c</span><br><span>+++ b/main/stasis_channels.c</span><br><span>@@ -36,7 +36,6 @@</span><br><span> #include "asterisk/bridge.h"</span><br><span> #include "asterisk/translate.h"</span><br><span> #include "asterisk/stasis.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/stasis_cache_pattern.h"</span><br><span> #include "asterisk/stasis_channels.h"</span><br><span> #include "asterisk/dial.h"</span><br><span> #include "asterisk/linkedlists.h"</span><br><span>@@ -117,53 +116,23 @@</span><br><span> </span><br><span> #define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct stasis_cp_all *channel_cache_all;</span><br><span style="color: hsl(0, 100%, 40%);">-static struct stasis_cache *channel_cache_by_name;</span><br><span style="color: hsl(0, 100%, 40%);">-static struct stasis_caching_topic *channel_by_name_topic;</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stasis_topic *channel_topic_all;</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ao2_container *channel_cache;</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ao2_container *channel_cache_by_name;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_cp_all *ast_channel_cache_all(void)</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *ast_channel_cache_all(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  return channel_cache_all;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_cache *ast_channel_cache(void)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return stasis_cp_all_cache(channel_cache_all);</span><br><span style="color: hsl(120, 100%, 40%);">+        return ao2_bump(channel_cache);</span><br><span> }</span><br><span> </span><br><span> struct stasis_topic *ast_channel_topic_all(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       return stasis_cp_all_topic(channel_cache_all);</span><br><span style="color: hsl(120, 100%, 40%);">+        return channel_topic_all;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_topic *ast_channel_topic_all_cached(void)</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *ast_channel_cache_by_name(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   return stasis_cp_all_topic_cached(channel_cache_all);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct stasis_cache *ast_channel_cache_by_name(void)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     return channel_cache_by_name;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static const char *channel_snapshot_get_id(struct stasis_message *message)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-  if (ast_channel_snapshot_type() != stasis_message_type(message)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-       snapshot = stasis_message_data(message);</span><br><span style="color: hsl(0, 100%, 40%);">-        return snapshot->uniqueid;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static const char *channel_snapshot_get_name(struct stasis_message *message)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-  if (ast_channel_snapshot_type() != stasis_message_type(message)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-       snapshot = stasis_message_data(message);</span><br><span style="color: hsl(0, 100%, 40%);">-        return snapshot->name;</span><br><span style="color: hsl(120, 100%, 40%);">+     return ao2_bump(channel_cache_by_name);</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span>@@ -219,6 +188,59 @@</span><br><span>       return CMP_MATCH;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Hash function (using uniqueid) for \ref ast_channel_snapshot objects</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int channel_snapshot_uniqueid_hash_cb(const void *obj, const int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_channel_snapshot *object = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *key;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (flags & OBJ_SEARCH_MASK) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case OBJ_SEARCH_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+          key = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case OBJ_SEARCH_OBJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+               key = object->uniqueid;</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return ast_str_case_hash(key);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Comparison function (using uniqueid) for \ref ast_channel_snapshot objects</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int channel_snapshot_uniqueid_cmp_cb(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct ast_channel_snapshot *object_left = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_channel_snapshot *object_right = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *right_key = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+  int cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (flags & OBJ_SEARCH_MASK) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case OBJ_SEARCH_OBJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+               right_key = object_right->uniqueid;</span><br><span style="color: hsl(120, 100%, 40%);">+        case OBJ_SEARCH_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+          cmp = strcasecmp(object_left->uniqueid, right_key);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case OBJ_SEARCH_PARTIAL_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+          cmp = strncasecmp(object_left->uniqueid, right_key, strlen(right_key));</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              cmp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cmp) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return CMP_MATCH;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void channel_snapshot_dtor(void *obj)</span><br><span> {</span><br><span>  struct ast_channel_snapshot *snapshot = obj;</span><br><span>@@ -309,6 +331,34 @@</span><br><span>  return snapshot;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void channel_snapshot_update_dtor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_channel_snapshot_update *update = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_cleanup(update->old_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(update->new_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_channel_snapshot_update *channel_snapshot_update_create(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_channel_snapshot_update *update;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ update = ao2_alloc_options(sizeof(*update), channel_snapshot_update_dtor,</span><br><span style="color: hsl(120, 100%, 40%);">+             AO2_ALLOC_OPT_LOCK_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!update) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   update->old_snapshot = ao2_bump(ast_channel_snapshot(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+       update->new_snapshot = ast_channel_snapshot_create(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!update->new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_ref(update, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return update;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void publish_message_for_channel_topics(struct stasis_message *message, struct ast_channel *chan)</span><br><span> {</span><br><span>         if (chan) {</span><br><span>@@ -521,7 +571,7 @@</span><br><span>            return NULL;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   snapshot = chan ? ast_channel_snapshot_create(chan) : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   snapshot = chan ? ao2_bump(ast_channel_snapshot(chan)) : NULL;</span><br><span>       msg = create_channel_blob_message(snapshot, type, blob);</span><br><span>     ao2_cleanup(snapshot);</span><br><span>       return msg;</span><br><span>@@ -628,38 +678,48 @@</span><br><span> </span><br><span> struct ast_channel_snapshot *ast_channel_snapshot_get_latest(const char *uniqueid)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-      struct stasis_message *message;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>       ast_assert(!ast_strlen_zero(uniqueid));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     message = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(),</span><br><span style="color: hsl(0, 100%, 40%);">-            uniqueid);</span><br><span style="color: hsl(0, 100%, 40%);">-      if (!message) {</span><br><span style="color: hsl(0, 100%, 40%);">-         return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       snapshot = ao2_bump(stasis_message_data(message));</span><br><span style="color: hsl(0, 100%, 40%);">-      ao2_ref(message, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-   return snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+      return ao2_find(channel_cache, uniqueid, OBJ_SEARCH_KEY);</span><br><span> }</span><br><span> </span><br><span> struct ast_channel_snapshot *ast_channel_snapshot_get_latest_by_name(const char *name)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct stasis_message *message;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>       ast_assert(!ast_strlen_zero(name));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- message = stasis_cache_get(ast_channel_cache_by_name(), ast_channel_snapshot_type(),</span><br><span style="color: hsl(0, 100%, 40%);">-            name);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!message) {</span><br><span style="color: hsl(0, 100%, 40%);">-         return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  return ao2_find(channel_cache_by_name, name, OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_channel_publish_final_snapshot(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_channel_snapshot_update *update;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct stasis_message *message;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ast_channel_snapshot_type()) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   snapshot = ao2_bump(stasis_message_data(message));</span><br><span style="color: hsl(120, 100%, 40%);">+    update = channel_snapshot_update_create(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!update) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   message = stasis_message_create(ast_channel_snapshot_type(), update);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* In the success path message holds a reference to update so it will be valid</span><br><span style="color: hsl(120, 100%, 40%);">+         * for the lifetime of this function until the end.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(update, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!message) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlink(channel_cache, update->old_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlink(channel_cache_by_name, update->old_snapshot);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_snapshot_set(chan, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       stasis_publish(ast_channel_topic(chan), message);</span><br><span>    ao2_ref(message, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-   return snapshot;</span><br><span> }</span><br><span> </span><br><span> static void channel_role_snapshot_dtor(void *obj)</span><br><span>@@ -763,7 +823,7 @@</span><br><span> </span><br><span> void ast_channel_publish_snapshot(struct ast_channel *chan)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_channel_snapshot_update *update;</span><br><span>  struct stasis_message *message;</span><br><span> </span><br><span>  if (!ast_channel_snapshot_type()) {</span><br><span>@@ -774,17 +834,41 @@</span><br><span>          return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   snapshot = ast_channel_snapshot_create(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+      update = channel_snapshot_update_create(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!update) {</span><br><span>               return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   message = stasis_message_create(ast_channel_snapshot_type(), snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(snapshot, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+        message = stasis_message_create(ast_channel_snapshot_type(), update);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* In the success path message holds a reference to update so it will be valid</span><br><span style="color: hsl(120, 100%, 40%);">+         * for the lifetime of this function until the end.</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(update, -1);</span><br><span>         if (!message) {</span><br><span>              return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* We lock these ourselves so that the update is atomic and there isn't time where a</span><br><span style="color: hsl(120, 100%, 40%);">+       * snapshot is not in the cache.</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_wrlock(channel_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (update->old_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_unlink_flags(channel_cache, update->old_snapshot, OBJ_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_link_flags(channel_cache, update->new_snapshot, OBJ_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(channel_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* The same applies here. */</span><br><span style="color: hsl(120, 100%, 40%);">+  ao2_wrlock(channel_cache_by_name);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (update->old_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ao2_unlink_flags(channel_cache_by_name, update->old_snapshot, OBJ_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_link_flags(channel_cache_by_name, update->new_snapshot, OBJ_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(channel_cache_by_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* As update has the reference to new_snapshot we need to bump it up here for the channel */</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_channel_snapshot_set(chan, ao2_bump(update->new_snapshot));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>         ast_assert(ast_channel_topic(chan) != NULL);</span><br><span>         stasis_publish(ast_channel_topic(chan), message);</span><br><span>    ao2_ref(message, -1);</span><br><span>@@ -840,13 +924,8 @@</span><br><span>                 ast_channel_publish_snapshot(chan);</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (chan) {</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_channel_publish_cached_blob(chan, ast_channel_varset_type(), blob);</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                /* This function is NULL safe for global variables */</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_channel_publish_blob(NULL, ast_channel_varset_type(), blob);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+     /* This function is NULL safe for global variables */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_publish_blob(chan, ast_channel_varset_type(), blob);</span><br><span>     ast_json_unref(blob);</span><br><span> }</span><br><span> </span><br><span>@@ -930,36 +1009,6 @@</span><br><span>       return ev;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-void ast_publish_channel_state(struct ast_channel *chan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct stasis_message *message;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_channel_snapshot_type()) {</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_assert(chan != NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!chan) {</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       snapshot = ast_channel_snapshot_create(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-                return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       message = stasis_message_create(ast_channel_snapshot_type(), snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_ref(snapshot, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!message) {</span><br><span style="color: hsl(0, 100%, 40%);">-         return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_assert(ast_channel_topic(chan) != NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-    stasis_publish(ast_channel_topic(chan), message);</span><br><span style="color: hsl(0, 100%, 40%);">-       ao2_ref(message, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> struct ast_json *ast_channel_snapshot_to_json(</span><br><span>     const struct ast_channel_snapshot *snapshot,</span><br><span>         const struct stasis_message_sanitizer *sanitize)</span><br><span>@@ -1331,12 +1380,12 @@</span><br><span> </span><br><span> static void stasis_channels_cleanup(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        stasis_caching_unsubscribe_and_join(channel_by_name_topic);</span><br><span style="color: hsl(0, 100%, 40%);">-     channel_by_name_topic = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(channel_topic_all);</span><br><span style="color: hsl(120, 100%, 40%);">+       channel_topic_all = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_cleanup(channel_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+   channel_cache = NULL;</span><br><span>        ao2_cleanup(channel_cache_by_name);</span><br><span>  channel_cache_by_name = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-   ao2_cleanup(channel_cache_all);</span><br><span style="color: hsl(0, 100%, 40%);">- channel_cache_all = NULL;</span><br><span> </span><br><span>        STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_snapshot_type);</span><br><span>      STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_dial_type);</span><br><span>@@ -1366,29 +1415,27 @@</span><br><span> </span><br><span>    ast_register_cleanup(stasis_channels_cleanup);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      channel_cache_all = stasis_cp_all_create("ast_channel_topic_all",</span><br><span style="color: hsl(0, 100%, 40%);">-             channel_snapshot_get_id);</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!channel_cache_all) {</span><br><span style="color: hsl(120, 100%, 40%);">+     channel_topic_all = stasis_topic_create("ast_channel_topic_all");</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!channel_topic_all) {</span><br><span>            return -1;</span><br><span>   }</span><br><span style="color: hsl(0, 100%, 40%);">-       res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type);</span><br><span style="color: hsl(0, 100%, 40%);">-  res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     channel_cache_by_name = stasis_cache_create(channel_snapshot_get_name);</span><br><span style="color: hsl(120, 100%, 40%);">+       channel_cache = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_NUM_CHANNEL_BUCKETS, channel_snapshot_uniqueid_hash_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+           channel_snapshot_uniqueid_cmp_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!channel_cache) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   channel_cache_by_name = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_NUM_CHANNEL_BUCKETS, channel_snapshot_hash_cb, channel_snapshot_cmp_cb);</span><br><span>         if (!channel_cache_by_name) {</span><br><span>                return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* This should be initialized before the caching topic */</span><br><span style="color: hsl(120, 100%, 40%);">+     res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type);</span><br><span style="color: hsl(120, 100%, 40%);">+        res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type);</span><br><span>      res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_snapshot_type);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     channel_by_name_topic = stasis_caching_topic_create(</span><br><span style="color: hsl(0, 100%, 40%);">-            stasis_cp_all_topic(channel_cache_all),</span><br><span style="color: hsl(0, 100%, 40%);">-         channel_cache_by_name);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!channel_by_name_topic) {</span><br><span style="color: hsl(0, 100%, 40%);">-           return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_dial_type);</span><br><span>      res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_varset_type);</span><br><span>    res |= STASIS_MESSAGE_TYPE_INIT(ast_channel_hangup_request_type);</span><br><span>diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c</span><br><span>index cdf0f88..44bf360 100644</span><br><span>--- a/res/ari/resource_channels.c</span><br><span>+++ b/res/ari/resource_channels.c</span><br><span>@@ -833,32 +833,19 @@</span><br><span>   struct ast_ari_channels_get_args *args,</span><br><span>      struct ast_ari_response *response)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-      struct stasis_cache *cache;</span><br><span>  struct ast_channel_snapshot *snapshot;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      cache = ast_channel_cache();</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!cache) {</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_ari_response_error(</span><br><span style="color: hsl(0, 100%, 40%);">-                 response, 500, "Internal Server Error",</span><br><span style="color: hsl(0, 100%, 40%);">-                       "Message bus not initialized");</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       msg = stasis_cache_get(cache, ast_channel_snapshot_type(),</span><br><span style="color: hsl(0, 100%, 40%);">-                                 args->channel_id);</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+   snapshot = ast_channel_snapshot_get_latest(args->channel_id);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!snapshot) {</span><br><span>             ast_ari_response_error(</span><br><span>                      response, 404, "Not Found",</span><br><span>                        "Channel not found");</span><br><span>              return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   snapshot = stasis_message_data(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-    ast_assert(snapshot != NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        ast_ari_response_ok(response,</span><br><span>                                ast_channel_snapshot_to_json(snapshot, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(snapshot, -1);</span><br><span> }</span><br><span> </span><br><span> void ast_ari_channels_hangup(struct ast_variable *headers,</span><br><span>@@ -903,27 +890,13 @@</span><br><span>        struct ast_ari_channels_list_args *args,</span><br><span>     struct ast_ari_response *response)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);</span><br><span>   RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);</span><br><span>      RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);</span><br><span>     struct ao2_iterator i;</span><br><span>       void *obj;</span><br><span>   struct stasis_message_sanitizer *sanitize = stasis_app_get_sanitizer();</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     cache = ast_channel_cache();</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!cache) {</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_ari_response_error(</span><br><span style="color: hsl(0, 100%, 40%);">-                 response, 500, "Internal Server Error",</span><br><span style="color: hsl(0, 100%, 40%);">-                       "Message bus not initialized");</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-       ao2_ref(cache, +1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     snapshots = stasis_cache_dump(cache, ast_channel_snapshot_type());</span><br><span style="color: hsl(0, 100%, 40%);">-      if (!snapshots) {</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_ari_response_alloc_failed(response);</span><br><span style="color: hsl(0, 100%, 40%);">-                return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+     snapshots = ast_channel_cache_all();</span><br><span> </span><br><span>     json = ast_json_array_create();</span><br><span>      if (!json) {</span><br><span>@@ -933,12 +906,12 @@</span><br><span> </span><br><span>     i = ao2_iterator_init(snapshots, 0);</span><br><span>         while ((obj = ao2_iterator_next(&i))) {</span><br><span style="color: hsl(0, 100%, 40%);">-             RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);</span><br><span style="color: hsl(0, 100%, 40%);">-               struct ast_channel_snapshot *snapshot = stasis_message_data(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+             struct ast_channel_snapshot *snapshot = obj;</span><br><span>                 int r;</span><br><span> </span><br><span>           if (sanitize && sanitize->channel_snapshot</span><br><span>                        && sanitize->channel_snapshot(snapshot)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ao2_ref(snapshot, -1);</span><br><span>                       continue;</span><br><span>            }</span><br><span> </span><br><span>@@ -947,8 +920,10 @@</span><br><span>                 if (r != 0) {</span><br><span>                        ast_ari_response_alloc_failed(response);</span><br><span>                     ao2_iterator_destroy(&i);</span><br><span style="color: hsl(120, 100%, 40%);">+                 ao2_ref(snapshot, -1);</span><br><span>                       return;</span><br><span>              }</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_ref(snapshot, -1);</span><br><span>       }</span><br><span>    ao2_iterator_destroy(&i);</span><br><span> </span><br><span>diff --git a/res/res_agi.c b/res/res_agi.c</span><br><span>index 0931c1a..e322d7f 100644</span><br><span>--- a/res/res_agi.c</span><br><span>+++ b/res/res_agi.c</span><br><span>@@ -3182,13 +3182,13 @@</span><br><span>                 ast_agi_send(agi->fd, chan, "200 result=%u\n", ast_channel_state(chan));</span><br><span>                return RESULT_SUCCESS;</span><br><span>       } else if (argc == 3) {</span><br><span style="color: hsl(0, 100%, 40%);">-         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+            struct ast_channel_snapshot *snapshot;</span><br><span> </span><br><span>           /* one argument: look for info on the specified channel */</span><br><span style="color: hsl(0, 100%, 40%);">-              if ((msg = stasis_cache_get(ast_channel_cache_by_name(), ast_channel_snapshot_type(), argv[2]))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                      struct ast_channel_snapshot *snapshot = stasis_message_data(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+             snapshot = ast_channel_snapshot_get_latest_by_name(argv[2]);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (snapshot) {</span><br><span>                      ast_agi_send(agi->fd, chan, "200 result=%u\n", snapshot->state);</span><br><span style="color: hsl(120, 100%, 40%);">+                      ao2_ref(snapshot, -1);</span><br><span>                       return RESULT_SUCCESS;</span><br><span>               }</span><br><span>            /* if we get this far no channel name matched the argument given */</span><br><span>diff --git a/res/res_chan_stats.c b/res/res_chan_stats.c</span><br><span>index dbc79f0..bed95a0 100644</span><br><span>--- a/res/res_chan_stats.c</span><br><span>+++ b/res/res_chan_stats.c</span><br><span>@@ -78,7 +78,7 @@</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Router callback for \ref stasis_cache_update messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Router callback for \ref ast_channel_snapshot_update messages.</span><br><span>  * \param data Data pointer given when added to router.</span><br><span>  * \param sub This subscription.</span><br><span>  * \param topic The topic the message was posted to. This is not necessarily the</span><br><span>@@ -92,34 +92,25 @@</span><br><span>      /* Since this came from a message router, we know the type of the</span><br><span>     * message. We can cast the data without checking its type.</span><br><span>   */</span><br><span style="color: hsl(0, 100%, 40%);">-     struct stasis_cache_update *update = stasis_message_data(message);</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_channel_snapshot_update *update = stasis_message_data(message);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  /* We're only interested in channel snapshots, so check the type</span><br><span style="color: hsl(0, 100%, 40%);">-     * of the underlying message.</span><br><span style="color: hsl(0, 100%, 40%);">-    */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (ast_channel_snapshot_type() != update->type) {</span><br><span style="color: hsl(0, 100%, 40%);">-           return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* There are three types of cache updates.</span><br><span style="color: hsl(0, 100%, 40%);">-       * !old && new -> Initial cache entry</span><br><span style="color: hsl(0, 100%, 40%);">-         * old && new -> Updated cache entry</span><br><span style="color: hsl(0, 100%, 40%);">-  * old && !new -> Cache entry removed.</span><br><span style="color: hsl(120, 100%, 40%);">+     /* There are three types of channel snapshot updates.</span><br><span style="color: hsl(120, 100%, 40%);">+  * !old && new -> Initial channel creation</span><br><span style="color: hsl(120, 100%, 40%);">+  * old && new -> Updated channel snapshot</span><br><span style="color: hsl(120, 100%, 40%);">+   * old && dead -> Final channel snapshot</span><br><span>   */</span><br><span> </span><br><span>      if (!update->old_snapshot && update->new_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-              /* Initial cache entry; count a channel creation */</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Initial channel snapshot; count a channel creation */</span><br><span>             ast_statsd_log_string("channels.count", AST_STATSD_GAUGE, "+1", 1.0);</span><br><span style="color: hsl(0, 100%, 40%);">-       } else if (update->old_snapshot && !update->new_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Cache entry removed. Compute the age of the channel and post</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (update->old_snapshot && ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Channel is gone. Compute the age of the channel and post</span><br><span>           * that, as well as decrementing the channel count.</span><br><span>           */</span><br><span style="color: hsl(0, 100%, 40%);">-             struct ast_channel_snapshot *last;</span><br><span>           int64_t age;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-                last = stasis_message_data(update->old_snapshot);</span><br><span>                 age = ast_tvdiff_ms(*stasis_message_timestamp(message),</span><br><span style="color: hsl(0, 100%, 40%);">-                 last->creationtime);</span><br><span style="color: hsl(120, 100%, 40%);">+                       update->new_snapshot->creationtime);</span><br><span>           ast_statsd_log("channels.calltime", AST_STATSD_TIMER, age);</span><br><span> </span><br><span>            /* And decrement the channel count */</span><br><span>@@ -161,11 +152,11 @@</span><br><span> {</span><br><span>   /* You can create a message router to route messages by type */</span><br><span>      router = stasis_message_router_create(</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_channel_topic_all_cached());</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_channel_topic_all());</span><br><span>    if (!router) {</span><br><span>               return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span style="color: hsl(0, 100%, 40%);">-       stasis_message_router_add(router, stasis_cache_update_type(),</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_message_router_add(router, ast_channel_snapshot_type(),</span><br><span>               updates, NULL);</span><br><span>      stasis_message_router_set_default(router, default_route, NULL);</span><br><span> </span><br><span>diff --git a/res/stasis/app.c b/res/stasis/app.c</span><br><span>index 18ac7d6..a23ed04 100644</span><br><span>--- a/res/stasis/app.c</span><br><span>+++ b/res/stasis/app.c</span><br><span>@@ -149,7 +149,7 @@</span><br><span>                       app->topic);</span><br><span>      }</span><br><span>    forwards->topic_cached_forward = stasis_forward_all(</span><br><span style="color: hsl(0, 100%, 40%);">-         chan ? ast_channel_topic_cached(chan) : ast_channel_topic_all_cached(),</span><br><span style="color: hsl(120, 100%, 40%);">+               chan ? ast_channel_topic(chan) : ast_channel_topic_all(),</span><br><span>            app->topic);</span><br><span> </span><br><span>  if ((!forwards->topic_forward && chan) || !forwards->topic_cached_forward) {</span><br><span>@@ -420,7 +420,7 @@</span><br><span> </span><br><span>         if (!old_snapshot) {</span><br><span>                 return channel_created_event(snapshot, tv);</span><br><span style="color: hsl(0, 100%, 40%);">-     } else if (!new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+   } else if (ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD)) {</span><br><span>              return channel_destroyed_event(snapshot, tv);</span><br><span>        } else if (old_snapshot->state != new_snapshot->state) {</span><br><span>               return channel_state_change_event(snapshot, tv);</span><br><span>@@ -436,8 +436,8 @@</span><br><span> {</span><br><span>  struct ast_json *json_channel;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      /* No Newexten event on cache clear or first event */</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No Newexten event on first channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!old_snapshot) {</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span>@@ -470,8 +470,8 @@</span><br><span> {</span><br><span>     struct ast_json *json_channel;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      /* No NewCallerid event on cache clear or first event */</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No NewCallerid event on first channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!old_snapshot) {</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span>@@ -500,8 +500,8 @@</span><br><span> {</span><br><span>     struct ast_json *json_channel;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      /* No ChannelConnectedLine event on cache clear or first event */</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!old_snapshot || !new_snapshot) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No ChannelConnectedLine event on first channel snapshot */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!old_snapshot) {</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span>@@ -532,39 +532,22 @@</span><br><span>        struct stasis_message *message)</span><br><span> {</span><br><span>         struct stasis_app *app = data;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct stasis_cache_update *update;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_channel_snapshot *new_snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct ast_channel_snapshot *old_snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-      const struct timeval *tv;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_channel_snapshot_update *update = stasis_message_data(message);</span><br><span>   int i;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      ast_assert(stasis_message_type(message) == stasis_cache_update_type());</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- update = stasis_message_data(message);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_assert(update->type == ast_channel_snapshot_type());</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     new_snapshot = stasis_message_data(update->new_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-    old_snapshot = stasis_message_data(update->old_snapshot);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Pull timestamp from the new snapshot, or from the update message</span><br><span style="color: hsl(0, 100%, 40%);">-      * when there isn't one. */</span><br><span style="color: hsl(0, 100%, 40%);">- tv = update->new_snapshot ?</span><br><span style="color: hsl(0, 100%, 40%);">-          stasis_message_timestamp(update->new_snapshot) :</span><br><span style="color: hsl(0, 100%, 40%);">-             stasis_message_timestamp(message);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>   for (i = 0; i < ARRAY_LEN(channel_monitors); ++i) {</span><br><span>               struct ast_json *msg;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-               msg = channel_monitors[i](old_snapshot, new_snapshot, tv);</span><br><span style="color: hsl(120, 100%, 40%);">+            msg = channel_monitors[i](update->old_snapshot, update->new_snapshot,</span><br><span style="color: hsl(120, 100%, 40%);">+                   stasis_message_timestamp(message));</span><br><span>          if (msg) {</span><br><span>                   app_send(app, msg);</span><br><span>                  ast_json_unref(msg);</span><br><span>                 }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!new_snapshot && old_snapshot) {</span><br><span style="color: hsl(0, 100%, 40%);">-            unsubscribe(app, "channel", old_snapshot->uniqueid, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_test_flag(&update->new_snapshot->flags, AST_FLAG_DEAD)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           unsubscribe(app, "channel", update->new_snapshot->uniqueid, 1);</span><br><span>      }</span><br><span> }</span><br><span> </span><br><span>@@ -987,7 +970,7 @@</span><br><span>     res |= stasis_message_router_add_cache_update(app->router,</span><br><span>                ast_bridge_snapshot_type(), sub_bridge_update_handler, app);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        res |= stasis_message_router_add_cache_update(app->router,</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= stasis_message_router_add(app->router,</span><br><span>             ast_channel_snapshot_type(), sub_channel_update_handler, app);</span><br><span> </span><br><span>   res |= stasis_message_router_add_cache_update(app->router,</span><br><span>diff --git a/res/stasis/control.c b/res/stasis/control.c</span><br><span>index e4d007c..5b3b048 100644</span><br><span>--- a/res/stasis/control.c</span><br><span>+++ b/res/stasis/control.c</span><br><span>@@ -773,22 +773,7 @@</span><br><span> struct ast_channel_snapshot *stasis_app_control_get_snapshot(</span><br><span>   const struct stasis_app_control *control)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  struct stasis_message *msg;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct ast_channel_snapshot *snapshot;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(),</span><br><span style="color: hsl(0, 100%, 40%);">-                stasis_app_control_get_channel_id(control));</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!msg) {</span><br><span style="color: hsl(0, 100%, 40%);">-             return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       snapshot = stasis_message_data(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-    ast_assert(snapshot != NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   ao2_ref(snapshot, +1);</span><br><span style="color: hsl(0, 100%, 40%);">-  ao2_ref(msg, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return snapshot;</span><br><span style="color: hsl(120, 100%, 40%);">+      return ast_channel_snapshot_get_latest(stasis_app_control_get_channel_id(control));</span><br><span> }</span><br><span> </span><br><span> static int app_send_command_on_condition(struct stasis_app_control *control,</span><br><span>diff --git a/tests/test_cel.c b/tests/test_cel.c</span><br><span>index c9ceaf9..6b28074 100644</span><br><span>--- a/tests/test_cel.c</span><br><span>+++ b/tests/test_cel.c</span><br><span>@@ -276,8 +276,7 @@</span><br><span>      ast_hangup((channel)); \</span><br><span>     HANGUP_EVENT(channel, cause, dialstatus); \</span><br><span>  APPEND_EVENT(channel, AST_CEL_CHANNEL_END, NULL, NULL); \</span><br><span style="color: hsl(0, 100%, 40%);">-       ao2_cleanup(stasis_cache_get(ast_channel_cache(), \</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_channel_snapshot_type(), ast_channel_uniqueid(channel))); \</span><br><span style="color: hsl(120, 100%, 40%);">+       ao2_cleanup(ast_channel_snapshot_get_latest(ast_channel_uniqueid(channel))); \</span><br><span>       ao2_cleanup(channel); \</span><br><span>      channel = NULL; \</span><br><span>    } while (0)</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/10478">change 10478</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/10478"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I9334febff60a82d7c39703e49059fa3a68825786 </div>
<div style="display:none"> Gerrit-Change-Number: 10478 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@digium.com> </div>