[svn-commits] russell: branch russell/event_performance_trunk r184327 - in /team/russell/ev...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Mar 25 15:52:25 CDT 2009


Author: russell
Date: Wed Mar 25 15:52:14 2009
New Revision: 184327

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=184327
Log:
Look at that, ZOMBIES!  I mean, it compiles.

Modified:
    team/russell/event_performance_trunk/apps/app_minivm.c
    team/russell/event_performance_trunk/apps/app_voicemail.c
    team/russell/event_performance_trunk/channels/chan_dahdi.c
    team/russell/event_performance_trunk/channels/chan_iax2.c
    team/russell/event_performance_trunk/channels/chan_mgcp.c
    team/russell/event_performance_trunk/channels/chan_sip.c
    team/russell/event_performance_trunk/channels/chan_unistim.c
    team/russell/event_performance_trunk/include/asterisk/_private.h
    team/russell/event_performance_trunk/include/asterisk/devicestate.h
    team/russell/event_performance_trunk/include/asterisk/event.h
    team/russell/event_performance_trunk/include/asterisk/strings.h
    team/russell/event_performance_trunk/main/asterisk.c
    team/russell/event_performance_trunk/main/devicestate.c
    team/russell/event_performance_trunk/main/event.c
    team/russell/event_performance_trunk/res/ais/evt.c

Modified: team/russell/event_performance_trunk/apps/app_minivm.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/apps/app_minivm.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/apps/app_minivm.c (original)
+++ team/russell/event_performance_trunk/apps/app_minivm.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/apps/app_voicemail.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/apps/app_voicemail.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/apps/app_voicemail.c (original)
+++ team/russell/event_performance_trunk/apps/app_voicemail.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/channels/chan_dahdi.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/channels/chan_dahdi.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/channels/chan_dahdi.c (original)
+++ team/russell/event_performance_trunk/channels/chan_dahdi.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/channels/chan_iax2.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/channels/chan_iax2.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/channels/chan_iax2.c (original)
+++ team/russell/event_performance_trunk/channels/chan_iax2.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/channels/chan_mgcp.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/channels/chan_mgcp.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/channels/chan_mgcp.c (original)
+++ team/russell/event_performance_trunk/channels/chan_mgcp.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/channels/chan_sip.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/channels/chan_sip.c (original)
+++ team/russell/event_performance_trunk/channels/chan_sip.c Wed Mar 25 15:52:14 2009
@@ -18059,10 +18059,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);
 			}
 		}
 
@@ -21038,8 +21035,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: team/russell/event_performance_trunk/channels/chan_unistim.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/channels/chan_unistim.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/channels/chan_unistim.c (original)
+++ team/russell/event_performance_trunk/channels/chan_unistim.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/include/asterisk/_private.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/include/asterisk/_private.h?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/include/asterisk/_private.h (original)
+++ team/russell/event_performance_trunk/include/asterisk/_private.h Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/include/asterisk/devicestate.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/include/asterisk/devicestate.h?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/include/asterisk/devicestate.h (original)
+++ team/russell/event_performance_trunk/include/asterisk/devicestate.h Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/include/asterisk/event.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/include/asterisk/event.h?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/include/asterisk/event.h (original)
+++ team/russell/event_performance_trunk/include/asterisk/event.h Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/include/asterisk/strings.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/include/asterisk/strings.h?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/include/asterisk/strings.h (original)
+++ team/russell/event_performance_trunk/include/asterisk/strings.h Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/main/asterisk.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/main/asterisk.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/main/asterisk.c (original)
+++ team/russell/event_performance_trunk/main/asterisk.c Wed Mar 25 15:52:14 2009
@@ -3509,7 +3509,10 @@
 	}
 #endif
 
-	ast_event_init();
+	if (ast_event_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
 
 	ast_makesocket();
 	sigemptyset(&sigs);

Modified: team/russell/event_performance_trunk/main/devicestate.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/main/devicestate.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/main/devicestate.c (original)
+++ team/russell/event_performance_trunk/main/devicestate.c Wed Mar 25 15:52:14 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: team/russell/event_performance_trunk/main/event.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/main/event.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/main/event.c (original)
+++ team/russell/event_performance_trunk/main/event.c Wed Mar 25 15:52:14 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);
-}
+	}
+
+	for (i = 0; i < AST_EVENT_TOTAL; i++) {
+		if (!ast_event_cache[i].hash_fn) {
+			/* This event type is not cached. */
+			continue;
+		}
+
+		if (!(ast_event_cache[i].container = ao2_container_alloc(NUM_CACHE_BUCKETS,
+				ast_event_hash, ast_event_cmp))) {
+			return -1;
+		}
+	}
+
+	if (!(event_dispatcher = ast_taskprocessor_get("core_event_dispatcher", 0))) {
+		return -1;
+	}
+
+	return 0;
+}

Modified: team/russell/event_performance_trunk/res/ais/evt.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/event_performance_trunk/res/ais/evt.c?view=diff&rev=184327&r1=184326&r2=184327
==============================================================================
--- team/russell/event_performance_trunk/res/ais/evt.c (original)

[... 82 lines stripped ...]



More information about the svn-commits mailing list