From 39400e68b6202dc40545b5ac26468fc3f7a11184 Mon Sep 17 00:00:00 2001
From: Nick Mathewson <nickm@torproject.org>
Date: Fri, 30 May 2008 16:56:34 +0000
Subject: [PATCH] Patch from Valery Kholodkov: support for edge-triggered
 events with epoll and kqueue.  Changed from original patch: made test into a
 regression test, with explicit success/failure for edge-triggered and
 non-edge-triggered cases. Closes SF request 1968284.

svn:r840
---
 ChangeLog              |  3 ++-
 epoll.c                |  6 ++++--
 include/event2/event.h | 13 +++++++++++--
 kqueue.c               |  6 +++++-
 test/Makefile.am       |  2 +-
 test/regress.c         |  6 ++++--
 test/regress.h         |  4 +++-
 7 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 16ec4595..c934f35c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -103,8 +103,9 @@ Changes in current version:
  o Fix use of freed memory in event_reinit; pointed out by Peter Postma
  o constify struct timeval * where possible
  o make event_get_supported_methods obey environment variables
+ o support for edge-triggered events on epoll and kqueue backends: patch from Valery Kholodkov
+
 
-        
 Changes in 1.4.0:
  o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr.
  o demote most http warnings to debug messages
diff --git a/epoll.c b/epoll.c
index 423d9898..0b718cc8 100644
--- a/epoll.c
+++ b/epoll.c
@@ -241,9 +241,9 @@ epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 			continue;
 
 		if (evread != NULL)
-			event_active(evread, EV_READ, 1);
+			event_active(evread, EV_READ | (evread->ev_events & EV_ET), 1);
 		if (evwrite != NULL)
-			event_active(evwrite, EV_WRITE, 1);
+			event_active(evwrite, EV_WRITE | (evwrite->ev_events & EV_ET), 1);
 	}
 
 	return (0);
@@ -283,6 +283,8 @@ epoll_add(void *arg, struct event *ev)
 		events |= EPOLLIN;
 	if (ev->ev_events & EV_WRITE)
 		events |= EPOLLOUT;
+	if(ev->ev_events & EV_ET)
+		events |= EPOLLET;
 
 	epev.data.ptr = evep;
 	epev.events = events;
diff --git a/include/event2/event.h b/include/event2/event.h
index 5702b939..4cd4608d 100644
--- a/include/event2/event.h
+++ b/include/event2/event.h
@@ -258,12 +258,16 @@ void evperiodic_assign(struct event *ev, struct event_base *base,
     const struct timeval *tv, void (*cb)(int, short, void *), void *arg);
 
 /* Flags to pass to event_set(), event_new(), event_assign(),
- * event_pending()... */
+ * event_pending(), and anything else with an argument of the form
+ * "short events" */
 #define EV_TIMEOUT	0x01
 #define EV_READ		0x02
 #define EV_WRITE	0x04
 #define EV_SIGNAL	0x08
-#define EV_PERSIST	0x10	/* Persistant event */
+/** Persistent event: won't get removed automatically when activated. */
+#define EV_PERSIST	0x10
+/** Select edge-triggered behavior, if supported by the backend. */
+#define EV_ET       0x20
 
 /**
   Define a timer event.
@@ -321,6 +325,11 @@ void evperiodic_assign(struct event *ev, struct event_base *base,
   EV_READ, or EV_WRITE.  The additional flag EV_PERSIST makes an event_add()
   persistent until event_del() has been called.
 
+  For read and write events, edge-triggered behavior can be requested
+  with the EV_ET flag.  Not all backends support edge-triggered
+  behavior.  When an edge-triggered event is activated, the EV_ET flag
+  is added to its events argument.
+
   @param ev an event struct to be modified
   @param fd the file descriptor to be monitored
   @param event desired events to monitor; can be EV_READ and/or EV_WRITE
diff --git a/kqueue.c b/kqueue.c
index b213be02..38278938 100644
--- a/kqueue.c
+++ b/kqueue.c
@@ -272,7 +272,7 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 		if (!(ev->ev_events & EV_PERSIST))
 			ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
 
-		event_active(ev, which,
+		event_active(ev, which | (ev->ev_events & EV_ET),
 		    ev->ev_events & EV_SIGNAL ? events[i].data : 1);
 	}
 
@@ -322,6 +322,8 @@ kq_add(void *arg, struct event *ev)
 		kev.flags = EV_ADD;
 		if (!(ev->ev_events & EV_PERSIST))
 			kev.flags |= EV_ONESHOT;
+		if (ev->ev_events & EV_ET)
+			kev.flags |= EV_CLEAR;
 		kev.udata = PTR_TO_UDATA(ev);
 		
 		if (kq_insert(kqop, &kev) == -1)
@@ -337,6 +339,8 @@ kq_add(void *arg, struct event *ev)
 		kev.flags = EV_ADD;
 		if (!(ev->ev_events & EV_PERSIST))
 			kev.flags |= EV_ONESHOT;
+		if (ev->ev_events & EV_ET)
+			kev.flags |= EV_CLEAR;
 		kev.udata = PTR_TO_UDATA(ev);
 		
 		if (kq_insert(kqop, &kev) == -1)
diff --git a/test/Makefile.am b/test/Makefile.am
index 2e3b7767..4f89eece 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -17,7 +17,7 @@ test_weof_LDADD = ../libevent_core.la
 test_time_SOURCES = test-time.c
 test_time_LDADD = ../libevent_core.la
 regress_SOURCES = regress.c regress.h regress_http.c regress_dns.c \
-	regress_rpc.c regress.gen.c regress.gen.h
+	regress_rpc.c regress.gen.c regress.gen.h regress_et.c
 if PTHREAD_REGRESS
 regress_SOURCES += regress_pthread.c
 endif
diff --git a/test/regress.c b/test/regress.c
index ca83e879..a6ca9b4d 100644
--- a/test/regress.c
+++ b/test/regress.c
@@ -2185,7 +2185,7 @@ main (int argc, char **argv)
 	test_evbuffer_iterative();
 	test_evbuffer_readln();
 	test_evbuffer_find();
-	
+
 	test_bufferevent();
 	test_bufferevent_watermarks();
 	test_bufferevent_filters();
@@ -2201,7 +2201,7 @@ main (int argc, char **argv)
 #if defined(HAVE_LIBZ)
 	regress_zlib();
 #endif
-	
+
 	http_suite();
 
 	rpc_suite();
@@ -2224,6 +2224,8 @@ main (int argc, char **argv)
 
 	test_simpletimeout();
 
+    test_edgetriggered();
+
 #ifndef WIN32
 	test_simplesignal();
 	test_immediatesignal();
diff --git a/test/regress.h b/test/regress.h
index 5ac6dff9..cb92c9ea 100644
--- a/test/regress.h
+++ b/test/regress.h
@@ -39,7 +39,9 @@ void rpc_suite(void);
 void dns_suite(void);
 
 void regress_pthread(void);
-void regress_zlib(void);	
+void regress_zlib(void);
+
+void test_edgetriggered(void);
 
 #ifdef __cplusplus
 }
-- 
2.40.0