[asterisk-commits] russell: branch 1.6.2 r184343 - in /branches/1.6.2: ./ apps/ channels/ includ...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 25 17:07:11 CDT 2009


Author: russell
Date: Wed Mar 25 17:07:07 2009
New Revision: 184343

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=184343
Log:
Merged revisions 184339 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/trunk

........
r184339 | russell | 2009-03-25 16:57:19 -0500 (Wed, 25 Mar 2009) | 35 lines

Improve performance of the ast_event cache functionality.

This code comes from svn/asterisk/team/russell/event_performance/.

Here is a summary of the changes that have been made, in order of both
invasiveness and performance impact, from smallest to largest.

1) Asterisk 1.6.1 introduces some additional logic to be able to handle
   distributed device state.  This functionality comes at a cost.
   One relatively minor change in this patch is that the extra processing
   required for distributed device state is now completely bypassed if
   it's not needed.

2) One of the things that I noticed when profiling this code was that a
   _lot_ of time was spent doing string comparisons.  I changed the way
   strings are represented in an event to include a hash value at the front.
   So, before doing a string comparison, we do an integer comparison on the
   hash.

3) Finally, the code that handles the event cache has been re-written.
   I tried to do this in a such a way that it had minimal impact on the API.
   I did have to change one API call, though - ast_event_queue_and_cache().
   However, the way it works now is nicer, IMO.  Each type of event that
   can be cached (MWI, device state) has its own hash table and rules for
   hashing and comparing objects.  This by far made the biggest impact on
   performance.

For additional details regarding this code and how it was tested, please see the
review request.

(closes issue #14738)
Reported by: russell

Review: http://reviewboard.digium.com/r/205/

........

Modified:
    branches/1.6.2/   (props changed)
    branches/1.6.2/apps/app_minivm.c
    branches/1.6.2/apps/app_voicemail.c
    branches/1.6.2/channels/chan_dahdi.c
    branches/1.6.2/channels/chan_iax2.c
    branches/1.6.2/channels/chan_mgcp.c
    branches/1.6.2/channels/chan_sip.c
    branches/1.6.2/channels/chan_unistim.c
    branches/1.6.2/include/asterisk/_private.h
    branches/1.6.2/include/asterisk/devicestate.h
    branches/1.6.2/include/asterisk/event.h
    branches/1.6.2/include/asterisk/strings.h
    branches/1.6.2/main/asterisk.c
    branches/1.6.2/main/devicestate.c
    branches/1.6.2/main/event.c
    branches/1.6.2/res/ais/evt.c

Propchange: branches/1.6.2/
------------------------------------------------------------------------------
--- trunk-merged (original)
+++ trunk-merged Wed Mar 25 17:07:07 2009
@@ -1,1 +1,1 @@
-/trunk:1-182359,182408,182450,182525,182530,182553,182722,182847,183028,183057,183108,183117,183242,183244,183321,183511,183560,183652,183701,183766,183865,183914,184037,184043,184079,184147,184151,184219-184220,184280
+/trunk:1-182359,182408,182450,182525,182530,182553,182722,182847,183028,183057,183108,183117,183242,183244,183321,183511,183560,183652,183701,183766,183865,183914,184037,184043,184079,184147,184151,184219-184220,184280,184339

Modified: branches/1.6.2/apps/app_minivm.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/apps/app_minivm.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/apps/app_minivm.c (original)
+++ branches/1.6.2/apps/app_minivm.c Wed Mar 25 17:07:07 2009
@@ -1785,13 +1785,10 @@
 			AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
 			AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
 			AST_EVENT_IE_END))) {
- 		return;
-	}
-
-	ast_event_queue_and_cache(event,
-		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_END);
+		return;
+	}
+
+	ast_event_queue_and_cache(event);
 }
 
 /*! \brief Send MWI using interal Asterisk event subsystem */

Modified: branches/1.6.2/apps/app_voicemail.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/apps/app_voicemail.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/apps/app_voicemail.c (original)
+++ branches/1.6.2/apps/app_voicemail.c Wed Mar 25 17:07:07 2009
@@ -6215,10 +6215,7 @@
 		return;
 	}
 
-	ast_event_queue_and_cache(event,
-		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_END);
+	ast_event_queue_and_cache(event);
 }
 
 /*!

Modified: branches/1.6.2/channels/chan_dahdi.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/channels/chan_dahdi.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/channels/chan_dahdi.c (original)
+++ branches/1.6.2/channels/chan_dahdi.c Wed Mar 25 17:07:07 2009
@@ -2959,10 +2959,7 @@
 		return;
 	}
 
-	ast_event_queue_and_cache(event,
-		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_END);
+	ast_event_queue_and_cache(event);
 
 	if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) {
 		snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot);
@@ -3016,7 +3013,6 @@
 	event = ast_event_get_cached(AST_EVENT_MWI,
 		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
 		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
-		AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
 		AST_EVENT_IE_END);
 
 	if (event) {

Modified: branches/1.6.2/channels/chan_iax2.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/channels/chan_iax2.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/channels/chan_iax2.c (original)
+++ branches/1.6.2/channels/chan_iax2.c Wed Mar 25 17:07:07 2009
@@ -7394,8 +7394,6 @@
 			event = ast_event_get_cached(AST_EVENT_MWI,
 				AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
 				AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
-				AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
-				AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
 				AST_EVENT_IE_END);
 			if (event) {
 				new = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);

Modified: branches/1.6.2/channels/chan_mgcp.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/channels/chan_mgcp.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/channels/chan_mgcp.c (original)
+++ branches/1.6.2/channels/chan_mgcp.c Wed Mar 25 17:07:07 2009
@@ -472,7 +472,6 @@
 	event = ast_event_get_cached(AST_EVENT_MWI,
 		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mbox,
 		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cntx,
-		AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
 		AST_EVENT_IE_END);
 
 	if (event) {

Modified: branches/1.6.2/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/channels/chan_sip.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/channels/chan_sip.c (original)
+++ branches/1.6.2/channels/chan_sip.c Wed Mar 25 17:07:07 2009
@@ -18054,10 +18054,7 @@
 						   AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(new),
 						   AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(old),
 						   AST_EVENT_IE_END))) {
-				ast_event_queue_and_cache(event,
-							  AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
-							  AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
-							  AST_EVENT_IE_END);
+				ast_event_queue_and_cache(event);
 			}
 		}
 
@@ -21033,8 +21030,6 @@
 		event = ast_event_get_cached(AST_EVENT_MWI,
 			AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
 			AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
-			AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
-			AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
 			AST_EVENT_IE_END);
 		if (!event)
 			continue;

Modified: branches/1.6.2/channels/chan_unistim.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/channels/chan_unistim.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/channels/chan_unistim.c (original)
+++ branches/1.6.2/channels/chan_unistim.c Wed Mar 25 17:07:07 2009
@@ -4391,7 +4391,6 @@
 	event = ast_event_get_cached(AST_EVENT_MWI,
 		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
 		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
-		AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
 		AST_EVENT_IE_END);
 
 	if (event) {

Modified: branches/1.6.2/include/asterisk/_private.h
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/include/asterisk/_private.h?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/include/asterisk/_private.h (original)
+++ branches/1.6.2/include/asterisk/_private.h Wed Mar 25 17:07:07 2009
@@ -29,7 +29,7 @@
 void dnsmgr_start_refresh(void);	/*!< Provided by dnsmgr.c */
 int dnsmgr_reload(void);		/*!< Provided by dnsmgr.c */
 void threadstorage_init(void);		/*!< Provided by threadstorage.c */
-void ast_event_init(void);		/*!< Provided by event.c */
+int ast_event_init(void);		/*!< Provided by event.c */
 int ast_device_state_engine_init(void);	/*!< Provided by devicestate.c */
 int astobj2_init(void);			/*!< Provided by astobj2.c */
 int ast_file_init(void);		/*!< Provided by file.c */

Modified: branches/1.6.2/include/asterisk/devicestate.h
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/include/asterisk/devicestate.h?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/include/asterisk/devicestate.h (original)
+++ branches/1.6.2/include/asterisk/devicestate.h Wed Mar 25 17:07:07 2009
@@ -36,6 +36,8 @@
 
 #ifndef _ASTERISK_DEVICESTATE_H
 #define _ASTERISK_DEVICESTATE_H
+
+#include "asterisk/channel.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
@@ -260,6 +262,21 @@
 	unsigned int ring:1;
 };
 
+/*!
+ * \brief Enable distributed device state processing.
+ *
+ * \details
+ * By default, Asterisk assumes that device state change events will only be
+ * originating from one instance.  If a module gets loaded and configured such
+ * that multiple instances of Asterisk will be sharing device state, this
+ * function should be called to enable distributed device state processing.
+ * It is off by default to save on unnecessary processing.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_enable_distributed_devstate(void);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: branches/1.6.2/include/asterisk/event.h
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/include/asterisk/event.h?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/include/asterisk/event.h (original)
+++ branches/1.6.2/include/asterisk/event.h Wed Mar 25 17:07:07 2009
@@ -358,42 +358,18 @@
  *
  * \param event the event to be queued and cached
  *
- * The rest of the arguments to this function specify information elements to
- * use for determining which events in the cache that this event should replace.
- * All events in the cache that match the specified criteria will be removed from
- * the cache and then this one will be added.  The arguments are specified in
- * the form:
- *
- * \code
- *    <enum ast_event_ie_type>, [enum ast_event_ie_pltype]
- * \endcode
- * and must end with AST_EVENT_IE_END.
- *
- * If the ie_type specified is *not* AST_EVENT_IE_END, then it must be followed
- * by a valid IE payload type.  If the payload type given is EXISTS, then all
- * events that contain that information element will be removed from the cache.
- * Otherwise, all events in the cache that contain an information element with
- * the same value as the new event will be removed.
- *
- * \note If more than one IE parameter is specified, they *all* must match for
- *       the event to be removed from the cache.
- *
- * Example usage:
- *
- * \code
- * ast_event_queue_and_cache(event,
- *     AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
- *     AST_EVENT_IE_END);
- * \endcode
- *
- * This example queues and caches an event.  Any events in the cache that have
- * the same MAILBOX information element as this event will be removed.
- *
+ * \details
  * The purpose of caching events is so that the core can retain the last known
  * information for events that represent some sort of state.  That way, when
  * code needs to find out the current state, it can query the cache.
- */
-int ast_event_queue_and_cache(struct ast_event *event, ...);
+ *
+ * The event API already knows which events can be cached and how to cache them.
+ *
+ * \retval 0 success
+ * \retval non-zero failure.  If failure is returned, the event must be destroyed
+ *         by the caller of this function.
+ */
+int ast_event_queue_and_cache(struct ast_event *event);
 
 /*!
  * \brief Retrieve an event from the cache
@@ -511,6 +487,18 @@
 const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type);
 
 /*!
+ * \brief Get the hash for the string payload of an IE
+ *
+ * \param event The event to get the IE from
+ * \param ie_type the type of information element to retrieve the hash for
+ *
+ * \return This function returns the hash value as calculated by ast_str_hash()
+ *         for the string payload.  This is stored in the event to avoid
+ *         unnecessary string comparisons.
+ */
+uint32_t ast_event_get_ie_str_hash(const struct ast_event *event, enum ast_event_ie_type ie_type);
+
+/*!
  * \brief Get the value of an information element that has a raw payload
  *
  * \param event The event to get the IE from

Modified: branches/1.6.2/include/asterisk/strings.h
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/include/asterisk/strings.h?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/include/asterisk/strings.h (original)
+++ branches/1.6.2/include/asterisk/strings.h Wed Mar 25 17:07:07 2009
@@ -852,6 +852,29 @@
 }
 
 /*!
+ * \brief Compute a hash value on a string
+ *
+ * \param[in] str The string to add to the hash
+ * \param[in] hash The hash value to add to
+ * 
+ * \details
+ * This version of the function is for when you need to compute a
+ * string hash of more than one string.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
+ */
+static force_inline int ast_str_hash_add(const char *str, int hash)
+{
+	while (*str)
+		hash = hash * 33 ^ *str++;
+
+	return abs(hash);
+}
+
+/*!
  * \brief Compute a hash value on a case-insensitive string
  *
  * Uses the same hash algorithm as ast_str_hash, but converts

Modified: branches/1.6.2/main/asterisk.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/main/asterisk.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/main/asterisk.c (original)
+++ branches/1.6.2/main/asterisk.c Wed Mar 25 17:07:07 2009
@@ -3509,7 +3509,10 @@
 	}
 #endif
 
-	ast_event_init();
+	if (ast_event_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
 
 	ast_makesocket();
 	sigemptyset(&sigs);

Modified: branches/1.6.2/main/devicestate.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/main/devicestate.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/main/devicestate.c (original)
+++ branches/1.6.2/main/devicestate.c Wed Mar 25 17:07:07 2009
@@ -196,8 +196,10 @@
 	ast_cond_t cond;
 	ast_mutex_t lock;
 	AST_LIST_HEAD_NOLOCK(, devstate_change) devstate_change_q;
+	unsigned int enabled:1;
 } devstate_collector = {
 	.thread = AST_PTHREADT_NULL,
+	.enabled = 0,
 };
 
 /* Forward declarations */
@@ -428,22 +430,26 @@
 static void devstate_event(const char *device, enum ast_device_state state)
 {
 	struct ast_event *event;
+	enum ast_event_type event_type;
+
+	if (devstate_collector.enabled) {
+		/* Distributed device state is enabled, so this state change is a change
+		 * for a single server, not the real state. */
+		event_type = AST_EVENT_DEVICE_STATE_CHANGE;
+	} else {
+		event_type = AST_EVENT_DEVICE_STATE;
+	}
 
 	ast_debug(3, "device '%s' state '%d'\n", device, state);
 
-	if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
+	if (!(event = ast_event_new(event_type,
 			AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
 			AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
 			AST_EVENT_IE_END))) {
 		return;
 	}
 
-	/* Cache this event, replacing an event in the cache with the same
-	 * device name if it exists. */
-	ast_event_queue_and_cache(event,
-		AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, sizeof(struct ast_eid),
-		AST_EVENT_IE_END);
+	ast_event_queue_and_cache(event);
 }
 
 /*! Called by the state change thread to find out what the state is, and then
@@ -632,13 +638,12 @@
 		AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
 		AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
 		AST_EVENT_IE_END);
-	
-	if (!event)
+
+	if (!event) {
 		return;
-
-	ast_event_queue_and_cache(event,
-		AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
-		AST_EVENT_IE_END);
+	}
+
+	ast_event_queue_and_cache(event);
 }
 
 static void handle_devstate_change(struct devstate_change *sc)
@@ -719,21 +724,6 @@
 /*! \brief Initialize the device state engine in separate thread */
 int ast_device_state_engine_init(void)
 {
-	devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
-		devstate_change_collector_cb, NULL, AST_EVENT_IE_END);
-
-	if (!devstate_collector.event_sub) {
-		ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n");
-		return -1;
-	}
-
-	ast_mutex_init(&devstate_collector.lock);
-	ast_cond_init(&devstate_collector.cond, NULL);
-	if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) {
-		ast_log(LOG_ERROR, "Unable to start device state collector thread.\n");
-		return -1;
-	}
-
 	ast_cond_init(&change_pending, NULL);
 	if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
 		ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
@@ -830,3 +820,28 @@
 	return AST_DEVICE_NOT_INUSE;
 }
 
+int ast_enable_distributed_devstate(void)
+{
+	if (devstate_collector.enabled) {
+		return 0;
+	}
+
+	devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
+		devstate_change_collector_cb, NULL, AST_EVENT_IE_END);
+
+	if (!devstate_collector.event_sub) {
+		ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n");
+		return -1;
+	}
+
+	ast_mutex_init(&devstate_collector.lock);
+	ast_cond_init(&devstate_collector.cond, NULL);
+	if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) {
+		ast_log(LOG_ERROR, "Unable to start device state collector thread.\n");
+		return -1;
+	}
+
+	devstate_collector.enabled = 1;
+
+	return 0;
+}

Modified: branches/1.6.2/main/event.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.6.2/main/event.c?view=diff&rev=184343&r1=184342&r2=184343
==============================================================================
--- branches/1.6.2/main/event.c (original)
+++ branches/1.6.2/main/event.c Wed Mar 25 17:07:07 2009
@@ -28,6 +28,7 @@
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/_private.h"
+
 #include "asterisk/event.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/dlinkedlists.h"
@@ -36,6 +37,7 @@
 #include "asterisk/unaligned.h"
 #include "asterisk/utils.h"
 #include "asterisk/taskprocessor.h"
+#include "asterisk/astobj2.h"
 
 struct ast_taskprocessor *event_dispatcher;
 
@@ -52,6 +54,16 @@
 	/*! Total length of the IE payload */
 	uint16_t ie_payload_len;
 	unsigned char ie_payload[0];
+} __attribute__((packed));
+
+/*!
+ * \brief The payload for a string information element
+ */
+struct ast_event_ie_str_payload {
+	/*! \brief A hash calculated with ast_str_hash(), to speed up comparisons */
+	uint32_t hash;
+	/*! \brief The actual string, null terminated */
+	char str[1];
 } __attribute__((packed));
 
 /*!
@@ -85,7 +97,10 @@
 	enum ast_event_ie_pltype ie_pltype;
 	union {
 		uint32_t uint;
-		const char *str;
+		struct {
+			uint32_t hash;
+			const char *str;
+		};
 		void *raw;
 	} payload;
 	size_t raw_datalen;
@@ -107,11 +122,55 @@
  * The event subscribers are indexed by which event they are subscribed to */
 static AST_RWDLLIST_HEAD(ast_event_sub_list, ast_event_sub) ast_event_subs[AST_EVENT_TOTAL];
 
-/*! \brief Cached events
- * The event cache is indexed on the event type.  The purpose of this is 
- * for events that express some sort of state.  So, when someone first
- * needs to know this state, it can get the last known state from the cache. */
-static AST_RWLIST_HEAD(ast_event_ref_list, ast_event_ref) ast_event_cache[AST_EVENT_TOTAL];
+static int ast_event_cmp(void *obj, void *arg, int flags);
+static int ast_event_hash_mwi(const void *obj, const int flags);
+static int ast_event_hash_devstate(const void *obj, const int flags);
+static int ast_event_hash_devstate_change(const void *obj, const int flags);
+
+#ifdef LOW_MEMORY
+#define NUM_CACHE_BUCKETS 17
+#else
+#define NUM_CACHE_BUCKETS 563
+#endif
+
+#define MAX_CACHE_ARGS 8
+
+/*!
+ * \brief Event types that are kept in the cache.
+ */
+static struct {
+	/*! 
+	 * \brief Container of cached events
+	 *
+	 * \details This gets allocated in ast_event_init() when Asterisk starts
+	 * for the event types declared as using the cache.
+	 */
+	struct ao2_container *container;
+	/*! \brief Event type specific hash function */
+	ao2_hash_fn *hash_fn;
+	/*!
+	 * \brief Information Elements used for caching
+	 *
+	 * \details This array is the set of information elements that will be unique
+	 * among all events in the cache for this event type.  When a new event gets
+	 * cached, a previous event with the same values for these information elements
+	 * will be replaced.
+	 */
+	enum ast_event_ie_type cache_args[MAX_CACHE_ARGS];
+} ast_event_cache[AST_EVENT_TOTAL] = {
+	[AST_EVENT_MWI] = {
+		.hash_fn = ast_event_hash_mwi,
+		.cache_args = { AST_EVENT_IE_MAILBOX, AST_EVENT_IE_CONTEXT },
+	},
+	[AST_EVENT_DEVICE_STATE] = {
+		.hash_fn = ast_event_hash_devstate,
+		.cache_args = { AST_EVENT_IE_DEVICE, },
+	},
+	[AST_EVENT_DEVICE_STATE_CHANGE] = {
+		.hash_fn = ast_event_hash_devstate_change,
+		.cache_args = { AST_EVENT_IE_DEVICE, AST_EVENT_IE_EID, },
+	},
+};
 
 /*!
  * The index of each entry _must_ match the event type number!
@@ -237,6 +296,8 @@
 {
 	switch (ie_val->ie_pltype) {
 	case AST_EVENT_IE_PLTYPE_STR:
+		ast_free((char *) ie_val->payload.str);
+		break;
 	case AST_EVENT_IE_PLTYPE_RAW:
 		ast_free(ie_val->payload.raw);
 		break;
@@ -328,7 +389,8 @@
 	return res;
 }
 
-static int match_ie_val(struct ast_event *event, struct ast_event_ie_val *ie_val, struct ast_event *event2)
+static int match_ie_val(const struct ast_event *event,
+		const struct ast_event_ie_val *ie_val, const struct ast_event *event2)
 {
 	if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT) {
 		uint32_t val = event2 ? ast_event_get_ie_uint(event2, ie_val->ie_type) : ie_val->payload.uint;
@@ -338,9 +400,19 @@
 	}
 
 	if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR) {
-		const char *str = event2 ? ast_event_get_ie_str(event2, ie_val->ie_type) : ie_val->payload.str;
-		if (str && !strcmp(str, ast_event_get_ie_str(event, ie_val->ie_type)))
+		const char *str;
+		uint32_t hash;
+
+		hash = event2 ? ast_event_get_ie_str_hash(event2, ie_val->ie_type) : ie_val->payload.hash;
+		if (hash != ast_event_get_ie_str_hash(event, ie_val->ie_type)) {
+			return 0;
+		}
+
+		str = event2 ? ast_event_get_ie_str(event2, ie_val->ie_type) : ie_val->payload.str;
+		if (str && !strcmp(str, ast_event_get_ie_str(event, ie_val->ie_type))) {
 			return 1;
+		}
+
 		return 0;
 	}
 
@@ -360,26 +432,32 @@
 	return 0;
 }
 
+static int dump_cache_cb(void *obj, void *arg, int flags)
+{
+	const struct ast_event_ref *event_ref = obj;
+	const struct ast_event *event = event_ref->event;
+	const struct ast_event_sub *event_sub = arg;
+	struct ast_event_ie_val *ie_val = NULL;
+
+	AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) {
+		if (!match_ie_val(event, ie_val, NULL)) {
+			break;
+		}
+	}
+
+	if (!ie_val) {
+		/* All parameters were matched on this cache entry, so dump it */
+		event_sub->cb(event, event_sub->userdata);
+	}
+
+	return 0;
+}
+
 /*! \brief Dump the event cache for the subscribed event type */
 void ast_event_dump_cache(const struct ast_event_sub *event_sub)
 {
-	struct ast_event_ref *event_ref;
-	enum ast_event_type type = event_sub->type;
-
-	AST_RWLIST_RDLOCK(&ast_event_cache[type]);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) {
-		struct ast_event_ie_val *ie_val;
-		AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) {
-			if (!match_ie_val(event_ref->event, ie_val, NULL))
-				break;
-		}
-		if (!ie_val) {
-			/* All parameters were matched on this cache entry, so dump it */
-			event_sub->cb(event_ref->event, event_sub->userdata);
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END
-	AST_RWLIST_UNLOCK(&ast_event_cache[type]);
+	ao2_callback(ast_event_cache[event_sub->type].container, OBJ_NODATA,
+			dump_cache_cb, (void *) event_sub);
 }
 
 static struct ast_event *gen_sub_event(struct ast_event_sub *sub)
@@ -535,6 +613,8 @@
 		ast_free(ie_val);
 		return -1;
 	}
+
+	ie_val->payload.hash = ast_str_hash(str);
 
 	AST_LIST_INSERT_TAIL(&sub->ie_vals, ie_val, entry);
 
@@ -703,7 +783,11 @@
 
 const char *ast_event_iterator_get_ie_str(struct ast_event_iterator *iterator)
 {
-	return (const char*)iterator->ie->ie_payload;
+	const struct ast_event_ie_str_payload *str_payload;
+
+	str_payload = (struct ast_event_ie_str_payload *) iterator->ie->ie_payload;
+
+	return str_payload->str;
 }
 
 void *ast_event_iterator_get_ie_raw(struct ast_event_iterator *iterator)
@@ -725,9 +809,22 @@
 	return ie_val ? ntohl(get_unaligned_uint32(ie_val)) : 0;
 }
 
+uint32_t ast_event_get_ie_str_hash(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+	const struct ast_event_ie_str_payload *str_payload;
+
+	str_payload = ast_event_get_ie_raw(event, ie_type);
+
+	return str_payload->hash;
+}
+
 const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type)
 {
-	return ast_event_get_ie_raw(event, ie_type);
+	const struct ast_event_ie_str_payload *str_payload;
+
+	str_payload = ast_event_get_ie_raw(event, ie_type);
+
+	return str_payload->str;
 }
 
 const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type)
@@ -746,7 +843,16 @@
 int ast_event_append_ie_str(struct ast_event **event, enum ast_event_ie_type ie_type,
 	const char *str)
 {
-	return ast_event_append_ie_raw(event, ie_type, str, strlen(str) + 1);
+	struct ast_event_ie_str_payload *str_payload;
+	size_t payload_len;
+
+	payload_len = sizeof(*str_payload) + strlen(str);
+	str_payload = alloca(payload_len);
+
+	strcpy(str_payload->str, str);
+	str_payload->hash = ast_str_hash(str);
+
+	return ast_event_append_ie_raw(event, ie_type, str_payload, payload_len);
 }
 
 int ast_event_append_ie_uint(struct ast_event **event, enum ast_event_ie_type ie_type,
@@ -850,10 +956,11 @@
 	ast_free(event);
 }
 
-static void ast_event_ref_destroy(struct ast_event_ref *event_ref)
-{
+static void ast_event_ref_destroy(void *obj)
+{
+	struct ast_event_ref *event_ref = obj;
+
 	ast_event_destroy(event_ref->event);
-	ast_free(event_ref);
 }
 
 static struct ast_event *ast_event_dup(const struct ast_event *event)
@@ -863,9 +970,10 @@
 
 	event_len = ast_event_get_size(event);
 
-	if (!(dup_event = ast_calloc(1, event_len)))
+	if (!(dup_event = ast_calloc(1, event_len))) {
 		return NULL;
-	
+	}
+
 	memcpy(dup_event, event, event_len);
 
 	return dup_event;
@@ -876,12 +984,24 @@
 	va_list ap;
 	enum ast_event_ie_type ie_type;
 	struct ast_event *dup_event = NULL;
-	struct ast_event_ref *event_ref;
-	struct ast_event_ie_val *cache_arg;
-	AST_LIST_HEAD_NOLOCK_STATIC(cache_args, ast_event_ie_val);
+	struct ast_event_ref *cached_event_ref;
+	struct ast_event *cache_arg_event;
+	struct ast_event_ref tmp_event_ref = {
+		.event = NULL,
+	};
+	struct ao2_container *container = NULL;
 
 	if (type >= AST_EVENT_TOTAL) {
 		ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+		return NULL;
+	}
+
+	if (!(container = ast_event_cache[type].container)) {
+		ast_log(LOG_ERROR, "%u is not a cached event type\n", type);
+		return NULL;
+	}
+
+	if (!(cache_arg_event = ast_event_new(type, AST_EVENT_IE_END))) {
 		return NULL;
 	}
 
@@ -890,125 +1010,96 @@
 		ie_type != AST_EVENT_IE_END;
 		ie_type = va_arg(ap, enum ast_event_type))
 	{
-		cache_arg = alloca(sizeof(*cache_arg));
-		memset(cache_arg, 0, sizeof(*cache_arg));
-		cache_arg->ie_type = ie_type;
-		cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
-		if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
-			cache_arg->payload.uint = va_arg(ap, uint32_t);
-		else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
-			cache_arg->payload.str = ast_strdupa(va_arg(ap, const char *));
-		else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_RAW) {
+		enum ast_event_ie_pltype ie_pltype;
+
+		ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+
+		switch (ie_pltype) {
+		case AST_EVENT_IE_PLTYPE_UINT:
+			ast_event_append_ie_uint(&cache_arg_event, ie_type, va_arg(ap, uint32_t));
+			break;
+		case AST_EVENT_IE_PLTYPE_STR:
+			ast_event_append_ie_str(&cache_arg_event, ie_type, va_arg(ap, const char *));
+			break;
+		case AST_EVENT_IE_PLTYPE_RAW:
+		{
 			void *data = va_arg(ap, void *);
 			size_t datalen = va_arg(ap, size_t);
-			cache_arg->payload.raw = alloca(datalen);
-			memcpy(cache_arg->payload.raw, data, datalen);
-			cache_arg->raw_datalen = datalen;
-		}
-		AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
+			ast_event_append_ie_raw(&cache_arg_event, ie_type, data, datalen);
+		}
+		case AST_EVENT_IE_PLTYPE_EXISTS:
+			ast_log(LOG_WARNING, "PLTYPE_EXISTS not supported by this function\n");
+			break;
+		case AST_EVENT_IE_PLTYPE_UNKNOWN:
+			break;
+		}
 	}
 	va_end(ap);
 
-	if (AST_LIST_EMPTY(&cache_args)) {
-		ast_log(LOG_ERROR, "Events can not be retrieved from the cache without "
-			"specifying at least one IE type!\n");
-		return NULL;
-	}
-
-	AST_RWLIST_RDLOCK(&ast_event_cache[type]);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) {
-		AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
-			if (!match_ie_val(event_ref->event, cache_arg, NULL))
-				break;	
-		}
-		if (!cache_arg) {
-			/* All parameters were matched on this cache entry, so return it */
-			dup_event = ast_event_dup(event_ref->event);
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END
-	AST_RWLIST_UNLOCK(&ast_event_cache[type]);
+	tmp_event_ref.event = cache_arg_event;
+
+	cached_event_ref = ao2_find(container, &tmp_event_ref, OBJ_POINTER);
+
+	ast_event_destroy(cache_arg_event);
+	cache_arg_event = NULL;
+
+	if (cached_event_ref) {
+		dup_event = ast_event_dup(cached_event_ref->event);
+		ao2_ref(cached_event_ref, -1);
+		cached_event_ref = NULL;
+	}
 
 	return dup_event;
+}
+
+static struct ast_event_ref *alloc_event_ref(void)
+{
+	return ao2_alloc(sizeof(struct ast_event_ref), ast_event_ref_destroy);
 }
 
 /*! \brief Duplicate an event and add it to the cache
  * \note This assumes this index in to the cache is locked */
-static int ast_event_dup_and_cache(const struct ast_event *event)
+static int attribute_unused ast_event_dup_and_cache(const struct ast_event *event)
 {
 	struct ast_event *dup_event;
 	struct ast_event_ref *event_ref;
 
-	if (!(dup_event = ast_event_dup(event)))
-		return -1;
-	if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
-		return -1;
-	
+	if (!(dup_event = ast_event_dup(event))) {
+		return -1;
+	}
+
+	if (!(event_ref = alloc_event_ref())) {
+		ast_event_destroy(dup_event);
+		return -1;
+	}
+
 	event_ref->event = dup_event;
 
-	AST_LIST_INSERT_TAIL(&ast_event_cache[ntohs(event->type)], event_ref, entry);
+	ao2_link(ast_event_cache[ast_event_get_type(event)].container, event_ref);
+
+	ao2_ref(event_ref, -1);
 
 	return 0;
 }
 
-int ast_event_queue_and_cache(struct ast_event *event, ...)
-{
-	va_list ap;
-	enum ast_event_type ie_type;
-	uint16_t host_event_type;
-	struct ast_event_ref *event_ref;
-	int res;
-	struct ast_event_ie_val *cache_arg;
-	AST_LIST_HEAD_NOLOCK_STATIC(cache_args, ast_event_ie_val);
-
-	host_event_type = ntohs(event->type);
-
-	/* Invalid type */
-	if (host_event_type >= AST_EVENT_TOTAL) {
-		ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
-			"type '%d'!\n", host_event_type);
-		return -1;
-	}
-
-	va_start(ap, event);
-	for (ie_type = va_arg(ap, enum ast_event_type);
-		ie_type != AST_EVENT_IE_END;
-		ie_type = va_arg(ap, enum ast_event_type))
-	{
-		cache_arg = alloca(sizeof(*cache_arg));
-		memset(cache_arg, 0, sizeof(*cache_arg));
-		cache_arg->ie_type = ie_type;
-		cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
-		if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_RAW)
-			cache_arg->raw_datalen = va_arg(ap, size_t);
-		AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
-	}
-	va_end(ap);
-
-	if (AST_LIST_EMPTY(&cache_args)) {
-		ast_log(LOG_ERROR, "Events can not be cached without specifying at "
-			"least one IE type!\n");
-		return ast_event_queue(event);
-	}
- 
-	AST_RWLIST_WRLOCK(&ast_event_cache[host_event_type]);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[host_event_type], event_ref, entry) {
-		AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
-			if (!match_ie_val(event_ref->event, cache_arg, event))
-				break;	
-		}
-		if (!cache_arg) {
-			/* All parameters were matched on this cache entry, so remove it */
-			AST_LIST_REMOVE_CURRENT(entry);
-			ast_event_ref_destroy(event_ref);
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END;
-	res = ast_event_dup_and_cache(event);
-	AST_RWLIST_UNLOCK(&ast_event_cache[host_event_type]);
-
-	return (ast_event_queue(event) || res) ? -1 : 0;
+int ast_event_queue_and_cache(struct ast_event *event)
+{
+	struct ao2_container *container;
+	struct ast_event_ref tmp_event_ref = {
+		.event = event,
+	};
+
+	if (!(container = ast_event_cache[ast_event_get_type(event)].container)) {
+		ast_log(LOG_WARNING, "cache requested for non-cached event type\n");
+		goto queue_event;
+	}
+
+	/* Remove matches from the cache */
+	ao2_callback(container, OBJ_POINTER | OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA,
+			ast_event_cmp, &tmp_event_ref);
+
+queue_event:
+	return ast_event_queue(event);
 }
 
 static int handle_event(void *data)
@@ -1024,22 +1115,25 @@
 	AST_RWDLLIST_TRAVERSE(&ast_event_subs[host_event_type], sub, entry) {
 		struct ast_event_ie_val *ie_val;
 		AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
-			if (!match_ie_val(event_ref->event, ie_val, NULL))
+			if (!match_ie_val(event_ref->event, ie_val, NULL)) {
 				break;
-		}
-		if (ie_val)
+			}
+		}
+		if (ie_val) {
 			continue;
+		}
 		sub->cb(event_ref->event, sub->userdata);
 	}
 	AST_RWDLLIST_UNLOCK(&ast_event_subs[host_event_type]);
 
 	/* Now to subscribers to all event types */
 	AST_RWDLLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
-	AST_RWDLLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry)
+	AST_RWDLLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry) {
 		sub->cb(event_ref->event, sub->userdata);
+	}
 	AST_RWDLLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
 
-	ast_event_ref_destroy(event_ref);
+	ao2_ref(event_ref, -1);
 
 	return 0;
 }
@@ -1059,29 +1153,149 @@
 	}
 
 	/* If nobody has subscribed to this event type, throw it away now */
-	if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END) 
-		== AST_EVENT_SUB_NONE) {
+	if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END)
+			== AST_EVENT_SUB_NONE) {
 		ast_event_destroy(event);
 		return 0;
 	}
 
-	if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
-		return -1;
+	if (!(event_ref = alloc_event_ref())) {
+		return -1;
+	}
 
 	event_ref->event = event;
 
 	return ast_taskprocessor_push(event_dispatcher, handle_event, event_ref);
 }
 
-void ast_event_init(void)
-{
+static int ast_event_hash_mwi(const void *obj, const int flags)
+{
+	const struct ast_event *event = obj;
+	const char *mailbox = ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX);
+	const char *context = ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT);
+
+	return ast_str_hash_add(context, ast_str_hash(mailbox));
+}
+
+/*!
+ * \internal
+ * \brief Hash function for AST_EVENT_DEVICE_STATE
+ *
+ * \param[in] obj an ast_event
+ * \param[in] flags unused
+ *
+ * \return hash value
+ */
+static int ast_event_hash_devstate(const void *obj, const int flags)
+{
+	const struct ast_event *event = obj;
+
+	return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE));
+}
+
+/*!
+ * \internal
+ * \brief Hash function for AST_EVENT_DEVICE_STATE_CHANGE
+ *
+ * \param[in] obj an ast_event
+ * \param[in] flags unused
+ *
+ * \return hash value
+ */
+static int ast_event_hash_devstate_change(const void *obj, const int flags)
+{
+	const struct ast_event *event = obj;
+
+	return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE));
+}
+
+static int ast_event_hash(const void *obj, const int flags)
+{
+	const struct ast_event_ref *event_ref;
+	const struct ast_event *event;
+	ao2_hash_fn *hash_fn;
+
+	event_ref = obj;
+	event = event_ref->event;
+
+	if (!(hash_fn = ast_event_cache[ast_event_get_type(event)].hash_fn)) {
+		return 0;
+	}
+
+	return hash_fn(event, flags);
+}
+
+/*!
+ * \internal
+ * \brief Compare two events
+ *
+ * \param[in] obj the first event, as an ast_event_ref
+ * \param[in] arg the second event, as an ast_event_ref
+ * \param[in] flags unused
+ *
+ * \pre Both events must be the same type.
+ * \pre The event type must be declared as a cached event type in ast_event_cache
+ *
+ * \details This function takes two events, and determines if they are considered
+ * equivalent.  The values of information elements specified in the cache arguments
+ * for the event type are used to determine if the events are equivalent.
+ *
+ * \retval 0 No match
+ * \retval CMP_MATCH The events are considered equivalent based on the cache arguments
+ */
+static int ast_event_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_event_ref *event_ref, *event_ref2;
+	struct ast_event *event, *event2;
+	int res = CMP_MATCH;
 	int i;
-
-	for (i = 0; i < AST_EVENT_TOTAL; i++)
+	enum ast_event_ie_type *cache_args;
+
+	event_ref = obj;
+	event = event_ref->event;
+
+	event_ref2 = arg;
+	event2 = event_ref2->event;
+
+	cache_args = ast_event_cache[ast_event_get_type(event)].cache_args;
+
+	for (i = 0; i < ARRAY_LEN(ast_event_cache[0].cache_args) && cache_args[i]; i++) {
+		struct ast_event_ie_val ie_val = {
+			.ie_type = cache_args[i],
+		};
+
+		if (!match_ie_val(event, &ie_val, event2)) {
+			res = 0;
+			break;
+		}
+	}
+
+	return res;
+}
+
+int ast_event_init(void)
+{
+	int i;
+
+	for (i = 0; i < AST_EVENT_TOTAL; i++) {
 		AST_RWDLLIST_HEAD_INIT(&ast_event_subs[i]);
-
-	for (i = 0; i < AST_EVENT_TOTAL; i++)
-		AST_RWLIST_HEAD_INIT(&ast_event_cache[i]);
-
-	event_dispatcher = ast_taskprocessor_get("core_event_dispatcher", 0);
-}
+	}
+

[... 105 lines stripped ...]



More information about the asterisk-commits mailing list