summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am10
-rw-r--r--Makefile.nmake2
-rw-r--r--WIN32-Code/evconfig-private.h6
-rw-r--r--WIN32-Code/event2/event-config.h8
-rw-r--r--arc4random.c11
-rw-r--r--buffer.c947
-rw-r--r--buffer_iocp.c5
-rw-r--r--bufferevent-internal.h17
-rw-r--r--bufferevent.c9
-rw-r--r--bufferevent_async.c3
-rw-r--r--bufferevent_filter.c4
-rw-r--r--bufferevent_openssl.c44
-rw-r--r--bufferevent_pair.c6
-rw-r--r--bufferevent_ratelim.c96
-rw-r--r--bufferevent_sock.c17
-rw-r--r--compat/sys/queue.h2
-rw-r--r--configure.in155
-rw-r--r--defer-internal.h6
-rw-r--r--devpoll.c1
-rw-r--r--epoll.c393
-rw-r--r--epoll_sub.c1
-rw-r--r--evbuffer-internal.h80
-rw-r--r--evconfig-private.h.in48
-rw-r--r--evdns.c14
-rw-r--r--event-internal.h13
-rw-r--r--event.c586
-rw-r--r--event.h2
-rw-r--r--event_iocp.c3
-rw-r--r--event_tagging.c5
-rw-r--r--evmap-internal.h3
-rw-r--r--evmap.c129
-rw-r--r--evport.c198
-rw-r--r--evrpc.c13
-rw-r--r--evthread-internal.h6
-rw-r--r--evthread.c14
-rw-r--r--evthread_pthread.c6
-rw-r--r--evthread_win32.c3
-rw-r--r--evutil.c289
-rw-r--r--evutil_rand.c1
-rw-r--r--http-internal.h9
-rw-r--r--http.c335
-rw-r--r--include/event2/buffer.h176
-rw-r--r--include/event2/bufferevent.h24
-rw-r--r--include/event2/bufferevent_ssl.h20
-rw-r--r--include/event2/dns.h2
-rw-r--r--include/event2/dns_compat.h2
-rw-r--r--include/event2/event.h79
-rw-r--r--include/event2/event_struct.h22
-rw-r--r--include/event2/http.h91
-rw-r--r--include/event2/listener.h3
-rw-r--r--include/event2/rpc.h2
-rw-r--r--include/event2/thread.h2
-rw-r--r--include/event2/util.h29
-rw-r--r--iocp-internal.h2
-rw-r--r--ipv6-internal.h4
-rw-r--r--kqueue.c19
-rw-r--r--listener.c20
-rw-r--r--log-internal.h27
-rw-r--r--log.c22
-rw-r--r--m4/ac_backport_259_ssizet.m43
-rw-r--r--m4/libevent_openssl.m447
-rw-r--r--m4/ntp_pkg_config.m427
-rwxr-xr-xmake_epoll_table.py57
-rw-r--r--minheap-internal.h41
-rw-r--r--mm-internal.h28
-rw-r--r--poll.c1
-rw-r--r--sample/Makefile.am1
-rw-r--r--sample/dns-example.c4
-rw-r--r--sample/event-test.c12
-rw-r--r--sample/hello-world.c4
-rw-r--r--sample/http-server.c22
-rw-r--r--sample/le-proxy.c2
-rw-r--r--sample/signal-test.c4
-rw-r--r--sample/time-test.c6
-rw-r--r--select.c7
-rw-r--r--signal.c20
-rw-r--r--strlcpy-internal.h1
-rw-r--r--strlcpy.c5
-rw-r--r--test/Makefile.nmake2
-rw-r--r--test/bench.c8
-rw-r--r--test/bench_cascade.c6
-rw-r--r--test/bench_http.c6
-rw-r--r--test/bench_httpclient.c10
-rw-r--r--test/regress.c129
-rw-r--r--test/regress.h2
-rw-r--r--test/regress_buffer.c588
-rw-r--r--test/regress_bufferevent.c13
-rw-r--r--test/regress_dns.c27
-rw-r--r--test/regress_et.c6
-rw-r--r--test/regress_http.c51
-rw-r--r--test/regress_listener.c8
-rw-r--r--test/regress_main.c27
-rw-r--r--test/regress_minheap.c2
-rw-r--r--test/regress_rpc.c8
-rw-r--r--test/regress_ssl.c4
-rw-r--r--test/regress_testutils.c7
-rw-r--r--test/regress_thread.c118
-rw-r--r--test/regress_util.c152
-rw-r--r--test/regress_zlib.c4
-rw-r--r--test/test-changelist.c18
-rw-r--r--test/test-eof.c4
-rw-r--r--test/test-init.c4
-rw-r--r--test/test-ratelim.c12
-rw-r--r--test/test-time.c6
-rw-r--r--test/test-weof.c6
-rwxr-xr-xtest/test.sh58
-rw-r--r--test/tinytest.c15
-rw-r--r--test/tinytest_local.h4
-rw-r--r--util-internal.h8
-rw-r--r--win32select.c3
111 files changed, 4149 insertions, 1507 deletions
diff --git a/.gitignore b/.gitignore
index 2dca840e..5b9e63a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,8 +67,10 @@ libevent_openssl.pc
/ltmain.sh
/missing
/stamp-h1
+/stamp-h2
/include/event2/event-config.h
+/evconfig-private.h
/sample/dns-example
/sample/event-test
diff --git a/Makefile.am b/Makefile.am
index 1cc7f812..47ebf199 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,7 @@ ACLOCAL_AMFLAGS = -I m4
# will increment for each series until we revise our interfaces enough
# that we can seriously expect ABI compatibility between series.
#
-RELEASE = -release 2.0
+RELEASE = -release 2.1
# This is the version info for the libevent binary API. It has three
# numbers:
@@ -38,7 +38,7 @@ RELEASE = -release 2.0
#
# Once an RC is out, DO NOT MAKE ANY ABI-BREAKING CHANGES IN THAT SERIES
# UNLESS YOU REALLY REALLY HAVE TO.
-VERSION_INFO = 6:5:1
+VERSION_INFO = 1:0:0
# History: RELEASE VERSION_INFO
# 2.0.1-alpha -- 2.0 1:0:0
@@ -163,7 +163,7 @@ endif
BUILT_SOURCES = ./include/event2/event-config.h
./include/event2/event-config.h: config.h
- @MKDIR_P@ ./include/event2
+ $(MKDIR_P) ./include/event2 || true
echo '/* event2/event-config.h' > $@
echo ' *' >> $@
echo ' * This file was generated by autoconf when libevent was built, and post-' >> $@
@@ -178,6 +178,7 @@ BUILT_SOURCES = ./include/event2/event-config.h
sed -e 's/#define /#define _EVENT_/' \
-e 's/#undef /#undef _EVENT_/' \
+ -e 's/# define /# define _EVENT_/' \
-e 's/#ifndef /#ifndef _EVENT_/' < config.h >> $@
echo "#endif" >> $@
@@ -219,6 +220,7 @@ if OPENSSL
libevent_openssl_la_SOURCES = bufferevent_openssl.c
libevent_openssl_la_LIBADD = $(MAYBE_CORE) $(OPENSSL_LIBS)
libevent_openssl_la_LDFLAGS = $(GENERIC_LDFLAGS)
+libevent_openssl_la_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS)
endif
noinst_HEADERS = util-internal.h mm-internal.h ipv6-internal.h \
@@ -228,7 +230,9 @@ noinst_HEADERS = util-internal.h mm-internal.h ipv6-internal.h \
minheap-internal.h log-internal.h evsignal-internal.h evmap-internal.h \
changelist-internal.h iocp-internal.h \
ratelim-internal.h \
+ evconfig-private.h \
WIN32-Code/event2/event-config.h \
+ WIN32-Code/evconfig-private.h \
WIN32-Code/tree.h \
compat/sys/queue.h
diff --git a/Makefile.nmake b/Makefile.nmake
index 4cd04c13..e472e988 100644
--- a/Makefile.nmake
+++ b/Makefile.nmake
@@ -2,7 +2,7 @@
# tons of important things. DO NOT RELY ON IT TO BUILD A GOOD LIBEVENT.
# Needed for correctness
-CFLAGS=/IWIN32-Code /Iinclude /Icompat /DWIN32 /DHAVE_CONFIG_H /I.
+CFLAGS=/IWIN32-Code /Iinclude /Icompat /DHAVE_CONFIG_H /I.
# For optimization and warnings
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo
diff --git a/WIN32-Code/evconfig-private.h b/WIN32-Code/evconfig-private.h
new file mode 100644
index 00000000..88b3f44f
--- /dev/null
+++ b/WIN32-Code/evconfig-private.h
@@ -0,0 +1,6 @@
+#if !defined(_EVENT_EVCONFIG__PRIVATE_H) && !defined(__MINGW32__)
+#define _EVENT_EVCONFIG__PRIVATE_H
+
+/* Nothing to see here. Move along. */
+
+#endif
diff --git a/WIN32-Code/event2/event-config.h b/WIN32-Code/event2/event-config.h
index 144f0acc..f2ea1fcb 100644
--- a/WIN32-Code/event2/event-config.h
+++ b/WIN32-Code/event2/event-config.h
@@ -277,7 +277,7 @@
/* #undef _EVENT_HAVE_WORKING_KQUEUE */
/* Numeric representation of the version */
-#define _EVENT_NUMERIC_VERSION 0x02001100
+#define _EVENT_NUMERIC_VERSION 0x02010001
/* Name of package */
#define _EVENT_PACKAGE "libevent"
@@ -334,7 +334,13 @@
#define _EVENT_TIME_WITH_SYS_TIME 1
/* Version number of package */
+<<<<<<< HEAD
+#define _EVENT_VERSION "2.1.0-alpha-dev"
+||||||| merged common ancestors
+#define _EVENT_VERSION "2.0.16-stable-dev"
+=======
#define _EVENT_VERSION "2.0.17-stable"
+>>>>>>> origin/patches-2.0
/* Define to appropriate substitue if compiler doesnt have __func__ */
#define _EVENT___func__ __FUNCTION__
diff --git a/arc4random.c b/arc4random.c
index ee2b73a2..31f3842f 100644
--- a/arc4random.c
+++ b/arc4random.c
@@ -50,7 +50,8 @@
#endif
#ifndef ARC4RANDOM_NO_INCLUDES
-#ifdef WIN32
+#include "evconfig-private.h"
+#ifdef _WIN32
#include <wincrypt.h>
#include <process.h>
#else
@@ -79,7 +80,7 @@ struct arc4_stream {
unsigned char s[256];
};
-#ifdef WIN32
+#ifdef _WIN32
#define getpid _getpid
#define pid_t int
#endif
@@ -120,7 +121,7 @@ arc4_addrandom(const unsigned char *dat, int datlen)
rs.j = rs.i;
}
-#ifndef WIN32
+#ifndef _WIN32
static ssize_t
read_all(int fd, unsigned char *buf, size_t count)
{
@@ -140,7 +141,7 @@ read_all(int fd, unsigned char *buf, size_t count)
}
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define TRY_SEED_WIN32
static int
arc4_seed_win32(void)
@@ -290,7 +291,7 @@ arc4_seed_proc_sys_kernel_random_uuid(void)
}
#endif
-#ifndef WIN32
+#ifndef _WIN32
#define TRY_SEED_URANDOM
static int
arc4_seed_urandom(void)
diff --git a/buffer.c b/buffer.c
index ff64aede..00035745 100644
--- a/buffer.c
+++ b/buffer.c
@@ -26,16 +26,18 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#endif
#ifdef _EVENT_HAVE_VASPRINTF
-/* If we have vasprintf, we need to define this before we include stdio.h. */
-#define _GNU_SOURCE
+/* If we have vasprintf, we need to define _GNU_SOURCE before we include
+ * stdio.h. This comes from evconfig-private.h.
+ */
#endif
#include <sys/types.h>
@@ -63,6 +65,10 @@
#ifdef _EVENT_HAVE_SYS_SENDFILE_H
#include <sys/sendfile.h>
#endif
+#ifdef _EVENT_HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
#include <errno.h>
#include <stdio.h>
@@ -83,7 +89,6 @@
#include "event2/bufferevent_compat.h"
#include "event2/bufferevent_struct.h"
#include "event2/thread.h"
-#include "event2/event-config.h"
#include "log-internal.h"
#include "mm-internal.h"
#include "util-internal.h"
@@ -111,14 +116,6 @@
#define SENDFILE_IS_SOLARIS 1
#endif
-#ifdef USE_SENDFILE
-static int use_sendfile = 1;
-#endif
-#ifdef _EVENT_HAVE_MMAP
-static int use_mmap = 1;
-#endif
-
-
/* Mask of user-selectable callback flags. */
#define EVBUFFER_CB_USER_FLAGS 0xffff
/* Mask of all internal-use-only flags. */
@@ -135,6 +132,13 @@ static int use_mmap = 1;
#define CHAIN_PINNED(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_ANY) != 0)
#define CHAIN_PINNED_R(ch) (((ch)->flags & EVBUFFER_MEM_PINNED_R) != 0)
+/* evbuffer_ptr support */
+#define PTR_NOT_FOUND(ptr) do { \
+ (ptr)->pos = -1; \
+ (ptr)->_internal.chain = NULL; \
+ (ptr)->_internal.pos_in_chain = 0; \
+} while (0)
+
static void evbuffer_chain_align(struct evbuffer_chain *chain);
static int evbuffer_chain_should_realign(struct evbuffer_chain *chain,
size_t datalen);
@@ -143,13 +147,10 @@ static int evbuffer_ptr_memcmp(const struct evbuffer *buf,
const struct evbuffer_ptr *pos, const char *mem, size_t len);
static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
size_t datlen);
-
-#ifdef WIN32
-static int evbuffer_readfile(struct evbuffer *buf, evutil_socket_t fd,
- ev_ssize_t howmuch);
-#else
-#define evbuffer_readfile evbuffer_read
-#endif
+static int evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos,
+ size_t howfar);
+static int evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg);
+static inline void evbuffer_chain_incref(struct evbuffer_chain *chain);
static struct evbuffer_chain *
evbuffer_chain_new(size_t size)
@@ -177,50 +178,66 @@ evbuffer_chain_new(size_t size)
*/
chain->buffer = EVBUFFER_CHAIN_EXTRA(u_char, chain);
+ chain->refcnt = 1;
+
return (chain);
}
static inline void
evbuffer_chain_free(struct evbuffer_chain *chain)
{
+ EVUTIL_ASSERT(chain->refcnt > 0);
+ if (--chain->refcnt > 0) {
+ /* chain is still referenced by other chains */
+ return;
+ }
+
if (CHAIN_PINNED(chain)) {
+ /* will get freed once no longer dangling */
+ chain->refcnt++;
chain->flags |= EVBUFFER_DANGLING;
return;
}
- if (chain->flags & (EVBUFFER_MMAP|EVBUFFER_SENDFILE|
- EVBUFFER_REFERENCE)) {
- if (chain->flags & EVBUFFER_REFERENCE) {
- struct evbuffer_chain_reference *info =
- EVBUFFER_CHAIN_EXTRA(
- struct evbuffer_chain_reference,
- chain);
- if (info->cleanupfn)
- (*info->cleanupfn)(chain->buffer,
- chain->buffer_len,
- info->extra);
- }
-#ifdef _EVENT_HAVE_MMAP
- if (chain->flags & EVBUFFER_MMAP) {
- struct evbuffer_chain_fd *info =
- EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd,
- chain);
- if (munmap(chain->buffer, chain->buffer_len) == -1)
- event_warn("%s: munmap failed", __func__);
- if (close(info->fd) == -1)
- event_warn("%s: close(%d) failed",
- __func__, info->fd);
- }
+
+ /* safe to release chain, it's either a referencing
+ * chain or all references to it have been freed */
+ if (chain->flags & EVBUFFER_REFERENCE) {
+ struct evbuffer_chain_reference *info =
+ EVBUFFER_CHAIN_EXTRA(
+ struct evbuffer_chain_reference,
+ chain);
+ if (info->cleanupfn)
+ (*info->cleanupfn)(chain->buffer,
+ chain->buffer_len,
+ info->extra);
+ }
+ if (chain->flags & EVBUFFER_FILESEGMENT) {
+ struct evbuffer_chain_file_segment *info =
+ EVBUFFER_CHAIN_EXTRA(
+ struct evbuffer_chain_file_segment,
+ chain);
+ if (info->segment) {
+#ifdef _WIN32
+ if (info->segment->is_mapping)
+ UnmapViewOfFile(chain->buffer);
#endif
-#ifdef USE_SENDFILE
- if (chain->flags & EVBUFFER_SENDFILE) {
- struct evbuffer_chain_fd *info =
- EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd,
- chain);
- if (close(info->fd) == -1)
- event_warn("%s: close(%d) failed",
- __func__, info->fd);
+ evbuffer_file_segment_free(info->segment);
}
-#endif
+ }
+ if (chain->flags & EVBUFFER_MULTICAST) {
+ struct evbuffer_multicast_parent *info =
+ EVBUFFER_CHAIN_EXTRA(
+ struct evbuffer_multicast_parent,
+ chain);
+ /* referencing chain is being freed, decrease
+ * refcounts of source chain and associated
+ * evbuffer (which get freed once both reach
+ * zero) */
+ EVUTIL_ASSERT(info->source != NULL);
+ EVUTIL_ASSERT(info->parent != NULL);
+ EVBUFFER_LOCK(info->source);
+ evbuffer_chain_free(info->parent);
+ _evbuffer_decref_and_unlock(info->source);
}
mm_free(chain);
@@ -290,21 +307,11 @@ evbuffer_chain_insert(struct evbuffer *buf,
EVUTIL_ASSERT(buf->first == NULL);
buf->first = buf->last = chain;
} else {
- struct evbuffer_chain **ch = buf->last_with_datap;
- /* Find the first victim chain. It might be *last_with_datap */
- while ((*ch) && ((*ch)->off != 0 || CHAIN_PINNED(*ch)))
- ch = &(*ch)->next;
- if (*ch == NULL) {
- /* There is no victim; just append this new chain. */
- buf->last->next = chain;
- if (chain->off)
- buf->last_with_datap = &buf->last->next;
- } else {
- /* Replace all victim chains with this chain. */
- EVUTIL_ASSERT(evbuffer_chains_all_empty(*ch));
- evbuffer_free_all_chains(*ch);
- *ch = chain;
- }
+ struct evbuffer_chain **chp;
+ chp = evbuffer_free_trailing_empty_chains(buf);
+ *chp = chain;
+ if (chain->off)
+ buf->last_with_datap = chp;
buf->last = chain;
}
buf->total_len += chain->off;
@@ -336,6 +343,12 @@ _evbuffer_chain_unpin(struct evbuffer_chain *chain, unsigned flag)
evbuffer_chain_free(chain);
}
+static inline void
+evbuffer_chain_incref(struct evbuffer_chain *chain)
+{
+ ++chain->refcnt;
+}
+
struct evbuffer *
evbuffer_new(void)
{
@@ -345,7 +358,7 @@ evbuffer_new(void)
if (buffer == NULL)
return (NULL);
- TAILQ_INIT(&buffer->callbacks);
+ LIST_INIT(&buffer->callbacks);
buffer->refcnt = 1;
buffer->last_with_datap = &buffer->first;
@@ -454,7 +467,7 @@ evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
ASSERT_EVBUFFER_LOCKED(buffer);
- if (TAILQ_EMPTY(&buffer->callbacks)) {
+ if (LIST_EMPTY(&buffer->callbacks)) {
buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
return;
}
@@ -469,12 +482,12 @@ evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
buffer->n_add_for_cb = 0;
buffer->n_del_for_cb = 0;
}
- for (cbent = TAILQ_FIRST(&buffer->callbacks);
- cbent != TAILQ_END(&buffer->callbacks);
+ for (cbent = LIST_FIRST(&buffer->callbacks);
+ cbent != LIST_END(&buffer->callbacks);
cbent = next) {
/* Get the 'next' pointer now in case this callback decides
* to remove itself or something. */
- next = TAILQ_NEXT(cbent, next);
+ next = LIST_NEXT(cbent, next);
if ((cbent->flags & mask) != masked_val)
continue;
@@ -490,7 +503,7 @@ evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
void
evbuffer_invoke_callbacks(struct evbuffer *buffer)
{
- if (TAILQ_EMPTY(&buffer->callbacks)) {
+ if (LIST_EMPTY(&buffer->callbacks)) {
buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
return;
}
@@ -529,9 +542,9 @@ evbuffer_remove_all_callbacks(struct evbuffer *buffer)
{
struct evbuffer_cb_entry *cbent;
- while ((cbent = TAILQ_FIRST(&buffer->callbacks))) {
- TAILQ_REMOVE(&buffer->callbacks, cbent, next);
- mm_free(cbent);
+ while ((cbent = LIST_FIRST(&buffer->callbacks))) {
+ LIST_REMOVE(cbent, next);
+ mm_free(cbent);
}
}
@@ -609,6 +622,42 @@ evbuffer_get_contiguous_space(const struct evbuffer *buf)
return result;
}
+size_t
+evbuffer_add_iovec(struct evbuffer * buf, struct evbuffer_iovec * vec, int n_vec) {
+ int n;
+ size_t res;
+ size_t to_alloc;
+
+ EVBUFFER_LOCK(buf);
+
+ res = to_alloc = 0;
+
+ for (n = 0; n < n_vec; n++) {
+ to_alloc += vec[n].iov_len;
+ }
+
+ if (_evbuffer_expand_fast(buf, to_alloc, 2) < 0) {
+ goto done;
+ }
+
+ for (n = 0; n < n_vec; n++) {
+ /* XXX each 'add' call here does a bunch of setup that's
+ * obviated by _evbuffer_expand_fast, and some cleanup that we
+ * would like to do only once. Instead we should just extract
+ * the part of the code that's needed. */
+
+ if (evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len) < 0) {
+ goto done;
+ }
+
+ res += vec[n].iov_len;
+ }
+
+done:
+ EVBUFFER_UNLOCK(buf);
+ return res;
+}
+
int
evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
struct evbuffer_iovec *vec, int n_vecs)
@@ -837,6 +886,46 @@ APPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src)
dst->total_len += src->total_len;
}
+static inline void
+APPEND_CHAIN_MULTICAST(struct evbuffer *dst, struct evbuffer *src)
+{
+ struct evbuffer_chain *tmp;
+ struct evbuffer_chain *chain = src->first;
+ struct evbuffer_multicast_parent *extra;
+
+ ASSERT_EVBUFFER_LOCKED(dst);
+ ASSERT_EVBUFFER_LOCKED(src);
+
+ for (; chain; chain = chain->next) {
+ if (!chain->off || chain->flags & EVBUFFER_DANGLING) {
+ /* skip empty chains */
+ continue;
+ }
+
+ tmp = evbuffer_chain_new(sizeof(struct evbuffer_multicast_parent));
+ if (!tmp) {
+ event_warn("%s: out of memory", __func__);
+ return;
+ }
+ extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_multicast_parent, tmp);
+ /* reference evbuffer containing source chain so it
+ * doesn't get released while the chain is still
+ * being referenced to */
+ _evbuffer_incref(src);
+ extra->source = src;
+ /* reference source chain which now becomes immutable */
+ evbuffer_chain_incref(chain);
+ extra->parent = chain;
+ chain->flags |= EVBUFFER_IMMUTABLE;
+ tmp->buffer_len = chain->buffer_len;
+ tmp->misalign = chain->misalign;
+ tmp->off = chain->off;
+ tmp->flags |= EVBUFFER_MULTICAST|EVBUFFER_IMMUTABLE;
+ tmp->buffer = chain->buffer;
+ evbuffer_chain_insert(dst, tmp);
+ }
+}
+
static void
PREPEND_CHAIN(struct evbuffer *dst, struct evbuffer *src)
{
@@ -902,6 +991,49 @@ done:
}
int
+evbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf)
+{
+ size_t in_total_len, out_total_len;
+ struct evbuffer_chain *chain;
+ int result = 0;
+
+ EVBUFFER_LOCK2(inbuf, outbuf);
+ in_total_len = inbuf->total_len;
+ out_total_len = outbuf->total_len;
+ chain = inbuf->first;
+
+ if (in_total_len == 0)
+ goto done;
+
+ if (outbuf->freeze_end || outbuf == inbuf) {
+ result = -1;
+ goto done;
+ }
+
+ for (; chain; chain = chain->next) {
+ if ((chain->flags & (EVBUFFER_FILESEGMENT|EVBUFFER_SENDFILE|EVBUFFER_MULTICAST)) != 0) {
+ /* chain type can not be referenced */
+ result = -1;
+ goto done;
+ }
+ }
+
+ if (out_total_len == 0) {
+ /* There might be an empty chain at the start of outbuf; free
+ * it. */
+ evbuffer_free_all_chains(outbuf->first);
+ }
+ APPEND_CHAIN_MULTICAST(outbuf, inbuf);
+
+ outbuf->n_add_for_cb += in_total_len;
+ evbuffer_invoke_callbacks(outbuf);
+
+done:
+ EVBUFFER_UNLOCK2(inbuf, outbuf);
+ return result;
+}
+
+int
evbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
{
struct evbuffer_chain *pinned, *last;
@@ -1001,10 +1133,8 @@ evbuffer_drain(struct evbuffer *buf, size_t len)
}
buf->first = chain;
- if (chain) {
- chain->misalign += remaining;
- chain->off -= remaining;
- }
+ chain->misalign += remaining;
+ chain->off -= remaining;
}
buf->n_del_for_cb += len;
@@ -1022,7 +1152,7 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
{
ev_ssize_t n;
EVBUFFER_LOCK(buf);
- n = evbuffer_copyout(buf, data_out, datlen);
+ n = evbuffer_copyout_from(buf, NULL, data_out, datlen);
if (n > 0) {
if (evbuffer_drain(buf, n)<0)
n = -1;
@@ -1034,18 +1164,34 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
ev_ssize_t
evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen)
{
+ return evbuffer_copyout_from(buf, NULL, data_out, datlen);
+}
+
+ev_ssize_t
+evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos,
+ void *data_out, size_t datlen)
+{
/*XXX fails badly on sendfile case. */
struct evbuffer_chain *chain;
char *data = data_out;
size_t nread;
ev_ssize_t result = 0;
+ size_t pos_in_chain;
EVBUFFER_LOCK(buf);
- chain = buf->first;
+ if (pos) {
+ chain = pos->_internal.chain;
+ pos_in_chain = pos->_internal.pos_in_chain;
+ if (datlen + pos->pos > buf->total_len)
+ datlen = buf->total_len - pos->pos;
+ } else {
+ chain = buf->first;
+ pos_in_chain = 0;
+ if (datlen > buf->total_len)
+ datlen = buf->total_len;
+ }
- if (datlen >= buf->total_len)
- datlen = buf->total_len;
if (datlen == 0)
goto done;
@@ -1057,18 +1203,23 @@ evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen)
nread = datlen;
- while (datlen && datlen >= chain->off) {
- memcpy(data, chain->buffer + chain->misalign, chain->off);
- data += chain->off;
- datlen -= chain->off;
+ while (datlen && datlen >= chain->off - pos_in_chain) {
+ size_t copylen = chain->off - pos_in_chain;
+ memcpy(data,
+ chain->buffer + chain->misalign + pos_in_chain,
+ copylen);
+ data += copylen;
+ datlen -= copylen;
chain = chain->next;
+ pos_in_chain = 0;
EVUTIL_ASSERT(chain || datlen==0);
}
if (datlen) {
EVUTIL_ASSERT(chain);
- memcpy(data, chain->buffer + chain->misalign, datlen);
+ memcpy(data, chain->buffer + chain->misalign + pos_in_chain,
+ datlen);
}
result = nread;
@@ -1372,7 +1523,7 @@ evbuffer_strspn(
size_t i = ptr->_internal.pos_in_chain;
if (!chain)
- return -1;
+ return 0;
while (1) {
char *buffer = (char *)chain->buffer + chain->misalign;
@@ -1403,13 +1554,16 @@ evbuffer_strspn(
}
-static inline char
+static inline int
evbuffer_getchr(struct evbuffer_ptr *it)
{
struct evbuffer_chain *chain = it->_internal.chain;
size_t off = it->_internal.pos_in_chain;
- return chain->buffer[chain->misalign + off];
+ if (chain == NULL)
+ return -1;
+
+ return (unsigned char)chain->buffer[chain->misalign + off];
}
struct evbuffer_ptr
@@ -1421,6 +1575,14 @@ evbuffer_search_eol(struct evbuffer *buffer,
size_t extra_drain = 0;
int ok = 0;
+ /* Avoid locking in trivial edge cases */
+ if (start && start->_internal.chain == NULL) {
+ PTR_NOT_FOUND(&it);
+ if (eol_len_out)
+ *eol_len_out = extra_drain;
+ return it;
+ }
+
EVBUFFER_LOCK(buffer);
if (start) {
@@ -1447,29 +1609,37 @@ evbuffer_search_eol(struct evbuffer *buffer,
extra_drain = 2;
break;
}
- case EVBUFFER_EOL_CRLF:
- while (1) {
- if (evbuffer_find_eol_char(&it) < 0)
- goto done;
- if (evbuffer_getchr(&it) == '\n') {
- extra_drain = 1;
- break;
- } else if (!evbuffer_ptr_memcmp(
- buffer, &it, "\r\n", 2)) {
- extra_drain = 2;
- break;
- } else {
- if (evbuffer_ptr_set(buffer, &it, 1,
- EVBUFFER_PTR_ADD)<0)
- goto done;
- }
+ case EVBUFFER_EOL_CRLF: {
+ ev_ssize_t start_pos = it.pos;
+ /* Look for a LF ... */
+ if (evbuffer_strchr(&it, '\n') < 0)
+ goto done;
+ extra_drain = 1;
+ /* ... optionally preceeded by a CR. */
+ if (it.pos == start_pos)
+ break; /* If the first character is \n, don't back up */
+ /* This potentially does an extra linear walk over the first
+ * few chains. Probably, that's not too expensive unless you
+ * have a really pathological setup. */
+ memcpy(&it2, &it, sizeof(it));
+ if (evbuffer_ptr_subtract(buffer, &it2, 1)<0)
+ break;
+ if (evbuffer_getchr(&it2) == '\r') {
+ memcpy(&it, &it2, sizeof(it));
+ extra_drain = 2;
}
break;
+ }
case EVBUFFER_EOL_LF:
if (evbuffer_strchr(&it, '\n') < 0)
goto done;
extra_drain = 1;
break;
+ case EVBUFFER_EOL_NUL:
+ if (evbuffer_strchr(&it, '\0') < 0)
+ goto done;
+ extra_drain = 1;
+ break;
default:
goto done;
}
@@ -1478,9 +1648,8 @@ evbuffer_search_eol(struct evbuffer *buffer,
done:
EVBUFFER_UNLOCK(buffer);
- if (!ok) {
- it.pos = -1;
- }
+ if (!ok)
+ PTR_NOT_FOUND(&it);
if (eol_len_out)
*eol_len_out = extra_drain;
@@ -1949,7 +2118,7 @@ evbuffer_expand(struct evbuffer *buf, size_t datlen)
* Reads data from a file descriptor into a buffer.
*/
-#if defined(_EVENT_HAVE_SYS_UIO_H) || defined(WIN32)
+#if defined(_EVENT_HAVE_SYS_UIO_H) || defined(_WIN32)
#define USE_IOVEC_IMPL
#endif
@@ -2037,7 +2206,7 @@ _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
static int
get_n_bytes_readable_on_socket(evutil_socket_t fd)
{
-#if defined(FIONREAD) && defined(WIN32)
+#if defined(FIONREAD) && defined(_WIN32)
unsigned long lng = EVBUFFER_MAX_READ;
if (ioctlsocket(fd, FIONREAD, &lng) < 0)
return -1;
@@ -2103,7 +2272,7 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
#endif
-#ifdef WIN32
+#ifdef _WIN32
{
DWORD bytesRead;
DWORD flags=0;
@@ -2134,7 +2303,7 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
/* We can append new data at this point */
p = chain->buffer + chain->misalign + chain->off;
-#ifndef WIN32
+#ifndef _WIN32
n = read(fd, p, howmuch);
#else
n = recv(fd, p, howmuch, 0);
@@ -2179,56 +2348,6 @@ done:
return result;
}
-#ifdef WIN32
-static int
-evbuffer_readfile(struct evbuffer *buf, evutil_socket_t fd, ev_ssize_t howmuch)
-{
- int result;
- int nchains, n;
- struct evbuffer_iovec v[2];
-
- EVBUFFER_LOCK(buf);
-
- if (buf->freeze_end) {
- result = -1;
- goto done;
- }
-
- if (howmuch < 0)
- howmuch = 16384;
-
-
- /* XXX we _will_ waste some space here if there is any space left
- * over on buf->last. */
- nchains = evbuffer_reserve_space(buf, howmuch, v, 2);
- if (nchains < 1 || nchains > 2) {
- result = -1;
- goto done;
- }
- n = read((int)fd, v[0].iov_base, (unsigned int)v[0].iov_len);
- if (n <= 0) {
- result = n;
- goto done;
- }
- v[0].iov_len = (IOV_LEN_TYPE) n; /* XXXX another problem with big n.*/
- if (nchains > 1) {
- n = read((int)fd, v[1].iov_base, (unsigned int)v[1].iov_len);
- if (n <= 0) {
- result = (unsigned long) v[0].iov_len;
- evbuffer_commit_space(buf, v, 1);
- goto done;
- }
- v[1].iov_len = n;
- }
- evbuffer_commit_space(buf, v, nchains);
-
- result = n;
-done:
- EVBUFFER_UNLOCK(buf);
- return result;
-}
-#endif
-
#ifdef USE_IOVEC_IMPL
static inline int
evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
@@ -2263,7 +2382,7 @@ evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
}
chain = chain->next;
}
-#ifdef WIN32
+#ifdef _WIN32
{
DWORD bytesSent;
if (WSASend(fd, iov, i, &bytesSent, 0, NULL, NULL))
@@ -2280,37 +2399,39 @@ evbuffer_write_iovec(struct evbuffer *buffer, evutil_socket_t fd,
#ifdef USE_SENDFILE
static inline int
-evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t fd,
+evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t dest_fd,
ev_ssize_t howmuch)
{
struct evbuffer_chain *chain = buffer->first;
- struct evbuffer_chain_fd *info =
- EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
+ struct evbuffer_chain_file_segment *info =
+ EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment,
+ chain);
+ const int source_fd = info->segment->fd;
#if defined(SENDFILE_IS_MACOSX) || defined(SENDFILE_IS_FREEBSD)
int res;
- off_t len = chain->off;
+ ev_off_t len = chain->off;
#elif defined(SENDFILE_IS_LINUX) || defined(SENDFILE_IS_SOLARIS)
ev_ssize_t res;
- off_t offset = chain->misalign;
+ ev_off_t offset = chain->misalign;
#endif
ASSERT_EVBUFFER_LOCKED(buffer);
#if defined(SENDFILE_IS_MACOSX)
- res = sendfile(info->fd, fd, chain->misalign, &len, NULL, 0);
+ res = sendfile(source_fd, dest_fd, chain->misalign, &len, NULL, 0);
if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
return (-1);
return (len);
#elif defined(SENDFILE_IS_FREEBSD)
- res = sendfile(info->fd, fd, chain->misalign, chain->off, NULL, &len, 0);
+ res = sendfile(source_fd, dest_fd, chain->misalign, chain->off, NULL, &len, 0);
if (res == -1 && !EVUTIL_ERR_RW_RETRIABLE(errno))
return (-1);
return (len);
#elif defined(SENDFILE_IS_LINUX)
/* TODO(niels): implement splice */
- res = sendfile(fd, info->fd, &offset, chain->off);
+ res = sendfile(dest_fd, source_fd, &offset, chain->off);
if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
/* if this is EAGAIN or EINTR return 0; otherwise, -1 */
return (0);
@@ -2319,7 +2440,7 @@ evbuffer_write_sendfile(struct evbuffer *buffer, evutil_socket_t fd,
#elif defined(SENDFILE_IS_SOLARIS)
{
const off_t offset_orig = offset;
- res = sendfile(fd, info->fd, &offset, chain->off);
+ res = sendfile(dest_fd, source_fd, &offset, chain->off);
if (res == -1 && EVUTIL_ERR_RW_RETRIABLE(errno)) {
if (offset - offset_orig)
return offset - offset_orig;
@@ -2357,7 +2478,7 @@ evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
#endif
#ifdef USE_IOVEC_IMPL
n = evbuffer_write_iovec(buffer, fd, howmuch);
-#elif defined(WIN32)
+#elif defined(_WIN32)
/* XXX(nickm) Don't disable this code until we know if
* the WSARecv code above works. */
void *p = evbuffer_pullup(buffer, howmuch);
@@ -2405,12 +2526,36 @@ evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len)
return search;
}
+/* Subract <b>howfar</b> from the position of <b>pos</b> within
+ * <b>buf</b>. Returns 0 on success, -1 on failure.
+ *
+ * This isn't exposed yet, because of potential inefficiency issues.
+ * Maybe it should be. */
+static int
+evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos,
+ size_t howfar)
+{
+ if (howfar > (size_t)pos->pos)
+ return -1;
+ if (pos->_internal.chain && howfar <= pos->_internal.pos_in_chain) {
+ pos->_internal.pos_in_chain -= howfar;
+ pos->pos -= howfar;
+ return 0;
+ } else {
+ const size_t newpos = pos->pos - howfar;
+ /* Here's the inefficient part: it walks over the
+ * chains until we hit newpos. */
+ return evbuffer_ptr_set(buf, pos, newpos, EVBUFFER_PTR_SET);
+ }
+}
+
int
evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
size_t position, enum evbuffer_ptr_how how)
{
size_t left = position;
struct evbuffer_chain *chain = NULL;
+ int result = 0;
EVBUFFER_LOCK(buf);
@@ -2437,14 +2582,18 @@ evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
if (chain) {
pos->_internal.chain = chain;
pos->_internal.pos_in_chain = position + left;
- } else {
+ } else if (left == 0) {
+ /* The first byte in the (nonexistent) chain after the last chain */
pos->_internal.chain = NULL;
- pos->pos = -1;
+ pos->_internal.pos_in_chain = 0;
+ } else {
+ PTR_NOT_FOUND(pos);
+ result = -1;
}
EVBUFFER_UNLOCK(buf);
- return chain != NULL ? 0 : -1;
+ return result;
}
/**
@@ -2549,8 +2698,7 @@ evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, con
}
not_found:
- pos.pos = -1;
- pos._internal.chain = NULL;
+ PTR_NOT_FOUND(&pos);
done:
EVBUFFER_UNLOCK(buffer);
return pos;
@@ -2565,6 +2713,10 @@ evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len,
int idx = 0;
ev_ssize_t len_so_far = 0;
+ /* Avoid locking in trivial edge cases */
+ if (start_at && start_at->_internal.chain == NULL)
+ return 0;
+
EVBUFFER_LOCK(buffer);
if (start_at) {
@@ -2722,169 +2874,323 @@ done:
return result;
}
-/* TODO(niels): maybe we don't want to own the fd, however, in that
- * case, we should dup it - dup is cheap. Perhaps, we should use a
- * callback instead?
- */
/* TODO(niels): we may want to add to automagically convert to mmap, in
* case evbuffer_remove() or evbuffer_pullup() are being used.
*/
-int
-evbuffer_add_file(struct evbuffer *outbuf, int fd,
- ev_off_t offset, ev_off_t length)
+struct evbuffer_file_segment *
+evbuffer_file_segment_new(
+ int fd, ev_off_t offset, ev_off_t length, unsigned flags)
{
-#if defined(USE_SENDFILE) || defined(_EVENT_HAVE_MMAP)
- struct evbuffer_chain *chain;
- struct evbuffer_chain_fd *info;
+ struct evbuffer_file_segment *seg =
+ mm_calloc(sizeof(struct evbuffer_file_segment), 1);
+ if (!seg)
+ return NULL;
+ seg->refcnt = 1;
+ seg->fd = fd;
+ seg->flags = flags;
+ seg->file_offset = offset;
+
+#ifdef _WIN32
+#define lseek _lseeki64
+#define fstat _fstat
+#define stat _stat
#endif
+ if (length == -1) {
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ goto err;
+ length = st.st_size;
+ }
+ seg->length = length;
+
#if defined(USE_SENDFILE)
- int sendfile_okay = 1;
+ if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) {
+ seg->can_sendfile = 1;
+ goto done;
+ }
#endif
- int ok = 1;
+
+ if (evbuffer_file_segment_materialize(seg)<0)
+ goto err;
#if defined(USE_SENDFILE)
- if (use_sendfile) {
- EVBUFFER_LOCK(outbuf);
- sendfile_okay = outbuf->flags & EVBUFFER_FLAG_DRAINS_TO_FD;
- EVBUFFER_UNLOCK(outbuf);
+done:
+#endif
+ if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
+ EVTHREAD_ALLOC_LOCK(seg->lock, 0);
}
+ return seg;
+err:
+ mm_free(seg);
+ return NULL;
+}
- if (use_sendfile && sendfile_okay) {
- chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
- if (chain == NULL) {
- event_warn("%s: out of memory", __func__);
- return (-1);
- }
-
- chain->flags |= EVBUFFER_SENDFILE | EVBUFFER_IMMUTABLE;
- chain->buffer = NULL; /* no reading possible */
- chain->buffer_len = length + offset;
- chain->off = length;
- chain->misalign = offset;
+/* DOCDOC */
+/* Requires lock */
+static int
+evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg)
+{
+ const unsigned flags = seg->flags;
+ const int fd = seg->fd;
+ const ev_off_t length = seg->length;
+ const ev_off_t offset = seg->file_offset;
- info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
- info->fd = fd;
+ if (seg->contents)
+ return 0; /* already materialized */
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- mm_free(chain);
- ok = 0;
- } else {
- outbuf->n_add_for_cb += length;
- evbuffer_chain_insert(outbuf, chain);
- }
- } else
-#endif
#if defined(_EVENT_HAVE_MMAP)
- if (use_mmap) {
- void *mapped = mmap(NULL, length + offset, PROT_READ,
+ if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
+ off_t offset_rounded = 0, offset_leftover = 0;
+ void *mapped;
+ if (offset) {
+ /* mmap implementations don't generally like us
+ * to have an offset that isn't a round */
+#ifdef SC_PAGE_SIZE
+ long page_size = sysconf(SC_PAGE_SIZE);
+#elif defined(_SC_PAGE_SIZE)
+ long page_size = sysconf(_SC_PAGE_SIZE);
+#else
+ long page_size = 1;
+#endif
+ if (page_size == -1)
+ goto err;
+ offset_leftover = offset % page_size;
+ offset_rounded = offset - offset_leftover;
+ }
+ mapped = mmap(NULL, length + offset_leftover,
+ PROT_READ,
#ifdef MAP_NOCACHE
- MAP_NOCACHE |
+ MAP_NOCACHE | /* ??? */
#endif
#ifdef MAP_FILE
MAP_FILE |
#endif
MAP_PRIVATE,
- fd, 0);
- /* some mmap implementations require offset to be a multiple of
- * the page size. most users of this api, are likely to use 0
- * so mapping everything is not likely to be a problem.
- * TODO(niels): determine page size and round offset to that
- * page size to avoid mapping too much memory.
- */
+ fd, offset_rounded);
if (mapped == MAP_FAILED) {
event_warn("%s: mmap(%d, %d, %zu) failed",
__func__, fd, 0, (size_t)(offset + length));
- return (-1);
+ } else {
+ seg->mapping = mapped;
+ seg->contents = (char*)mapped+offset_leftover;
+ seg->mmap_offset = 0;
+ seg->is_mapping = 1;
+ goto done;
}
- chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
- if (chain == NULL) {
- event_warn("%s: out of memory", __func__);
- munmap(mapped, length);
- return (-1);
+ }
+#endif
+#ifdef _WIN32
+ if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
+ long h = (long)_get_osfhandle(fd);
+ HANDLE m;
+ ev_uint64_t total_size = length+offset;
+ if (h == (long)INVALID_HANDLE_VALUE)
+ goto err;
+ m = CreateFileMapping((HANDLE)h, NULL, PAGE_READONLY,
+ (total_size >> 32), total_size & 0xfffffffful,
+ NULL);
+ if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */
+ seg->mapping_handle = m;
+ seg->mmap_offset = offset;
+ seg->is_mapping = 1;
+ goto done;
+ }
+ }
+#endif
+ {
+ ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos;
+ ev_off_t read_so_far = 0;
+ char *mem;
+ int e;
+ ev_ssize_t n = 0;
+ if (!(mem = mm_malloc(length)))
+ goto err;
+ if (start_pos < 0) {
+ mm_free(mem);
+ goto err;
+ }
+ if (lseek(fd, offset, SEEK_SET) < 0) {
+ mm_free(mem);
+ goto err;
+ }
+ while (read_so_far < length) {
+ n = read(fd, mem+read_so_far, length-read_so_far);
+ if (n <= 0)
+ break;
+ read_so_far += n;
}
- chain->flags |= EVBUFFER_MMAP | EVBUFFER_IMMUTABLE;
- chain->buffer = mapped;
- chain->buffer_len = length + offset;
- chain->off = length + offset;
-
- info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
- info->fd = fd;
+ e = errno;
+ pos = lseek(fd, start_pos, SEEK_SET);
+ if (n < 0 || (n == 0 && length > read_so_far)) {
+ mm_free(mem);
+ errno = e;
+ goto err;
+ } else if (pos < 0) {
+ mm_free(mem);
+ goto err;
+ }
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- info->fd = -1;
- evbuffer_chain_free(chain);
- ok = 0;
- } else {
- outbuf->n_add_for_cb += length;
+ seg->contents = mem;
+ }
- evbuffer_chain_insert(outbuf, chain);
+done:
+ return 0;
+err:
+ return -1;
+}
- /* we need to subtract whatever we don't need */
- evbuffer_drain(outbuf, offset);
- }
- } else
+void
+evbuffer_file_segment_free(struct evbuffer_file_segment *seg)
+{
+ int refcnt;
+ EVLOCK_LOCK(seg->lock, 0);
+ refcnt = --seg->refcnt;
+ EVLOCK_UNLOCK(seg->lock, 0);
+ if (refcnt > 0)
+ return;
+ EVUTIL_ASSERT(refcnt == 0);
+
+ if (seg->is_mapping) {
+#ifdef _WIN32
+ CloseHandle(seg->mapping_handle);
+#elif defined (_EVENT_HAVE_MMAP)
+ if (munmap(seg->mapping, seg->length) == -1)
+ event_warn("%s: munmap failed", __func__);
#endif
- {
- /* the default implementation */
- struct evbuffer *tmp = evbuffer_new();
- ev_ssize_t read;
+ } else if (seg->contents) {
+ mm_free(seg->contents);
+ }
- if (tmp == NULL)
- return (-1);
+ if ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) {
+ close(seg->fd);
+ }
-#ifdef WIN32
-#define lseek _lseeki64
-#endif
- if (lseek(fd, offset, SEEK_SET) == -1) {
- evbuffer_free(tmp);
- return (-1);
- }
+ EVTHREAD_FREE_LOCK(seg->lock, 0);
+ mm_free(seg);
+}
- /* we add everything to a temporary buffer, so that we
- * can abort without side effects if the read fails.
- */
- while (length) {
- read = evbuffer_readfile(tmp, fd, (ev_ssize_t)length);
- if (read == -1) {
- evbuffer_free(tmp);
- return (-1);
- }
+int
+evbuffer_add_file_segment(struct evbuffer *buf,
+ struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length)
+{
+ struct evbuffer_chain *chain;
+ struct evbuffer_chain_file_segment *extra;
+ int can_use_sendfile = 0;
- length -= read;
+ EVBUFFER_LOCK(buf);
+ EVLOCK_LOCK(seg->lock, 0);
+ if (buf->flags & EVBUFFER_FLAG_DRAINS_TO_FD) {
+ can_use_sendfile = 1;
+ } else {
+ if (!seg->contents) {
+ if (evbuffer_file_segment_materialize(seg)<0) {
+ EVLOCK_UNLOCK(seg->lock, 0);
+ EVBUFFER_UNLOCK(buf);
+ return -1;
+ }
}
+ }
+ ++seg->refcnt;
+ EVLOCK_UNLOCK(seg->lock, 0);
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- evbuffer_free(tmp);
- ok = 0;
- } else {
- evbuffer_add_buffer(outbuf, tmp);
- evbuffer_free(tmp);
+ if (buf->freeze_end)
+ goto err;
-#ifdef WIN32
-#define close _close
-#endif
- close(fd);
+ if (length < 0) {
+ if (offset > seg->length)
+ goto err;
+ length = seg->length - offset;
+ }
+
+ /* Can we actually add this? */
+ if (offset+length > seg->length)
+ goto err;
+
+ chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_file_segment));
+ if (!chain)
+ goto err;
+ extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain);
+
+ chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT;
+ if (can_use_sendfile && seg->can_sendfile) {
+ chain->flags |= EVBUFFER_SENDFILE;
+ chain->misalign = seg->file_offset + offset;
+ chain->off = length;
+ chain->buffer_len = chain->misalign + length;
+ } else if (seg->is_mapping) {
+#ifdef _WIN32
+ ev_uint64_t total_offset = seg->mmap_offset+offset;
+ ev_uint64_t offset_rounded=0, offset_remaining=0;
+ LPVOID data;
+ if (total_offset) {
+ SYSTEM_INFO si;
+ memset(&si, 0, sizeof(si)); /* cargo cult */
+ GetSystemInfo(&si);
+ offset_remaining = total_offset % si.dwAllocationGranularity;
+ offset_rounded = total_offset - offset_remaining;
}
+ data = MapViewOfFile(
+ seg->mapping_handle,
+ FILE_MAP_READ,
+ offset_rounded >> 32,
+ offset_rounded & 0xfffffffful,
+ length + offset_remaining);
+ if (data == NULL) {
+ mm_free(chain);
+ goto err;
+ }
+ chain->buffer = (unsigned char*) data;
+ chain->buffer_len = length+offset_remaining;
+ chain->misalign = offset_remaining;
+ chain->off = length;
+#else
+ chain->buffer = (unsigned char*)(seg->contents + offset);
+ chain->buffer_len = length;
+ chain->off = length;
+#endif
+ } else {
+ chain->buffer = (unsigned char*)(seg->contents + offset);
+ chain->buffer_len = length;
+ chain->off = length;
}
- if (ok)
- evbuffer_invoke_callbacks(outbuf);
- EVBUFFER_UNLOCK(outbuf);
+ extra->segment = seg;
+ buf->n_add_for_cb += length;
+ evbuffer_chain_insert(buf, chain);
+
+ evbuffer_invoke_callbacks(buf);
+
+ EVBUFFER_UNLOCK(buf);
- return ok ? 0 : -1;
+ return 0;
+err:
+ EVBUFFER_UNLOCK(buf);
+ evbuffer_file_segment_free(seg);
+ return -1;
}
+int
+evbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length)
+{
+ struct evbuffer_file_segment *seg;
+ unsigned flags = EVBUF_FS_CLOSE_ON_FREE;
+ int r;
+
+ seg = evbuffer_file_segment_new(fd, offset, length, flags);
+ if (!seg)
+ return -1;
+ r = evbuffer_add_file_segment(buf, seg, 0, length);
+ evbuffer_file_segment_free(seg);
+ return r;
+}
void
evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
{
EVBUFFER_LOCK(buffer);
- if (!TAILQ_EMPTY(&buffer->callbacks))
+ if (!LIST_EMPTY(&buffer->callbacks))
evbuffer_remove_all_callbacks(buffer);
if (cb) {
@@ -2906,7 +3212,7 @@ evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
e->cb.cb_func = cb;
e->cbarg = cbarg;
e->flags = EVBUFFER_CB_ENABLED;
- TAILQ_INSERT_HEAD(&buffer->callbacks, e, next);
+ LIST_INSERT_HEAD(&buffer->callbacks, e, next);
EVBUFFER_UNLOCK(buffer);
return e;
}
@@ -2916,7 +3222,7 @@ evbuffer_remove_cb_entry(struct evbuffer *buffer,
struct evbuffer_cb_entry *ent)
{
EVBUFFER_LOCK(buffer);
- TAILQ_REMOVE(&buffer->callbacks, ent, next);
+ LIST_REMOVE(ent, next);
EVBUFFER_UNLOCK(buffer);
mm_free(ent);
return 0;
@@ -2928,7 +3234,7 @@ evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
struct evbuffer_cb_entry *cbent;
int result = -1;
EVBUFFER_LOCK(buffer);
- TAILQ_FOREACH(cbent, &buffer->callbacks, next) {
+ LIST_FOREACH(cbent, &buffer->callbacks, next) {
if (cb == cbent->cb.cb_func && cbarg == cbent->cbarg) {
result = evbuffer_remove_cb_entry(buffer, cbent);
goto done;
@@ -3013,50 +3319,3 @@ evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb)
}
#endif
-/* These hooks are exposed so that the unit tests can temporarily disable
- * sendfile support in order to test mmap, or both to test linear
- * access. Don't use it; if we need to add a way to disable sendfile support
- * in the future, it will probably be via an alternate version of
- * evbuffer_add_file() with a 'flags' argument.
- */
-int _evbuffer_testing_use_sendfile(void);
-int _evbuffer_testing_use_mmap(void);
-int _evbuffer_testing_use_linear_file_access(void);
-
-int
-_evbuffer_testing_use_sendfile(void)
-{
- int ok = 0;
-#ifdef USE_SENDFILE
- use_sendfile = 1;
- ok = 1;
-#endif
-#ifdef _EVENT_HAVE_MMAP
- use_mmap = 0;
-#endif
- return ok;
-}
-int
-_evbuffer_testing_use_mmap(void)
-{
- int ok = 0;
-#ifdef USE_SENDFILE
- use_sendfile = 0;
-#endif
-#ifdef _EVENT_HAVE_MMAP
- use_mmap = 1;
- ok = 1;
-#endif
- return ok;
-}
-int
-_evbuffer_testing_use_linear_file_access(void)
-{
-#ifdef USE_SENDFILE
- use_sendfile = 0;
-#endif
-#ifdef _EVENT_HAVE_MMAP
- use_mmap = 0;
-#endif
- return 1;
-}
diff --git a/buffer_iocp.c b/buffer_iocp.c
index 5d5303eb..7d5f230d 100644
--- a/buffer_iocp.c
+++ b/buffer_iocp.c
@@ -30,12 +30,13 @@
This module implements overlapped read and write functions for evbuffer
objects on Windows.
*/
+#include "event2/event-config.h"
+#include "evconfig-private.h"
#include "event2/buffer.h"
#include "event2/buffer_compat.h"
#include "event2/util.h"
#include "event2/thread.h"
-#include "event2/event-config.h"
#include "util-internal.h"
#include "evthread-internal.h"
#include "evbuffer-internal.h"
@@ -156,7 +157,7 @@ evbuffer_overlapped_new(evutil_socket_t fd)
if (!evo)
return NULL;
- TAILQ_INIT(&evo->buffer.callbacks);
+ LIST_INIT(&evo->buffer.callbacks);
evo->buffer.refcnt = 1;
evo->buffer.last_with_datap = &evo->buffer.first;
diff --git a/bufferevent-internal.h b/bufferevent-internal.h
index 8348ab7a..e442e168 100644
--- a/bufferevent-internal.h
+++ b/bufferevent-internal.h
@@ -31,6 +31,7 @@ extern "C" {
#endif
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include "event2/util.h"
#include "defer-internal.h"
#include "evthread-internal.h"
@@ -65,7 +66,7 @@ typedef ev_uint16_t bufferevent_suspend_flags;
struct bufferevent_rate_limit_group {
/** List of all members in the group */
- TAILQ_HEAD(rlim_group_member_list, bufferevent_private) members;
+ LIST_HEAD(rlim_group_member_list, bufferevent_private) members;
/** Current limits for the group. */
struct ev_token_bucket rate_limit;
struct ev_token_bucket_cfg rate_limit_cfg;
@@ -116,7 +117,7 @@ struct bufferevent_rate_limit {
*
* Note that this field is supposed to be protected by the group
* lock */
- TAILQ_ENTRY(bufferevent_private) next_in_group;
+ LIST_ENTRY(bufferevent_private) next_in_group;
/** The rate-limiting group for this bufferevent, or NULL if it is
* only rate-limited on its own. */
struct bufferevent_rate_limit_group *group;
@@ -189,6 +190,14 @@ struct bufferevent_private {
* If NULL, locking is disabled. */
void *lock;
+ /** No matter how big our bucket gets, don't try to read more than this
+ * much in a single read operation. */
+ ev_ssize_t max_single_read;
+
+ /** No matter how big our bucket gets, don't try to write more than this
+ * much in a single write operation. */
+ ev_ssize_t max_single_write;
+
/** Rate-limiting information for this bufferevent */
struct bufferevent_rate_limit *rate_limiting;
};
@@ -262,7 +271,7 @@ extern const struct bufferevent_ops bufferevent_ops_pair;
#define BEV_IS_FILTER(bevp) ((bevp)->be_ops == &bufferevent_ops_filter)
#define BEV_IS_PAIR(bevp) ((bevp)->be_ops == &bufferevent_ops_pair)
-#ifdef WIN32
+#ifdef _WIN32
extern const struct bufferevent_ops bufferevent_ops_async;
#define BEV_IS_ASYNC(bevp) ((bevp)->be_ops == &bufferevent_ops_async)
#else
@@ -402,6 +411,8 @@ int _bufferevent_decrement_read_buckets(struct bufferevent_private *bev,
ev_ssize_t _bufferevent_get_read_max(struct bufferevent_private *bev);
ev_ssize_t _bufferevent_get_write_max(struct bufferevent_private *bev);
+int _bufferevent_ratelim_init(struct bufferevent_private *bev);
+
#ifdef __cplusplus
}
#endif
diff --git a/bufferevent.c b/bufferevent.c
index 45a1437e..98799704 100644
--- a/bufferevent.c
+++ b/bufferevent.c
@@ -25,9 +25,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/types.h>
-
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -41,7 +42,7 @@
#include <stdarg.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
#include <errno.h>
@@ -297,6 +298,8 @@ bufferevent_init_common(struct bufferevent_private *bufev_private,
bufev->be_ops = ops;
+ _bufferevent_ratelim_init(bufev_private);
+
/*
* Set to EV_WRITE so that using bufferevent_write is going to
* trigger a callback. Reading needs to be explicitly enabled
diff --git a/bufferevent_async.c b/bufferevent_async.c
index f34ca5f1..b28e4b62 100644
--- a/bufferevent_async.c
+++ b/bufferevent_async.c
@@ -27,6 +27,7 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -43,7 +44,7 @@
#include <unistd.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
diff --git a/bufferevent_filter.c b/bufferevent_filter.c
index 557f8cce..bc70f8eb 100644
--- a/bufferevent_filter.c
+++ b/bufferevent_filter.c
@@ -26,6 +26,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "evconfig-private.h"
+
#include <sys/types.h>
#include "event2/event-config.h"
@@ -42,7 +44,7 @@
#include <stdarg.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c
index 412c08e5..090927da 100644
--- a/bufferevent_openssl.c
+++ b/bufferevent_openssl.c
@@ -24,9 +24,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/types.h>
-
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -43,7 +44,7 @@
#include <unistd.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
@@ -312,7 +313,7 @@ struct bufferevent_openssl {
unsigned read_blocked_on_write : 1;
/* When we next get data, we should say "write" instead of "read". */
unsigned write_blocked_on_read : 1;
- /* XXX */
+ /* Treat TCP close before SSL close on SSL >= v3 as clean EOF. */
unsigned allow_dirty_shutdown : 1;
/* XXXX */
unsigned fd_is_set : 1;
@@ -482,7 +483,7 @@ clear_wbor(struct bufferevent_openssl *bev_ssl)
}
static void
-conn_closed(struct bufferevent_openssl *bev_ssl, int errcode, int ret)
+conn_closed(struct bufferevent_openssl *bev_ssl, int when, int errcode, int ret)
{
int event = BEV_EVENT_ERROR;
int dirty_shutdown = 0;
@@ -528,6 +529,8 @@ conn_closed(struct bufferevent_openssl *bev_ssl, int errcode, int ret)
stop_reading(bev_ssl);
stop_writing(bev_ssl);
+ /* when is BEV_EVENT_{READING|WRITING} */
+ event = when | event;
_bufferevent_run_eventcb(&bev_ssl->bev.bev, event);
}
@@ -603,7 +606,7 @@ do_read(struct bufferevent_openssl *bev_ssl, int n_to_read)
return -1;
break;
default:
- conn_closed(bev_ssl, err, r);
+ conn_closed(bev_ssl, BEV_EVENT_READING, err, r);
break;
}
blocked = 1;
@@ -681,7 +684,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost)
bev_ssl->last_write = space[i].iov_len;
break;
default:
- conn_closed(bev_ssl, err, r);
+ conn_closed(bev_ssl, BEV_EVENT_WRITING, err, r);
bev_ssl->last_write = -1;
break;
}
@@ -995,7 +998,7 @@ do_handshake(struct bufferevent_openssl *bev_ssl)
}
return 0;
default:
- conn_closed(bev_ssl, err, r);
+ conn_closed(bev_ssl, BEV_EVENT_READING, err, r);
return -1;
}
}
@@ -1403,6 +1406,31 @@ bufferevent_openssl_socket_new(struct event_base *base,
base, NULL, fd, ssl, state, options);
}
+int
+bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev)
+{
+ int allow_dirty_shutdown = -1;
+ struct bufferevent_openssl *bev_ssl;
+ BEV_LOCK(bev);
+ bev_ssl = upcast(bev);
+ if (bev_ssl)
+ allow_dirty_shutdown = bev_ssl->allow_dirty_shutdown;
+ BEV_UNLOCK(bev);
+ return allow_dirty_shutdown;
+}
+
+void
+bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev,
+ int allow_dirty_shutdown)
+{
+ struct bufferevent_openssl *bev_ssl;
+ BEV_LOCK(bev);
+ bev_ssl = upcast(bev);
+ if (bev_ssl)
+ bev_ssl->allow_dirty_shutdown = !!allow_dirty_shutdown;
+ BEV_UNLOCK(bev);
+}
+
unsigned long
bufferevent_get_openssl_error(struct bufferevent *bev)
{
diff --git a/bufferevent_pair.c b/bufferevent_pair.c
index 6ec5cac8..6d370733 100644
--- a/bufferevent_pair.c
+++ b/bufferevent_pair.c
@@ -23,15 +23,15 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <sys/types.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
-#include "event2/event-config.h"
-
#include "event2/util.h"
#include "event2/buffer.h"
#include "event2/bufferevent.h"
diff --git a/bufferevent_ratelim.c b/bufferevent_ratelim.c
index 7965390e..40586473 100644
--- a/bufferevent_ratelim.c
+++ b/bufferevent_ratelim.c
@@ -25,6 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "evconfig-private.h"
#include <sys/types.h>
#include <limits.h>
@@ -177,12 +178,9 @@ ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg)
mm_free(cfg);
}
-/* No matter how big our bucket gets, don't try to read more than this
- * much in a single read operation. */
-#define MAX_TO_READ_EVER 16384
-/* No matter how big our bucket gets, don't try to write more than this
- * much in a single write operation. */
-#define MAX_TO_WRITE_EVER 16384
+/* Default values for max_single_read & max_single_write variables. */
+#define MAX_SINGLE_READ_DEFAULT 16384
+#define MAX_SINGLE_WRITE_DEFAULT 16384
#define LOCK_GROUP(g) EVLOCK_LOCK((g)->lock, 0)
#define UNLOCK_GROUP(g) EVLOCK_UNLOCK((g)->lock, 0)
@@ -200,7 +198,7 @@ static inline ev_ssize_t
_bufferevent_get_rlim_max(struct bufferevent_private *bev, int is_write)
{
/* needs lock on bev. */
- ev_ssize_t max_so_far = is_write?MAX_TO_WRITE_EVER:MAX_TO_READ_EVER;
+ ev_ssize_t max_so_far = is_write?bev->max_single_write:bev->max_single_read;
#define LIM(x) \
(is_write ? (x).write_limit : (x).read_limit)
@@ -362,7 +360,7 @@ _bev_group_suspend_reading(struct bufferevent_rate_limit_group *g)
bufferevent, it will find out later when it looks at its limit
and sees that its group is suspended.
*/
- TAILQ_FOREACH(bev, &g->members, rate_limiting->next_in_group) {
+ LIST_FOREACH(bev, &g->members, rate_limiting->next_in_group) {
if (EVLOCK_TRY_LOCK(bev->lock)) {
bufferevent_suspend_read(&bev->bev,
BEV_SUSPEND_BW_GROUP);
@@ -380,7 +378,7 @@ _bev_group_suspend_writing(struct bufferevent_rate_limit_group *g)
struct bufferevent_private *bev;
g->write_suspended = 1;
g->pending_unsuspend_write = 0;
- TAILQ_FOREACH(bev, &g->members, rate_limiting->next_in_group) {
+ LIST_FOREACH(bev, &g->members, rate_limiting->next_in_group) {
if (EVLOCK_TRY_LOCK(bev->lock)) {
bufferevent_suspend_write(&bev->bev,
BEV_SUSPEND_BW_GROUP);
@@ -452,13 +450,13 @@ _bev_group_random_element(struct bufferevent_rate_limit_group *group)
if (!group->n_members)
return NULL;
- EVUTIL_ASSERT(! TAILQ_EMPTY(&group->members));
+ EVUTIL_ASSERT(! LIST_EMPTY(&group->members));
which = _evutil_weakrand() % group->n_members;
- bev = TAILQ_FIRST(&group->members);
+ bev = LIST_FIRST(&group->members);
while (which--)
- bev = TAILQ_NEXT(bev, rate_limiting->next_in_group);
+ bev = LIST_NEXT(bev, rate_limiting->next_in_group);
return bev;
}
@@ -473,12 +471,12 @@ _bev_group_random_element(struct bufferevent_rate_limit_group *group)
#define FOREACH_RANDOM_ORDER(block) \
do { \
first = _bev_group_random_element(g); \
- for (bev = first; bev != TAILQ_END(&g->members); \
- bev = TAILQ_NEXT(bev, rate_limiting->next_in_group)) { \
+ for (bev = first; bev != LIST_END(&g->members); \
+ bev = LIST_NEXT(bev, rate_limiting->next_in_group)) { \
block ; \
} \
- for (bev = TAILQ_FIRST(&g->members); bev && bev != first; \
- bev = TAILQ_NEXT(bev, rate_limiting->next_in_group)) { \
+ for (bev = LIST_FIRST(&g->members); bev && bev != first; \
+ bev = LIST_NEXT(bev, rate_limiting->next_in_group)) { \
block ; \
} \
} while (0)
@@ -649,7 +647,7 @@ bufferevent_rate_limit_group_new(struct event_base *base,
if (!g)
return NULL;
memcpy(&g->rate_limit_cfg, cfg, sizeof(g->rate_limit_cfg));
- TAILQ_INIT(&g->members);
+ LIST_INIT(&g->members);
ev_token_bucket_init(&g->rate_limit, cfg, tick, 0);
@@ -759,7 +757,7 @@ bufferevent_add_to_rate_limit_group(struct bufferevent *bev,
LOCK_GROUP(g);
bevp->rate_limiting->group = g;
++g->n_members;
- TAILQ_INSERT_TAIL(&g->members, bevp, rate_limiting->next_in_group);
+ LIST_INSERT_HEAD(&g->members, bevp, rate_limiting->next_in_group);
rsuspend = g->read_suspended;
wsuspend = g->write_suspended;
@@ -794,7 +792,7 @@ bufferevent_remove_from_rate_limit_group_internal(struct bufferevent *bev,
LOCK_GROUP(g);
bevp->rate_limiting->group = NULL;
--g->n_members;
- TAILQ_REMOVE(&g->members, bevp, rate_limiting->next_in_group);
+ LIST_REMOVE(bevp, rate_limiting->next_in_group);
UNLOCK_GROUP(g);
}
if (unsuspend) {
@@ -850,6 +848,56 @@ bufferevent_get_write_limit(struct bufferevent *bev)
return r;
}
+int
+bufferevent_set_max_single_read(struct bufferevent *bev, size_t size)
+{
+ struct bufferevent_private *bevp;
+ BEV_LOCK(bev);
+ bevp = BEV_UPCAST(bev);
+ if (size == 0 || size > EV_SSIZE_MAX)
+ bevp->max_single_read = MAX_SINGLE_READ_DEFAULT;
+ else
+ bevp->max_single_read = size;
+ BEV_UNLOCK(bev);
+ return 0;
+}
+
+int
+bufferevent_set_max_single_write(struct bufferevent *bev, size_t size)
+{
+ struct bufferevent_private *bevp;
+ BEV_LOCK(bev);
+ bevp = BEV_UPCAST(bev);
+ if (size == 0 || size > EV_SSIZE_MAX)
+ bevp->max_single_write = MAX_SINGLE_WRITE_DEFAULT;
+ else
+ bevp->max_single_write = size;
+ BEV_UNLOCK(bev);
+ return 0;
+}
+
+ev_ssize_t
+bufferevent_get_max_single_read(struct bufferevent *bev)
+{
+ ev_ssize_t r;
+
+ BEV_LOCK(bev);
+ r = BEV_UPCAST(bev)->max_single_read;
+ BEV_UNLOCK(bev);
+ return r;
+}
+
+ev_ssize_t
+bufferevent_get_max_single_write(struct bufferevent *bev)
+{
+ ev_ssize_t r;
+
+ BEV_LOCK(bev);
+ r = BEV_UPCAST(bev)->max_single_write;
+ BEV_UNLOCK(bev);
+ return r;
+}
+
ev_ssize_t
bufferevent_get_max_to_read(struct bufferevent *bev)
{
@@ -1009,3 +1057,13 @@ bufferevent_rate_limit_group_reset_totals(struct bufferevent_rate_limit_group *g
{
grp->total_read = grp->total_written = 0;
}
+
+int
+_bufferevent_ratelim_init(struct bufferevent_private *bev)
+{
+ bev->rate_limiting = NULL;
+ bev->max_single_read = MAX_SINGLE_READ_DEFAULT;
+ bev->max_single_write = MAX_SINGLE_WRITE_DEFAULT;
+
+ return 0;
+}
diff --git a/bufferevent_sock.c b/bufferevent_sock.c
index 9050eda2..bf9e35e0 100644
--- a/bufferevent_sock.c
+++ b/bufferevent_sock.c
@@ -26,9 +26,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/types.h>
-
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -45,7 +46,7 @@
#include <unistd.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
@@ -70,7 +71,7 @@
#include "mm-internal.h"
#include "bufferevent-internal.h"
#include "util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "iocp-internal.h"
#endif
@@ -232,7 +233,7 @@ bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
goto done;
} else {
connected = 1;
-#ifdef WIN32
+#ifdef _WIN32
if (BEV_IS_ASYNC(bufev)) {
event_del(&bufev->ev_write);
bufferevent_async_set_connected(bufev);
@@ -314,7 +315,7 @@ bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
struct bufferevent_private *bufev_p;
struct bufferevent *bufev;
-#ifdef WIN32
+#ifdef _WIN32
if (base && event_base_get_iocp(base))
return bufferevent_async_new(base, fd, options);
#endif
@@ -372,7 +373,7 @@ bufferevent_socket_connect(struct bufferevent *bev,
ownfd = 1;
}
if (sa) {
-#ifdef WIN32
+#ifdef _WIN32
if (bufferevent_async_can_connect(bev)) {
bufferevent_setfd(bev, fd);
r = bufferevent_async_connect(bev, fd, sa, socklen);
@@ -387,7 +388,7 @@ bufferevent_socket_connect(struct bufferevent *bev,
if (r < 0)
goto freesock;
}
-#ifdef WIN32
+#ifdef _WIN32
/* ConnectEx() isn't always around, even when IOCP is enabled.
* Here, we borrow the socket object's write handler to fall back
* on a non-blocking connect() when ConnectEx() is unavailable. */
diff --git a/compat/sys/queue.h b/compat/sys/queue.h
index 53dd10d9..6a9c581b 100644
--- a/compat/sys/queue.h
+++ b/compat/sys/queue.h
@@ -93,7 +93,7 @@ struct name { \
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
-#ifndef WIN32
+#ifndef _WIN32
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
diff --git a/configure.in b/configure.in
index d61390bd..0e2770fe 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,3 @@
-dnl configure.in for libevent
dnl Copyright 2000-2007 Niels Provos
dnl Copyright 2007-2012 Niels Provos and Nick Mathewson
dnl
@@ -6,38 +5,49 @@ dnl See LICENSE for copying information.
dnl
dnl Original version Dug Song <dugsong@monkey.org>
-AC_PREREQ(2.59c)
+AC_PREREQ(2.59)
AC_INIT(event.c)
AC_CONFIG_MACRO_DIR([m4])
-AM_INIT_AUTOMAKE(libevent,2.0.17-stable)
-AM_CONFIG_HEADER(config.h)
-AC_DEFINE(NUMERIC_VERSION, 0x02001100, [Numeric representation of the version])
+AM_INIT_AUTOMAKE(libevent,2.1.0-alpha-dev)
+dnl AM_SILENT_RULES req. automake 1.11. [no] defaults V=1
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([no])])
+AM_CONFIG_HEADER(config.h evconfig-private.h:evconfig-private.h.in)
+AC_DEFINE(NUMERIC_VERSION, 0x02010001, [Numeric representation of the version])
dnl Initialize prefix.
if test "$prefix" = "NONE"; then
prefix="/usr/local"
fi
+dnl Try and get a full POSIX environment on obscure systems
+ifdef([AC_USE_SYSTEM_EXTENSIONS], [
+AC_USE_SYSTEM_EXTENSIONS
+], [
+AC_AIX
+AC_GNU_SOURCE
+AC_MINIX
+])
+
AC_CANONICAL_BUILD
AC_CANONICAL_HOST
dnl the 'build' machine is where we run configure and compile
dnl the 'host' machine is where the resulting stuff runs.
-case "$host_os" in
-
- osf5*)
- CFLAGS="$CFLAGS -D_OSF_SOURCE"
- ;;
-esac
+#case "$host_os" in
+#
+# osf5*)
+# CFLAGS="$CFLAGS -D_OSF_SOURCE"
+# ;;
+#esac
dnl Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_INSTALL
AC_PROG_LN_S
-AC_PROG_MKDIR_P
+# AC_PROG_MKDIR_P - $(MKDIR_P) should be defined by AM_INIT_AUTOMAKE
AC_PROG_GCC_TRADITIONAL
@@ -59,7 +69,16 @@ case "$host_os" in
esac
AC_ARG_ENABLE(gcc-warnings,
- AS_HELP_STRING(--enable-gcc-warnings, enable verbose warnings with GCC))
+ AS_HELP_STRING(--disable-gcc-warnings, disable verbose warnings with GCC))
+
+AC_ARG_ENABLE(gcc-hardening,
+ AS_HELP_STRING(--enable-gcc-hardening, enable compiler security checks),
+[if test x$enableval = xyes; then
+ CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2 -fstack-protector-all"
+ CFLAGS="$CFLAGS -fwrapv -fPIE -Wstack-protector"
+ CFLAGS="$CFLAGS --param ssp-buffer-size=1"
+fi])
+
AC_ARG_ENABLE(thread-support,
AS_HELP_STRING(--disable-thread-support, disable support for threading),
[], [enable_thread_support=yes])
@@ -81,6 +100,9 @@ AC_ARG_ENABLE([libevent-regress],
AC_ARG_ENABLE([function-sections],
AS_HELP_STRING([--enable-function-sections, make static library allow smaller binaries with --gc-sections]),
[], [enable_function_sections=no])
+AC_ARG_ENABLE([verbose-debug],
+ AS_HELP_STRING([--enable-verbose-debug, verbose debug logging]),
+ [], [enable_verbose_debug=no])
AC_PROG_LIBTOOL
@@ -100,12 +122,12 @@ AC_SEARCH_LIBS([inet_aton], [resolv])
AC_SEARCH_LIBS([clock_gettime], [rt])
AC_SEARCH_LIBS([sendfile], [sendfile])
-dnl - check if the macro WIN32 is defined on this compiler.
-dnl - (this is how we check for a windows version of GCC)
+dnl - check if the macro _WIN32 is defined on this compiler.
+dnl - (this is how we check for a windows compiler)
AC_MSG_CHECKING(for WIN32)
AC_TRY_COMPILE(,
[
-#ifndef WIN32
+#ifndef _WIN32
die horribly
#endif
],
@@ -155,26 +177,41 @@ fi
AC_SUBST(EV_LIB_WS32)
AC_SUBST(EV_LIB_GDI)
-AC_CHECK_HEADERS([openssl/bio.h])
+AC_SYS_LARGEFILE
-if test "$enable_openssl" = "yes"; then
-save_LIBS="$LIBS"
-LIBS=""
-OPENSSL_LIBS=""
-have_openssl=no
-AC_SEARCH_LIBS([SSL_new], [ssl],
- [have_openssl=yes
- OPENSSL_LIBS="$LIBS -lcrypto $EV_LIB_GDI $EV_LIB_WS32"
- AC_DEFINE(HAVE_OPENSSL, 1, [Define if the system has openssl])],
- [have_openssl=no],
- [-lcrypto $EV_LIB_GDI $EV_LIB_WS32])
-LIBS="$save_LIBS"
-AC_SUBST(OPENSSL_LIBS)
-fi
+LIBEVENT_OPENSSL
dnl Checks for header files.
AC_HEADER_STDC
-AC_CHECK_HEADERS([fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h sys/wait.h netdb.h])
+AC_CHECK_HEADERS([ \
+ arpa/inet.h \
+ fcntl.h \
+ ifaddrs.h \
+ inttypes.h \
+ netdb.h \
+ netinet/in.h \
+ netinet/in6.h \
+ poll.h \
+ port.h \
+ stdarg.h \
+ stddef.h \
+ stdint.h \
+ sys/devpoll.h \
+ sys/epoll.h \
+ sys/event.h \
+ sys/eventfd.h \
+ sys/ioctl.h \
+ sys/mman.h \
+ sys/param.h \
+ sys/queue.h \
+ sys/select.h \
+ sys/sendfile.h \
+ sys/socket.h \
+ sys/time.h \
+ sys/uio.h \
+ sys/wait.h \
+ unistd.h \
+])
AC_CHECK_HEADERS(sys/sysctl.h, [], [], [
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
@@ -272,8 +309,40 @@ AC_C_INLINE
AC_HEADER_TIME
dnl Checks for library functions.
-AC_CHECK_FUNCS([gettimeofday vasprintf fcntl clock_gettime strtok_r strsep])
-AC_CHECK_FUNCS([getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random arc4random_buf issetugid geteuid getegid getprotobynumber setenv unsetenv putenv sysctl])
+AC_CHECK_FUNCS([ \
+ arc4random \
+ arc4random_buf \
+ clock_gettime \
+ eventfd \
+ fcntl \
+ getegid \
+ geteuid \
+ getifaddrs \
+ getnameinfo \
+ getprotobynumber \
+ gettimeofday \
+ inet_aton \
+ inet_ntop \
+ inet_pton \
+ issetugid \
+ mmap \
+ nanosleep \
+ pipe \
+ putenv \
+ sendfile \
+ setenv \
+ sigaction \
+ signal \
+ splice \
+ strlcpy \
+ strsep \
+ strtok_r \
+ strtoll \
+ sysctl \
+ unsetenv \
+ usleep \
+ vasprintf \
+])
AC_CACHE_CHECK(
[for getaddrinfo],
@@ -540,6 +609,7 @@ AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(size_t)
AC_CHECK_SIZEOF(void *)
+AC_CHECK_SIZEOF(off_t)
AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t, struct addrinfo, struct sockaddr_storage], , ,
[#define _GNU_SOURCE
@@ -556,7 +626,7 @@ AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t, struct addrin
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_WINNT 0x400
#define _WIN32_WINNT 0x400
#define WIN32_LEAN_AND_MEAN
@@ -579,7 +649,7 @@ AC_CHECK_MEMBERS([struct in6_addr.s6_addr32, struct in6_addr.s6_addr16, struct s
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_WINNT 0x400
#define _WIN32_WINNT 0x400
#define WIN32_LEAN_AND_MEAN
@@ -652,12 +722,17 @@ if test x$enable_debug_mode = xno; then
[Define if libevent should build without support for a debug mode])
fi
+# check if we should enable verbose debugging
+if test x$enable_verbose_debug = xyes; then
+ CFLAGS="$CFLAGS -DUSE_DEBUG"
+fi
+
# check if we have and should use openssl
AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"])
# Add some more warnings which we use in development but not in the
# released versions. (Some relevant gcc versions can't handle these.)
-if test x$enable_gcc_warnings = xyes && test "$GCC" = "yes"; then
+if test x$enable_gcc_warnings != xno && test "$GCC" = "yes"; then
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
#if !defined(__GNUC__) || (__GNUC__ < 4)
@@ -684,7 +759,11 @@ if test x$enable_gcc_warnings = xyes && test "$GCC" = "yes"; then
#error
#endif])], have_clang29orlower=yes, have_clang29orlower=no)
- CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat -Wwrite-strings -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wbad-function-cast -Wswitch-enum -Werror"
+ CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat -Wwrite-strings -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wbad-function-cast -Wswitch-enum"
+ if test x$enable_gcc_warnings = xyes; then
+ CFLAGS="$CFLAGS -Werror"
+ fi
+
CFLAGS="$CFLAGS -Wno-unused-parameter -Wstrict-aliasing"
if test x$have_gcc4 = xyes ; then
diff --git a/defer-internal.h b/defer-internal.h
index 96a5cacc..d8443925 100644
--- a/defer-internal.h
+++ b/defer-internal.h
@@ -31,6 +31,8 @@ extern "C" {
#endif
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
#include <sys/queue.h>
struct deferred_cb;
@@ -55,6 +57,10 @@ struct deferred_cb_queue {
/** Lock used to protect the queue. */
void *lock;
+ /** Which event_base does this queue associate itself with?
+ * (Used for timing) */
+ struct event_base *base;
+
/** How many entries are in the queue? */
int active_count;
diff --git a/devpoll.c b/devpoll.c
index 0c7214b7..c3f389f5 100644
--- a/devpoll.c
+++ b/devpoll.c
@@ -25,6 +25,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <sys/types.h>
#include <sys/resource.h>
diff --git a/epoll.c b/epoll.c
index 4c5c3da0..69d02885 100644
--- a/epoll.c
+++ b/epoll.c
@@ -25,6 +25,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <stdint.h>
#include <sys/types.h>
@@ -170,6 +171,171 @@ epoll_op_to_string(int op)
"???";
}
+/*
+ Here are the values we're masking off to decide what operations to do.
+ Note that since EV_READ|EV_WRITE.
+
+ Note also that this table is a little sparse, since ADD+DEL is
+ nonsensical ("xxx" in the list below.)
+
+ Note also also that we are shifting old_events by only 3 bits, since
+ EV_READ is 2 and EV_WRITE is 4.
+
+ The table was auto-generated with a python script, according to this
+ pseudocode:
+
+ If either the read or the write change is add+del:
+ This is impossible; Set op==-1, events=0.
+ Else, if either the read or the write change is add:
+ Set events to 0.
+ If the read change is add, or
+ (the read change is not del, and ev_read is in old_events):
+ Add EPOLLIN to events.
+ If the write change is add, or
+ (the write change is not del, and ev_write is in old_events):
+ Add EPOLLOUT to events.
+
+ If old_events is set:
+ Set op to EPOLL_CTL_MOD [*1,*2]
+ Else:
+ Set op to EPOLL_CTL_ADD [*3]
+
+ Else, if the read or the write change is del:
+ Set op to EPOLL_CTL_DEL.
+ If the read change is del:
+ If the write change is del:
+ Set events to EPOLLIN|EPOLLOUT
+ Else if ev_write is in old_events:
+ Set events to EPOLLOUT
+ Set op to EPOLL_CTL_MOD
+ Else
+ Set events to EPOLLIN
+ Else:
+ {The write change is del.}
+ If ev_read is in old_events:
+ Set events to EPOLLIN
+ Set op to EPOLL_CTL_MOD
+ Else:
+ Set the events to EPOLLOUT
+
+ Else:
+ There is no read or write change; set op to 0 and events to 0.
+
+ The logic is a little tricky, since we had no events set on the fd before,
+ we need to set op="ADD" and set events=the events we want to add. If we
+ had any events set on the fd before, and we want any events to remain on
+ the fd, we need to say op="MOD" and set events=the events we want to
+ remain. But if we want to delete the last event, we say op="DEL" and
+ set events=(any non-null pointer).
+
+ [*1] This MOD is only a guess. MOD might fail with ENOENT if the file was
+ closed and a new file was opened with the same fd. If so, we'll retry
+ with ADD.
+
+ [*2] We can't replace this with a no-op even if old_events is the same as
+ the new events: if the file was closed and reopened, we need to retry
+ with an ADD. (We do a MOD in this case since "no change" is more
+ common than "close and reopen", so we'll usually wind up doing 1
+ syscalls instead of 2.)
+
+ [*3] This ADD is only a guess. There is a fun Linux kernel issue where if
+ you have two fds for the same file (via dup) and you ADD one to an
+ epfd, then close it, then re-create it with the same fd (via dup2 or an
+ unlucky dup), then try to ADD it again, you'll get an EEXIST, since the
+ struct epitem is not actually removed from the struct eventpoll until
+ the file itself is closed.
+
+ EV_CHANGE_ADD==1
+ EV_CHANGE_DEL==2
+ EV_READ ==2
+ EV_WRITE ==4
+ Bit 0: read change is add
+ Bit 1: read change is del
+ Bit 2: write change is add
+ Bit 3: write change is del
+ Bit 4: old events had EV_READ
+ Bit 5: old events had EV_WRITE
+*/
+
+#define INDEX(c) \
+ ( (((c)->read_change&(EV_CHANGE_ADD|EV_CHANGE_DEL))) | \
+ (((c)->write_change&(EV_CHANGE_ADD|EV_CHANGE_DEL)) << 2) | \
+ (((c)->old_events&(EV_READ|EV_WRITE)) << 3) )
+
+#if EV_READ != 2 || EV_WRITE != 4 || EV_CHANGE_ADD != 1 || EV_CHANGE_DEL != 2
+#error "Libevent's internals changed! Regenerate the op_table in epoll.c"
+#endif
+
+static const struct operation {
+ int events;
+ int op;
+} op_table[] = {
+ { 0, 0 }, /* old= 0, write: 0, read: 0 */
+ { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write: 0, read:add */
+ { EPOLLIN, EPOLL_CTL_DEL }, /* old= 0, write: 0, read:del */
+ { 0, -1 }, /* old= 0, write: 0, read:xxx */
+ { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read: 0 */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_ADD },/* old= 0, write:add, read:add */
+ { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read:del */
+ { 0, -1 }, /* old= 0, write:add, read:xxx */
+ { EPOLLOUT, EPOLL_CTL_DEL }, /* old= 0, write:del, read: 0 */
+ { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write:del, read:add */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old= 0, write:del, read:del */
+ { 0, -1 }, /* old= 0, write:del, read:xxx */
+ { 0, -1 }, /* old= 0, write:xxx, read: 0 */
+ { 0, -1 }, /* old= 0, write:xxx, read:add */
+ { 0, -1 }, /* old= 0, write:xxx, read:del */
+ { 0, -1 }, /* old= 0, write:xxx, read:xxx */
+ { 0, 0 }, /* old= r, write: 0, read: 0 */
+ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write: 0, read:add */
+ { EPOLLIN, EPOLL_CTL_DEL }, /* old= r, write: 0, read:del */
+ { 0, -1 }, /* old= r, write: 0, read:xxx */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= r, write:add, read: 0 */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= r, write:add, read:add */
+ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read:del */
+ { 0, -1 }, /* old= r, write:add, read:xxx */
+ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read: 0 */
+ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read:add */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old= r, write:del, read:del */
+ { 0, -1 }, /* old= r, write:del, read:xxx */
+ { 0, -1 }, /* old= r, write:xxx, read: 0 */
+ { 0, -1 }, /* old= r, write:xxx, read:add */
+ { 0, -1 }, /* old= r, write:xxx, read:del */
+ { 0, -1 }, /* old= r, write:xxx, read:xxx */
+ { 0, 0 }, /* old= w, write: 0, read: 0 */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= w, write: 0, read:add */
+ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read:del */
+ { 0, -1 }, /* old= w, write: 0, read:xxx */
+ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read: 0 */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= w, write:add, read:add */
+ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read:del */
+ { 0, -1 }, /* old= w, write:add, read:xxx */
+ { EPOLLOUT, EPOLL_CTL_DEL }, /* old= w, write:del, read: 0 */
+ { EPOLLIN, EPOLL_CTL_MOD }, /* old= w, write:del, read:add */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old= w, write:del, read:del */
+ { 0, -1 }, /* old= w, write:del, read:xxx */
+ { 0, -1 }, /* old= w, write:xxx, read: 0 */
+ { 0, -1 }, /* old= w, write:xxx, read:add */
+ { 0, -1 }, /* old= w, write:xxx, read:del */
+ { 0, -1 }, /* old= w, write:xxx, read:xxx */
+ { 0, 0 }, /* old=rw, write: 0, read: 0 */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old=rw, write: 0, read:add */
+ { EPOLLOUT, EPOLL_CTL_MOD }, /* old=rw, write: 0, read:del */
+ { 0, -1 }, /* old=rw, write: 0, read:xxx */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old=rw, write:add, read: 0 */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old=rw, write:add, read:add */
+ { EPOLLOUT, EPOLL_CTL_MOD }, /* old=rw, write:add, read:del */
+ { 0, -1 }, /* old=rw, write:add, read:xxx */
+ { EPOLLIN, EPOLL_CTL_MOD }, /* old=rw, write:del, read: 0 */
+ { EPOLLIN, EPOLL_CTL_MOD }, /* old=rw, write:del, read:add */
+ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old=rw, write:del, read:del */
+ { 0, -1 }, /* old=rw, write:del, read:xxx */
+ { 0, -1 }, /* old=rw, write:xxx, read: 0 */
+ { 0, -1 }, /* old=rw, write:xxx, read:add */
+ { 0, -1 }, /* old=rw, write:xxx, read:del */
+ { 0, -1 }, /* old=rw, write:xxx, read:xxx */
+};
+
static int
epoll_apply_one_change(struct event_base *base,
struct epollop *epollop,
@@ -177,156 +343,101 @@ epoll_apply_one_change(struct event_base *base,
{
struct epoll_event epev;
int op, events = 0;
+ int idx;
- if (1) {
- /* The logic here is a little tricky. If we had no events set
- on the fd before, we need to set op="ADD" and set
- events=the events we want to add. If we had any events set
- on the fd before, and we want any events to remain on the
- fd, we need to say op="MOD" and set events=the events we
- want to remain. But if we want to delete the last event,
- we say op="DEL" and set events=the remaining events. What
- fun!
- */
-
- /* TODO: Turn this into a switch or a table lookup. */
-
- if ((ch->read_change & EV_CHANGE_ADD) ||
- (ch->write_change & EV_CHANGE_ADD)) {
- /* If we are adding anything at all, we'll want to do
- * either an ADD or a MOD. */
- events = 0;
- op = EPOLL_CTL_ADD;
- if (ch->read_change & EV_CHANGE_ADD) {
- events |= EPOLLIN;
- } else if (ch->read_change & EV_CHANGE_DEL) {
- ;
- } else if (ch->old_events & EV_READ) {
- events |= EPOLLIN;
- }
- if (ch->write_change & EV_CHANGE_ADD) {
- events |= EPOLLOUT;
- } else if (ch->write_change & EV_CHANGE_DEL) {
- ;
- } else if (ch->old_events & EV_WRITE) {
- events |= EPOLLOUT;
- }
- if ((ch->read_change|ch->write_change) & EV_ET)
- events |= EPOLLET;
-
- if (ch->old_events) {
- /* If MOD fails, we retry as an ADD, and if
- * ADD fails we will retry as a MOD. So the
- * only hard part here is to guess which one
- * will work. As a heuristic, we'll try
- * MOD first if we think there were old
- * events and ADD if we think there were none.
- *
- * We can be wrong about the MOD if the file
- * has in fact been closed and re-opened.
- *
- * We can be wrong about the ADD if the
- * the fd has been re-created with a dup()
- * of the same file that it was before.
- */
- op = EPOLL_CTL_MOD;
- }
- } else if ((ch->read_change & EV_CHANGE_DEL) ||
- (ch->write_change & EV_CHANGE_DEL)) {
- /* If we're deleting anything, we'll want to do a MOD
- * or a DEL. */
- op = EPOLL_CTL_DEL;
-
- if (ch->read_change & EV_CHANGE_DEL) {
- if (ch->write_change & EV_CHANGE_DEL) {
- events = EPOLLIN|EPOLLOUT;
- } else if (ch->old_events & EV_WRITE) {
- events = EPOLLOUT;
- op = EPOLL_CTL_MOD;
- } else {
- events = EPOLLIN;
- }
- } else if (ch->write_change & EV_CHANGE_DEL) {
- if (ch->old_events & EV_READ) {
- events = EPOLLIN;
- op = EPOLL_CTL_MOD;
- } else {
- events = EPOLLOUT;
- }
- }
- }
+ idx = INDEX(ch);
+ op = op_table[idx].op;
+ events = op_table[idx].events;
- if (!events)
- return 0;
+ if (!events) {
+ EVUTIL_ASSERT(op == 0);
+ return 0;
+ }
- memset(&epev, 0, sizeof(epev));
- epev.data.fd = ch->fd;
- epev.events = events;
- if (epoll_ctl(epollop->epfd, op, ch->fd, &epev) == -1) {
- if (op == EPOLL_CTL_MOD && errno == ENOENT) {
- /* If a MOD operation fails with ENOENT, the
- * fd was probably closed and re-opened. We
- * should retry the operation as an ADD.
- */
- if (epoll_ctl(epollop->epfd, EPOLL_CTL_ADD, ch->fd, &epev) == -1) {
- event_warn("Epoll MOD(%d) on %d retried as ADD; that failed too",
- (int)epev.events, ch->fd);
- return -1;
- } else {
- event_debug(("Epoll MOD(%d) on %d retried as ADD; succeeded.",
- (int)epev.events,
- ch->fd));
- }
- } else if (op == EPOLL_CTL_ADD && errno == EEXIST) {
- /* If an ADD operation fails with EEXIST,
- * either the operation was redundant (as with a
- * precautionary add), or we ran into a fun
- * kernel bug where using dup*() to duplicate the
- * same file into the same fd gives you the same epitem
- * rather than a fresh one. For the second case,
- * we must retry with MOD. */
- if (epoll_ctl(epollop->epfd, EPOLL_CTL_MOD, ch->fd, &epev) == -1) {
- event_warn("Epoll ADD(%d) on %d retried as MOD; that failed too",
- (int)epev.events, ch->fd);
- return -1;
- } else {
- event_debug(("Epoll ADD(%d) on %d retried as MOD; succeeded.",
- (int)epev.events,
- ch->fd));
- }
- } else if (op == EPOLL_CTL_DEL &&
- (errno == ENOENT || errno == EBADF ||
- errno == EPERM)) {
- /* If a delete fails with one of these errors,
- * that's fine too: we closed the fd before we
- * got around to calling epoll_dispatch. */
- event_debug(("Epoll DEL(%d) on fd %d gave %s: DEL was unnecessary.",
- (int)epev.events,
- ch->fd,
- strerror(errno)));
+ if ((ch->read_change|ch->write_change) & EV_CHANGE_ET)
+ events |= EPOLLET;
+
+ memset(&epev, 0, sizeof(epev));
+ epev.data.fd = ch->fd;
+ epev.events = events;
+ if (epoll_ctl(epollop->epfd, op, ch->fd, &epev) == 0) {
+ event_debug(("Epoll %s(%d) on fd %d okay. [old events were %d; read change was %d; write change was %d]",
+ epoll_op_to_string(op),
+ (int)epev.events,
+ (int)ch->fd,
+ ch->old_events,
+ ch->read_change,
+ ch->write_change));
+ return 0;
+ }
+
+ switch (op) {
+ case EPOLL_CTL_MOD:
+ if (errno == ENOENT) {
+ /* If a MOD operation fails with ENOENT, the
+ * fd was probably closed and re-opened. We
+ * should retry the operation as an ADD.
+ */
+ if (epoll_ctl(epollop->epfd, EPOLL_CTL_ADD, ch->fd, &epev) == -1) {
+ event_warn("Epoll MOD(%d) on %d retried as ADD; that failed too",
+ (int)epev.events, ch->fd);
+ return -1;
} else {
- event_warn("Epoll %s(%d) on fd %d failed. Old events were %d; read change was %d (%s); write change was %d (%s)",
- epoll_op_to_string(op),
- (int)epev.events,
- ch->fd,
- ch->old_events,
- ch->read_change,
- change_to_string(ch->read_change),
- ch->write_change,
- change_to_string(ch->write_change));
+ event_debug(("Epoll MOD(%d) on %d retried as ADD; succeeded.",
+ (int)epev.events,
+ ch->fd));
+ return 0;
+ }
+ }
+ break;
+ case EPOLL_CTL_ADD:
+ if (errno == EEXIST) {
+ /* If an ADD operation fails with EEXIST,
+ * either the operation was redundant (as with a
+ * precautionary add), or we ran into a fun
+ * kernel bug where using dup*() to duplicate the
+ * same file into the same fd gives you the same epitem
+ * rather than a fresh one. For the second case,
+ * we must retry with MOD. */
+ if (epoll_ctl(epollop->epfd, EPOLL_CTL_MOD, ch->fd, &epev) == -1) {
+ event_warn("Epoll ADD(%d) on %d retried as MOD; that failed too",
+ (int)epev.events, ch->fd);
return -1;
+ } else {
+ event_debug(("Epoll ADD(%d) on %d retried as MOD; succeeded.",
+ (int)epev.events,
+ ch->fd));
+ return 0;
}
- } else {
- event_debug(("Epoll %s(%d) on fd %d okay. [old events were %d; read change was %d; write change was %d]",
- epoll_op_to_string(op),
+ }
+ break;
+ case EPOLL_CTL_DEL:
+ if (errno == ENOENT || errno == EBADF || errno == EPERM) {
+ /* If a delete fails with one of these errors,
+ * that's fine too: we closed the fd before we
+ * got around to calling epoll_dispatch. */
+ event_debug(("Epoll DEL(%d) on fd %d gave %s: DEL was unnecessary.",
(int)epev.events,
- (int)ch->fd,
- ch->old_events,
- ch->read_change,
- ch->write_change));
+ ch->fd,
+ strerror(errno)));
+ return 0;
}
+ break;
+ default:
+ break;
}
- return 0;
+
+ event_warn("Epoll %s(%d) on fd %d failed. Old events were %d; read change was %d (%s); write change was %d (%s)",
+ epoll_op_to_string(op),
+ (int)epev.events,
+ ch->fd,
+ ch->old_events,
+ ch->read_change,
+ change_to_string(ch->read_change),
+ ch->write_change,
+ change_to_string(ch->write_change));
+
+ return -1;
}
static int
diff --git a/epoll_sub.c b/epoll_sub.c
index 3738b26b..af35daa7 100644
--- a/epoll_sub.c
+++ b/epoll_sub.c
@@ -24,6 +24,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "evconfig-private.h"
#include <stdint.h>
#include <sys/param.h>
diff --git a/evbuffer-internal.h b/evbuffer-internal.h
index e68a59df..bdbde89e 100644
--- a/evbuffer-internal.h
+++ b/evbuffer-internal.h
@@ -32,6 +32,7 @@ extern "C" {
#endif
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include "event2/util.h"
#include "util-internal.h"
#include "defer-internal.h"
@@ -41,7 +42,7 @@ extern "C" {
* arguments. */
#define EVBUFFER_CB_NODEFER 2
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
#include <sys/queue.h>
@@ -59,7 +60,7 @@ extern "C" {
* when bytes are added to or removed from the evbuffer. */
struct evbuffer_cb_entry {
/** Structures to implement a doubly-linked queue of callbacks */
- TAILQ_ENTRY(evbuffer_cb_entry) next;
+ LIST_ENTRY(evbuffer_cb_entry) next;
/** The callback function to invoke when this callback is called.
If EVBUFFER_CB_OBSOLETE is set in flags, the cb_obsolete field is
valid; otherwise, cb_func is valid. */
@@ -125,7 +126,7 @@ struct evbuffer {
* overflows when we have mutually recursive callbacks, and for
* serializing callbacks in a single thread. */
unsigned deferred_cbs : 1;
-#ifdef WIN32
+#ifdef _WIN32
/** True iff this buffer is set up for overlapped IO. */
unsigned is_overlapped : 1;
#endif
@@ -146,7 +147,7 @@ struct evbuffer {
struct deferred_cb deferred;
/** A doubly-linked-list of callback functions */
- TAILQ_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;
+ LIST_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;
/** The parent bufferevent object this evbuffer belongs to.
* NULL if the evbuffer stands alone. */
@@ -172,8 +173,8 @@ struct evbuffer_chain {
/** Set if special handling is required for this chain */
unsigned flags;
-#define EVBUFFER_MMAP 0x0001 /**< memory in buffer is mmaped */
-#define EVBUFFER_SENDFILE 0x0002 /**< a chain used for sendfile */
+#define EVBUFFER_FILESEGMENT 0x0001 /**< A chain used for a file segment */
+#define EVBUFFER_SENDFILE 0x0002 /**< a chain used with sendfile */
#define EVBUFFER_REFERENCE 0x0004 /**< a chain with a mem reference */
#define EVBUFFER_IMMUTABLE 0x0008 /**< read-only chain */
/** a chain that mustn't be reallocated or freed, or have its contents
@@ -184,6 +185,11 @@ struct evbuffer_chain {
/** a chain that should be freed, but can't be freed until it is
* un-pinned. */
#define EVBUFFER_DANGLING 0x0040
+ /** a chain that is a referenced copy of another chain */
+#define EVBUFFER_MULTICAST 0x0080
+
+ /** number of references to this chain */
+ int refcnt;
/** Usually points to the read-write memory belonging to this
* buffer allocated as part of the evbuffer_chain allocation.
@@ -194,21 +200,63 @@ struct evbuffer_chain {
unsigned char *buffer;
};
-/* this is currently used by both mmap and sendfile */
-/* TODO(niels): something strange needs to happen for Windows here, I am not
- * sure what that is, but it needs to get looked into.
- */
-struct evbuffer_chain_fd {
- int fd; /**< the fd associated with this chain */
-};
-
-/** callback for a reference buffer; lets us know what to do with it when
- * we're done with it. */
+/** callback for a reference chain; lets us know what to do with it when
+ * we're done with it. Lives at the end of an evbuffer_chain with the
+ * EVBUFFER_REFERENCE flag set */
struct evbuffer_chain_reference {
evbuffer_ref_cleanup_cb cleanupfn;
void *extra;
};
+/** File segment for a file-segment chain. Lives at the end of an
+ * evbuffer_chain with the EVBUFFER_FILESEGMENT flag set. */
+struct evbuffer_chain_file_segment {
+ struct evbuffer_file_segment *segment;
+#ifdef _WIN32
+ /** If we're using CreateFileMapping, this is the handle to the view. */
+ HANDLE view_handle;
+#endif
+};
+
+/* Declared in event2/buffer.h; defined here. */
+struct evbuffer_file_segment {
+ void *lock; /**< lock prevent concurrent access to refcnt */
+ int refcnt; /**< Reference count for this file segment */
+ unsigned flags; /**< combination of EVBUF_FS_* flags */
+
+ /** What kind of file segment is this? */
+ unsigned can_sendfile : 1;
+ unsigned is_mapping : 1;
+
+ /** The fd that we read the data from. */
+ int fd;
+ /** If we're using mmap, this is the raw mapped memory. */
+ void *mapping;
+#ifdef _WIN32
+ /** If we're using CreateFileMapping, this is the mapping */
+ HANDLE mapping_handle;
+#endif
+ /** If we're using mmap or IO, this is the content of the file
+ * segment. */
+ char *contents;
+ /** Position of this segment within the file. */
+ ev_off_t file_offset;
+ /** If we're using mmap, this is the offset within 'mapping' where
+ * this data segment begins. */
+ ev_off_t mmap_offset;
+ /** The length of this segment. */
+ ev_off_t length;
+};
+
+/** Information about the multicast parent of a chain. Lives at the
+ * end of an evbuffer_chain with the EVBUFFER_MULTICAST flag set. */
+struct evbuffer_multicast_parent {
+ /** source buffer the multicast parent belongs to */
+ struct evbuffer *source;
+ /** multicast parent for this chain */
+ struct evbuffer_chain *parent;
+};
+
#define EVBUFFER_CHAIN_SIZE sizeof(struct evbuffer_chain)
/** Return a pointer to extra data allocated along with an evbuffer. */
#define EVBUFFER_CHAIN_EXTRA(t, c) (t *)((struct evbuffer_chain *)(c) + 1)
diff --git a/evconfig-private.h.in b/evconfig-private.h.in
new file mode 100644
index 00000000..254ae509
--- /dev/null
+++ b/evconfig-private.h.in
@@ -0,0 +1,48 @@
+/* evconfig-private.h template - see "Configuration Header Templates" */
+/* in AC manual. Kevin Bowling <kevin.bowling@kev009.com */
+#ifndef _EVENT_EVCONFIG__PRIVATE_H
+#define _EVENT_EVCONFIG__PRIVATE_H
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#ifndef _MINIX
+#undef _MINIX
+#endif
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#ifndef _POSIX_1_SOURCE
+#undef _POSIX_1_SOURCE
+#endif
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#ifndef _POSIX_SOURCE
+#undef _POSIX_SOURCE
+#endif
+
+#endif
diff --git a/evdns.c b/evdns.c
index b447fd91..d06971e2 100644
--- a/evdns.c
+++ b/evdns.c
@@ -48,8 +48,10 @@
* Version: 0.1b
*/
-#include <sys/types.h>
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
#ifndef _FORTIFY_SOURCE
#define _FORTIFY_SOURCE 3
@@ -73,7 +75,7 @@
#include <sys/stat.h>
#include <stdio.h>
#include <stdarg.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef _WIN32_IE
@@ -101,7 +103,7 @@
#include "ipv6-internal.h"
#include "util-internal.h"
#include "evthread-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <ctype.h>
#include <winsock2.h>
#include <windows.h>
@@ -3488,7 +3490,7 @@ evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *con
static char *
evdns_get_default_hosts_filename(void)
{
-#ifdef WIN32
+#ifdef _WIN32
/* Windows is a little coy about where it puts its configuration
* files. Sure, they're _usually_ in C:\windows\system32, but
* there's no reason in principle they couldn't be in
@@ -3570,7 +3572,7 @@ evdns_resolv_conf_parse(int flags, const char *const filename) {
}
-#ifdef WIN32
+#ifdef _WIN32
/* Add multiple nameservers from a space-or-comma-separated list. */
static int
evdns_nameserver_ip_add_line(struct evdns_base *base, const char *ips) {
@@ -3850,7 +3852,7 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
if (initialize_nameservers) {
int r;
-#ifdef WIN32
+#ifdef _WIN32
r = evdns_base_config_windows_nameservers(base);
#else
r = evdns_base_resolv_conf_parse(base, DNS_OPTIONS_ALL, "/etc/resolv.conf");
diff --git a/event-internal.h b/event-internal.h
index 1798b702..1b6421db 100644
--- a/event-internal.h
+++ b/event-internal.h
@@ -32,6 +32,8 @@ extern "C" {
#endif
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
#include <time.h>
#include <sys/queue.h>
#include "event2/event_struct.h"
@@ -99,7 +101,7 @@ struct eventop {
size_t fdinfo_len;
};
-#ifdef WIN32
+#ifdef _WIN32
/* If we're on win32, then file descriptors are not nice low densely packed
integers. Instead, they are pointer-like windows handles, and we want to
use a hashtable instead of an array to map fds to events.
@@ -266,7 +268,7 @@ struct event_base {
int current_event_waiters;
#endif
-#ifdef WIN32
+#ifdef _WIN32
/** IOCP support structure, if IOCP is enabled. */
struct event_iocp_port *iocp;
#endif
@@ -274,6 +276,10 @@ struct event_base {
/** Flags that this base was configured with */
enum event_base_config_flag flags;
+ struct timeval max_dispatch_time;
+ int max_dispatch_callbacks;
+ int limit_callbacks_after_prio;
+
/* Notify main thread to wake up break, etc. */
/** True if the base already has a pending notify, and we don't need
* to add any more. */
@@ -300,6 +306,9 @@ struct event_config {
TAILQ_HEAD(event_configq, event_config_entry) entries;
int n_cpus_hint;
+ struct timeval max_dispatch_interval;
+ int max_dispatch_callbacks;
+ int limit_callbacks_after_prio;
enum event_method_feature require_features;
enum event_base_config_flag flags;
};
diff --git a/event.c b/event.c
index f13cdcf1..8b6a0da7 100644
--- a/event.c
+++ b/event.c
@@ -25,15 +25,16 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#endif
#include <sys/types.h>
-#if !defined(WIN32) && defined(_EVENT_HAVE_SYS_TIME_H)
+#if !defined(_WIN32) && defined(_EVENT_HAVE_SYS_TIME_H)
#include <sys/time.h>
#endif
#include <sys/queue.h>
@@ -53,6 +54,7 @@
#include <signal.h>
#include <string.h>
#include <time.h>
+#include <limits.h>
#include "event2/event.h"
#include "event2/event_struct.h"
@@ -87,7 +89,7 @@ extern const struct eventop kqops;
#ifdef _EVENT_HAVE_DEVPOLL
extern const struct eventop devpollops;
#endif
-#ifdef WIN32
+#ifdef _WIN32
extern const struct eventop win32ops;
#endif
@@ -111,7 +113,7 @@ static const struct eventop *eventops[] = {
#ifdef _EVENT_HAVE_SELECT
&selectops,
#endif
-#ifdef WIN32
+#ifdef _WIN32
&win32ops,
#endif
NULL
@@ -130,8 +132,14 @@ static inline int event_add_internal(struct event *ev,
const struct timeval *tv, int tv_is_absolute);
static inline int event_del_internal(struct event *ev);
-static void event_queue_insert(struct event_base *, struct event *, int);
-static void event_queue_remove(struct event_base *, struct event *, int);
+static void event_queue_insert_active(struct event_base *, struct event *);
+static void event_queue_insert_timeout(struct event_base *, struct event *);
+static void event_queue_insert_inserted(struct event_base *, struct event *);
+static void event_queue_remove_active(struct event_base *, struct event *);
+static void event_queue_remove_timeout(struct event_base *, struct event *);
+static void event_queue_remove_inserted(struct event_base *, struct event *);
+static void event_queue_reinsert_timeout(struct event_base *,struct event *);
+
static int event_haveevents(struct event_base *);
static int event_process_active(struct event_base *);
@@ -145,6 +153,9 @@ static inline void event_persist_closure(struct event_base *, struct event *ev);
static int evthread_notify_base(struct event_base *base);
+static void insert_common_timeout_inorder(struct common_timeout_list *ctl,
+ struct event *ev);
+
#ifndef _EVENT_DISABLE_DEBUG_MODE
/* These functions implement a hashtable of which 'struct event *' structures
* have been setup or added. We don't want to trust the content of the struct
@@ -342,7 +353,7 @@ detect_monotonic(void)
/* How often (in seconds) do we check for changes in wall clock time relative
* to monotonic time? Set this to -1 for 'never.' */
-#define CLOCK_SYNC_INTERVAL -1
+#define CLOCK_SYNC_INTERVAL 5
/** Set 'tp' to the current time according to 'base'. We must hold the lock
* on 'base'. If there is a cached time, return it. Otherwise, use
@@ -424,6 +435,22 @@ update_time_cache(struct event_base *base)
gettime(base, &base->tv_cache);
}
+int
+event_base_update_cache_time(struct event_base *base)
+{
+
+ if (!base) {
+ base = current_base;
+ if (!current_base)
+ return -1;
+ }
+
+ EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+ update_time_cache(base);
+ EVBASE_RELEASE_LOCK(base, th_base_lock);
+ return 0;
+}
+
struct event_base *
event_init(void)
{
@@ -570,6 +597,7 @@ event_base_new_with_config(const struct event_config *cfg)
base->th_notify_fd[1] = -1;
event_deferred_cb_queue_init(&base->defer_queue);
+ base->defer_queue.base = base;
base->defer_queue.notify_fn = notify_base_cbq_callback;
base->defer_queue.notify_arg = base;
if (cfg)
@@ -584,6 +612,24 @@ event_base_new_with_config(const struct event_config *cfg)
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
+ if (cfg) {
+ memcpy(&base->max_dispatch_time,
+ &cfg->max_dispatch_interval, sizeof(struct timeval));
+ base->limit_callbacks_after_prio =
+ cfg->limit_callbacks_after_prio;
+ } else {
+ base->max_dispatch_time.tv_sec = -1;
+ base->limit_callbacks_after_prio = 1;
+ }
+ if (cfg && cfg->max_dispatch_callbacks >= 0) {
+ base->max_dispatch_callbacks = cfg->max_dispatch_callbacks;
+ } else {
+ base->max_dispatch_callbacks = INT_MAX;
+ }
+ if (base->max_dispatch_callbacks == INT_MAX &&
+ base->max_dispatch_time.tv_sec == -1)
+ base->limit_callbacks_after_prio = INT_MAX;
+
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
@@ -641,7 +687,7 @@ event_base_new_with_config(const struct event_config *cfg)
}
#endif
-#ifdef WIN32
+#ifdef _WIN32
if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP))
event_base_start_iocp(base, cfg->n_cpus_hint);
#endif
@@ -652,7 +698,7 @@ event_base_new_with_config(const struct event_config *cfg)
int
event_base_start_iocp(struct event_base *base, int n_cpus)
{
-#ifdef WIN32
+#ifdef _WIN32
if (base->iocp)
return 0;
base->iocp = event_iocp_port_launch(n_cpus);
@@ -669,7 +715,7 @@ event_base_start_iocp(struct event_base *base, int n_cpus)
void
event_base_stop_iocp(struct event_base *base)
{
-#ifdef WIN32
+#ifdef _WIN32
int rv;
if (!base->iocp)
@@ -702,7 +748,7 @@ event_base_free(struct event_base *base)
}
/* XXX(niels) - check for internal events first */
-#ifdef WIN32
+#ifdef _WIN32
event_base_stop_iocp(base);
#endif
@@ -787,94 +833,122 @@ event_base_free(struct event_base *base)
mm_free(base);
}
+/* Fake eventop; used to disable the backend temporarily inside event_reinit
+ * so that we can call event_del() on an event without telling the backend.
+ */
+static int
+nil_backend_del(struct event_base *b, evutil_socket_t fd, short old,
+ short events, void *fdinfo)
+{
+ return 0;
+}
+const struct eventop nil_eventop = {
+ "nil",
+ NULL, /* init: unused. */
+ NULL, /* add: unused. */
+ nil_backend_del, /* del: used, so needs to be killed. */
+ NULL, /* dispatch: unused. */
+ NULL, /* dealloc: unused. */
+ 0, 0, 0
+};
+
/* reinitialize the event base after a fork */
int
event_reinit(struct event_base *base)
{
const struct eventop *evsel;
int res = 0;
- struct event *ev;
int was_notifiable = 0;
+ int had_signal_added = 0;
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
evsel = base->evsel;
-#if 0
- /* Right now, reinit always takes effect, since even if the
- backend doesn't require it, the signal socketpair code does.
+ /* check if this event mechanism requires reinit on the backend */
+ if (evsel->need_reinit) {
+ /* We're going to call event_del() on our notify events (the
+ * ones that tell about signals and wakeup events). But we
+ * don't actually want to tell the backend to change its
+ * state, since it might still share some resource (a kqueue,
+ * an epoll fd) with the parent process, and we don't want to
+ * delete the fds from _that_ backend, we temporarily stub out
+ * the evsel with a replacement.
+ */
+ base->evsel = &nil_eventop;
+ }
- XXX
+ /* We need to re-create a new signal-notification fd and a new
+ * thread-notification fd. Otherwise, we'll still share those with
+ * the parent process, which would make any notification sent to them
+ * get received by one or both of the event loops, more or less at
+ * random.
*/
- /* check if this event mechanism requires reinit */
- if (!evsel->need_reinit)
- goto done;
-#endif
-
- /* prevent internal delete */
if (base->sig.ev_signal_added) {
- /* we cannot call event_del here because the base has
- * not been reinitialized yet. */
- event_queue_remove(base, &base->sig.ev_signal,
- EVLIST_INSERTED);
- if (base->sig.ev_signal.ev_flags & EVLIST_ACTIVE)
- event_queue_remove(base, &base->sig.ev_signal,
- EVLIST_ACTIVE);
+ event_del(&base->sig.ev_signal);
+ event_debug_unassign(&base->sig.ev_signal);
+ memset(&base->sig.ev_signal, 0, sizeof(base->sig.ev_signal));
if (base->sig.ev_signal_pair[0] != -1)
EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]);
if (base->sig.ev_signal_pair[1] != -1)
EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]);
+ had_signal_added = 1;
base->sig.ev_signal_added = 0;
}
if (base->th_notify_fd[0] != -1) {
- /* we cannot call event_del here because the base has
- * not been reinitialized yet. */
was_notifiable = 1;
- event_queue_remove(base, &base->th_notify,
- EVLIST_INSERTED);
- if (base->th_notify.ev_flags & EVLIST_ACTIVE)
- event_queue_remove(base, &base->th_notify,
- EVLIST_ACTIVE);
- base->sig.ev_signal_added = 0;
+ event_del(&base->th_notify);
EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);
if (base->th_notify_fd[1] != -1)
EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
event_debug_unassign(&base->th_notify);
+ base->th_notify_fn = NULL;
}
- if (base->evsel->dealloc != NULL)
- base->evsel->dealloc(base);
- base->evbase = evsel->init(base);
- if (base->evbase == NULL) {
- event_errx(1, "%s: could not reinitialize event mechanism",
- __func__);
- res = -1;
- goto done;
- }
-
- event_changelist_freemem(&base->changelist); /* XXX */
- evmap_io_clear(&base->io);
- evmap_signal_clear(&base->sigmap);
+ /* Replace the original evsel. */
+ base->evsel = evsel;
- TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
- if (ev->ev_events & (EV_READ|EV_WRITE)) {
- if (ev == &base->sig.ev_signal) {
- /* If we run into the ev_signal event, it's only
- * in eventqueue because some signal event was
- * added, which made evsig_add re-add ev_signal.
- * So don't double-add it. */
- continue;
- }
- if (evmap_io_add(base, ev->ev_fd, ev) == -1)
- res = -1;
- } else if (ev->ev_events & EV_SIGNAL) {
- if (evmap_signal_add(base, (int)ev->ev_fd, ev) == -1)
- res = -1;
+ if (evsel->need_reinit) {
+ /* Reconstruct the backend through brute-force, so that we do
+ * not share any structures with the parent process. For some
+ * backends, this is necessary: epoll and kqueue, for
+ * instance, have events associated with a kernel
+ * structure. If didn't reinitialize, we'd share that
+ * structure with the parent process, and any changes made by
+ * the parent would affect our backend's behavior (and vice
+ * versa).
+ */
+ if (base->evsel->dealloc != NULL)
+ base->evsel->dealloc(base);
+ base->evbase = evsel->init(base);
+ if (base->evbase == NULL) {
+ event_errx(1,
+ "%s: could not reinitialize event mechanism",
+ __func__);
+ res = -1;
+ goto done;
}
+
+ /* Empty out the changelist (if any): we are starting from a
+ * blank slate. */
+ event_changelist_freemem(&base->changelist);
+
+ /* Tell the event maps to re-inform the backend about all
+ * pending events. This will make the signal notification
+ * event get re-created if necessary. */
+ if (evmap_io_reinit(base) < 0)
+ res = -1;
+ if (evmap_signal_reinit(base) < 0)
+ res = -1;
+ } else {
+ if (had_signal_added)
+ res = evsig_init(base);
}
+ /* If we were notifiable before, and nothing just exploded, become
+ * notifiable again. */
if (was_notifiable && res == 0)
res = evthread_make_base_notifiable(base);
@@ -924,6 +998,9 @@ event_config_new(void)
return (NULL);
TAILQ_INIT(&cfg->entries);
+ cfg->max_dispatch_interval.tv_sec = -1;
+ cfg->max_dispatch_callbacks = INT_MAX;
+ cfg->limit_callbacks_after_prio = 1;
return (cfg);
}
@@ -994,6 +1071,23 @@ event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
}
int
+event_config_set_max_dispatch_interval(struct event_config *cfg,
+ const struct timeval *max_interval, int max_callbacks, int min_priority)
+{
+ if (max_interval)
+ memcpy(&cfg->max_dispatch_interval, max_interval,
+ sizeof(struct timeval));
+ else
+ cfg->max_dispatch_interval.tv_sec = -1;
+ cfg->max_dispatch_callbacks =
+ max_callbacks >= 0 ? max_callbacks : INT_MAX;
+ if (min_priority <= 0)
+ min_priority = 1;
+ cfg->limit_callbacks_after_prio = min_priority;
+ return (0);
+}
+
+int
event_priority_init(int npriorities)
{
return event_base_priority_init(current_base, npriorities);
@@ -1002,14 +1096,17 @@ event_priority_init(int npriorities)
int
event_base_priority_init(struct event_base *base, int npriorities)
{
- int i;
+ int i, r;
+ r = -1;
+
+ EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (N_ACTIVE_CALLBACKS(base) || npriorities < 1
|| npriorities >= EVENT_MAX_PRIORITIES)
- return (-1);
+ goto err;
if (npriorities == base->nactivequeues)
- return (0);
+ goto ok;
if (base->nactivequeues) {
mm_free(base->activequeues);
@@ -1021,7 +1118,7 @@ event_base_priority_init(struct event_base *base, int npriorities)
mm_calloc(npriorities, sizeof(struct event_list));
if (base->activequeues == NULL) {
event_warn("%s: calloc", __func__);
- return (-1);
+ goto err;
}
base->nactivequeues = npriorities;
@@ -1029,7 +1126,22 @@ event_base_priority_init(struct event_base *base, int npriorities)
TAILQ_INIT(&base->activequeues[i]);
}
- return (0);
+ok:
+ r = 0;
+err:
+ EVBASE_RELEASE_LOCK(base, th_base_lock);
+ return (r);
+}
+
+int
+event_base_get_npriorities(struct event_base *base)
+{
+
+ int n;
+ EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+ n = base->nactivequeues;
+ EVBASE_RELEASE_LOCK(base, th_base_lock);
+ return (n);
}
/* Returns true iff we're currently watching any events. */
@@ -1300,7 +1412,8 @@ event_persist_closure(struct event_base *base, struct event *ev)
*/
static int
event_process_active_single_queue(struct event_base *base,
- struct event_list *activeq)
+ struct event_list *activeq,
+ int max_to_process, const struct timeval *endtime)
{
struct event *ev;
int count = 0;
@@ -1309,7 +1422,7 @@ event_process_active_single_queue(struct event_base *base,
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
if (ev->ev_events & EV_PERSIST)
- event_queue_remove(base, ev, EVLIST_ACTIVE);
+ event_queue_remove_active(base, ev);
else
event_del_internal(ev);
if (!(ev->ev_flags & EVLIST_INTERNAL))
@@ -1353,6 +1466,15 @@ event_process_active_single_queue(struct event_base *base,
if (base->event_break)
return -1;
+ if (count >= max_to_process)
+ return count;
+ if (count && endtime) {
+ struct timeval now;
+ update_time_cache(base);
+ gettime(base, &now);
+ if (evutil_timercmp(&now, endtime, >=))
+ return count;
+ }
}
return count;
}
@@ -1364,12 +1486,16 @@ event_process_active_single_queue(struct event_base *base,
we process.
*/
static int
-event_process_deferred_callbacks(struct deferred_cb_queue *queue, int *breakptr)
+event_process_deferred_callbacks(struct deferred_cb_queue *queue, int *breakptr,
+ int max_to_process, const struct timeval *endtime)
{
int count = 0;
struct deferred_cb *cb;
-
#define MAX_DEFERRED 16
+ if (max_to_process > MAX_DEFERRED)
+ max_to_process = MAX_DEFERRED;
+#undef MAX_DEFERRED
+
while ((cb = TAILQ_FIRST(&queue->deferred_cb_list))) {
cb->queued = 0;
TAILQ_REMOVE(&queue->deferred_cb_list, cb, cb_next);
@@ -1381,10 +1507,16 @@ event_process_deferred_callbacks(struct deferred_cb_queue *queue, int *breakptr)
LOCK_DEFERRED_QUEUE(queue);
if (*breakptr)
return -1;
- if (++count == MAX_DEFERRED)
+ if (++count >= max_to_process)
break;
+ if (endtime) {
+ struct timeval now;
+ update_time_cache(queue->base);
+ gettime(queue->base, &now);
+ if (evutil_timercmp(&now, endtime, >=))
+ return count;
+ }
}
-#undef MAX_DEFERRED
return count;
}
@@ -1400,11 +1532,28 @@ event_process_active(struct event_base *base)
/* Caller must hold th_base_lock */
struct event_list *activeq = NULL;
int i, c = 0;
+ const struct timeval *endtime;
+ struct timeval tv;
+ const int maxcb = base->max_dispatch_callbacks;
+ const int limit_after_prio = base->limit_callbacks_after_prio;
+ if (base->max_dispatch_time.tv_sec >= 0) {
+ update_time_cache(base);
+ gettime(base, &tv);
+ evutil_timeradd(&base->max_dispatch_time, &tv, &tv);
+ endtime = &tv;
+ } else {
+ endtime = NULL;
+ }
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {
activeq = &base->activequeues[i];
- c = event_process_active_single_queue(base, activeq);
+ if (i < limit_after_prio)
+ c = event_process_active_single_queue(base, activeq,
+ INT_MAX, NULL);
+ else
+ c = event_process_active_single_queue(base, activeq,
+ maxcb, endtime);
if (c < 0)
return -1;
else if (c > 0)
@@ -1415,7 +1564,8 @@ event_process_active(struct event_base *base)
}
}
- event_process_deferred_callbacks(&base->defer_queue,&base->event_break);
+ event_process_deferred_callbacks(&base->defer_queue,&base->event_break,
+ maxcb-c, endtime);
return c;
}
@@ -1576,7 +1726,8 @@ event_base_loop(struct event_base *base, int flags)
}
/* If we have no events, we just exit */
- if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
+ if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) &&
+ !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = 1;
goto done;
@@ -1656,7 +1807,6 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
void *arg, const struct timeval *tv)
{
struct event_once *eonce;
- struct timeval etv;
int res = 0;
/* We cannot support signals that just fire once, or persistent
@@ -1671,12 +1821,16 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
eonce->arg = arg;
if (events == EV_TIMEOUT) {
- if (tv == NULL) {
- evutil_timerclear(&etv);
- tv = &etv;
- }
-
evtimer_assign(&eonce->ev, base, event_once_cb, eonce);
+
+ if (tv == NULL || ! evutil_timerisset(tv)) {
+ /* If the event is going to become active immediately,
+ * don't put it on the timeout queue. This is one
+ * idiom for scheduling a callback, so let's make
+ * it fast (and order-preserving). */
+ event_active(&eonce->ev, EV_TIMEOUT, 1);
+ return 0;
+ }
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;
@@ -1955,7 +2109,7 @@ evthread_notify_base_default(struct event_base *base)
char buf[1];
int r;
buf[0] = (char) 0;
-#ifdef WIN32
+#ifdef _WIN32
r = send(base->th_notify_fd[1], buf, 1, 0);
#else
r = write(base->th_notify_fd[1], buf, 1);
@@ -2049,7 +2203,7 @@ event_add_internal(struct event *ev, const struct timeval *tv,
else if (ev->ev_events & EV_SIGNAL)
res = evmap_signal_add(base, (int)ev->ev_fd, ev);
if (res != -1)
- event_queue_insert(base, ev, EVLIST_INSERTED);
+ event_queue_insert_inserted(base, ev);
if (res == 1) {
/* evmap says we need to notify the main thread. */
notify = 1;
@@ -2074,17 +2228,6 @@ event_add_internal(struct event *ev, const struct timeval *tv,
if (ev->ev_closure == EV_CLOSURE_PERSIST && !tv_is_absolute)
ev->ev_io_timeout = *tv;
- /*
- * we already reserved memory above for the case where we
- * are not replacing an existing timeout.
- */
- if (ev->ev_flags & EVLIST_TIMEOUT) {
- /* XXX I believe this is needless. */
- if (min_heap_elt_is_top(ev))
- notify = 1;
- event_queue_remove(base, ev, EVLIST_TIMEOUT);
- }
-
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
@@ -2100,7 +2243,7 @@ event_add_internal(struct event *ev, const struct timeval *tv,
}
}
- event_queue_remove(base, ev, EVLIST_ACTIVE);
+ event_queue_remove_active(base, ev);
}
gettime(base, &now);
@@ -2119,10 +2262,11 @@ event_add_internal(struct event *ev, const struct timeval *tv,
}
event_debug((
- "event_add: timeout in %d seconds, call %p",
- (int)tv->tv_sec, ev->ev_callback));
+ "event_add: event %p, timeout in %d seconds %d useconds, call %p",
+ ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));
+
+ event_queue_reinsert_timeout(base, ev);
- event_queue_insert(base, ev, EVLIST_TIMEOUT);
if (common_timeout) {
struct common_timeout_list *ctl =
get_common_timeout_list(base, &ev->ev_timeout);
@@ -2214,14 +2358,14 @@ event_del_internal(struct event *ev)
* dispatch loop early anyway, so we wouldn't gain anything by
* doing it.
*/
- event_queue_remove(base, ev, EVLIST_TIMEOUT);
+ event_queue_remove_timeout(base, ev);
}
if (ev->ev_flags & EVLIST_ACTIVE)
- event_queue_remove(base, ev, EVLIST_ACTIVE);
+ event_queue_remove_active(base, ev);
if (ev->ev_flags & EVLIST_INSERTED) {
- event_queue_remove(base, ev, EVLIST_INSERTED);
+ event_queue_remove_inserted(base, ev);
if (ev->ev_events & (EV_READ|EV_WRITE))
res = evmap_io_del(base, ev->ev_fd, ev);
else
@@ -2292,7 +2436,7 @@ event_active_nolock(struct event *ev, int res, short ncalls)
ev->ev_pncalls = NULL;
}
- event_queue_insert(base, ev, EVLIST_ACTIVE);
+ event_queue_insert_active(base, ev);
if (EVBASE_NEED_NOTIFY(base))
evthread_notify_base(base);
@@ -2379,7 +2523,7 @@ timeout_next(struct event_base *base, struct timeval **tv_p)
EVUTIL_ASSERT(tv->tv_sec >= 0);
EVUTIL_ASSERT(tv->tv_usec >= 0);
- event_debug(("timeout_next: in %d seconds", (int)tv->tv_sec));
+ event_debug(("timeout_next: event: %p, in %d seconds, %d useconds", ev, (int)tv->tv_sec, (int)tv->tv_usec));
out:
return (res);
@@ -2464,49 +2608,91 @@ timeout_process(struct event_base *base)
/* delete this event from the I/O queues */
event_del_internal(ev);
- event_debug(("timeout_process: call %p",
- ev->ev_callback));
+ event_debug(("timeout_process: event: %p, call %p",
+ ev, ev->ev_callback));
event_active_nolock(ev, EV_TIMEOUT, 1);
}
}
-/* Remove 'ev' from 'queue' (EVLIST_...) in base. */
+#if (EVLIST_INTERNAL >> 4) != 1
+#error "Mismatch for value of EVLIST_INTERNAL"
+#endif
+/* These are a fancy way to spell
+ if (~ev->ev_flags & EVLIST_INTERNAL)
+ base->event_count--/++;
+*/
+#define DECR_EVENT_COUNT(base,ev) \
+ ((base)->event_count -= (~((ev)->ev_flags >> 4) & 1))
+#define INCR_EVENT_COUNT(base, ev) \
+ ((base)->event_count += (~((ev)->ev_flags >> 4) & 1))
+
static void
-event_queue_remove(struct event_base *base, struct event *ev, int queue)
+event_queue_remove_inserted(struct event_base *base, struct event *ev)
{
EVENT_BASE_ASSERT_LOCKED(base);
-
- if (!(ev->ev_flags & queue)) {
+ if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_INSERTED))) {
event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
- ev, ev->ev_fd, queue);
+ ev, ev->ev_fd, EVLIST_INSERTED);
return;
}
+ DECR_EVENT_COUNT(base, ev);
+ ev->ev_flags &= ~EVLIST_INSERTED;
+ TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
+}
+static void
+event_queue_remove_active(struct event_base *base, struct event *ev)
+{
+ EVENT_BASE_ASSERT_LOCKED(base);
+ if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_ACTIVE))) {
+ event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
+ ev, ev->ev_fd, EVLIST_ACTIVE);
+ return;
+ }
+ DECR_EVENT_COUNT(base, ev);
+ ev->ev_flags &= ~EVLIST_ACTIVE;
+ base->event_count_active--;
+ TAILQ_REMOVE(&base->activequeues[ev->ev_pri],
+ ev, ev_active_next);
+}
+static void
+event_queue_remove_timeout(struct event_base *base, struct event *ev)
+{
+ EVENT_BASE_ASSERT_LOCKED(base);
+ if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_TIMEOUT))) {
+ event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
+ ev, ev->ev_fd, EVLIST_TIMEOUT);
+ return;
+ }
+ DECR_EVENT_COUNT(base, ev);
+ ev->ev_flags &= ~EVLIST_TIMEOUT;
- if (~ev->ev_flags & EVLIST_INTERNAL)
- base->event_count--;
-
- ev->ev_flags &= ~queue;
- switch (queue) {
- case EVLIST_INSERTED:
- TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
- break;
- case EVLIST_ACTIVE:
- base->event_count_active--;
- TAILQ_REMOVE(&base->activequeues[ev->ev_pri],
- ev, ev_active_next);
- break;
- case EVLIST_TIMEOUT:
- if (is_common_timeout(&ev->ev_timeout, base)) {
- struct common_timeout_list *ctl =
- get_common_timeout_list(base, &ev->ev_timeout);
- TAILQ_REMOVE(&ctl->events, ev,
- ev_timeout_pos.ev_next_with_common_timeout);
- } else {
- min_heap_erase(&base->timeheap, ev);
- }
- break;
- default:
- event_errx(1, "%s: unknown queue %x", __func__, queue);
+ if (is_common_timeout(&ev->ev_timeout, base)) {
+ struct common_timeout_list *ctl =
+ get_common_timeout_list(base, &ev->ev_timeout);
+ TAILQ_REMOVE(&ctl->events, ev,
+ ev_timeout_pos.ev_next_with_common_timeout);
+ } else {
+ min_heap_erase(&base->timeheap, ev);
+ }
+}
+
+/* Remove and reinsert 'ev' into the timeout queue. */
+static void
+event_queue_reinsert_timeout(struct event_base *base, struct event *ev)
+{
+ if (!(ev->ev_flags & EVLIST_TIMEOUT)) {
+ event_queue_insert_timeout(base, ev);
+ return;
+ }
+
+ if (is_common_timeout(&ev->ev_timeout, base)) {
+ struct common_timeout_list *ctl =
+ get_common_timeout_list(base, &ev->ev_timeout);
+ TAILQ_REMOVE(&ctl->events, ev,
+ ev_timeout_pos.ev_next_with_common_timeout);
+ insert_common_timeout_inorder(ctl, ev);
+ } else {
+ min_heap_adjust(&base->timeheap, ev);
}
}
@@ -2542,44 +2728,63 @@ insert_common_timeout_inorder(struct common_timeout_list *ctl,
}
static void
-event_queue_insert(struct event_base *base, struct event *ev, int queue)
+event_queue_insert_inserted(struct event_base *base, struct event *ev)
{
EVENT_BASE_ASSERT_LOCKED(base);
- if (ev->ev_flags & queue) {
- /* Double insertion is possible for active events */
- if (queue & EVLIST_ACTIVE)
- return;
+ if (EVUTIL_FAILURE_CHECK(ev->ev_flags & EVLIST_INSERTED)) {
+ event_errx(1, "%s: %p(fd %d) already inserted", __func__,
+ ev, ev->ev_fd);
+ return;
+ }
+
+ INCR_EVENT_COUNT(base, ev);
+
+ ev->ev_flags |= EVLIST_INSERTED;
+
+ TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
+}
+
+static void
+event_queue_insert_active(struct event_base *base, struct event *ev)
+{
+ EVENT_BASE_ASSERT_LOCKED(base);
- event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
- ev, ev->ev_fd, queue);
+ if (ev->ev_flags & EVLIST_ACTIVE) {
+ /* Double insertion is possible for active events */
return;
}
- if (~ev->ev_flags & EVLIST_INTERNAL)
- base->event_count++;
-
- ev->ev_flags |= queue;
- switch (queue) {
- case EVLIST_INSERTED:
- TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
- break;
- case EVLIST_ACTIVE:
- base->event_count_active++;
- TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],
- ev,ev_active_next);
- break;
- case EVLIST_TIMEOUT: {
- if (is_common_timeout(&ev->ev_timeout, base)) {
- struct common_timeout_list *ctl =
- get_common_timeout_list(base, &ev->ev_timeout);
- insert_common_timeout_inorder(ctl, ev);
- } else
- min_heap_push(&base->timeheap, ev);
- break;
+ INCR_EVENT_COUNT(base, ev);
+
+ ev->ev_flags |= EVLIST_ACTIVE;
+
+ base->event_count_active++;
+ TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],
+ ev,ev_active_next);
+}
+
+static void
+event_queue_insert_timeout(struct event_base *base, struct event *ev)
+{
+ EVENT_BASE_ASSERT_LOCKED(base);
+
+ if (EVUTIL_FAILURE_CHECK(ev->ev_flags & EVLIST_TIMEOUT)) {
+ event_errx(1, "%s: %p(fd %d) already on timeout", __func__,
+ ev, ev->ev_fd);
+ return;
}
- default:
- event_errx(1, "%s: unknown queue %x", __func__, queue);
+
+ INCR_EVENT_COUNT(base, ev);
+
+ ev->ev_flags |= EVLIST_TIMEOUT;
+
+ if (is_common_timeout(&ev->ev_timeout, base)) {
+ struct common_timeout_list *ctl =
+ get_common_timeout_list(base, &ev->ev_timeout);
+ insert_common_timeout_inorder(ctl, ev);
+ } else {
+ min_heap_push(&base->timeheap, ev);
}
}
@@ -2616,6 +2821,9 @@ static void (*_mm_free_fn)(void *p) = NULL;
void *
event_mm_malloc_(size_t sz)
{
+ if (sz == 0)
+ return NULL;
+
if (_mm_malloc_fn)
return _mm_malloc_fn(sz);
else
@@ -2625,31 +2833,51 @@ event_mm_malloc_(size_t sz)
void *
event_mm_calloc_(size_t count, size_t size)
{
+ if (count == 0 || size == 0)
+ return NULL;
+
if (_mm_malloc_fn) {
size_t sz = count * size;
- void *p = _mm_malloc_fn(sz);
+ void *p = NULL;
+ if (count > EV_SIZE_MAX / size)
+ goto error;
+ p = _mm_malloc_fn(sz);
if (p)
- memset(p, 0, sz);
- return p;
+ return memset(p, 0, sz);
} else
return calloc(count, size);
+
+error:
+ errno = ENOMEM;
+ return NULL;
}
char *
event_mm_strdup_(const char *str)
{
+ if (!str) {
+ errno = EINVAL;
+ return NULL;
+ }
+
if (_mm_malloc_fn) {
size_t ln = strlen(str);
- void *p = _mm_malloc_fn(ln+1);
+ void *p = NULL;
+ if (ln == EV_SIZE_MAX)
+ goto error;
+ p = _mm_malloc_fn(ln+1);
if (p)
- memcpy(p, str, ln+1);
- return p;
+ return memcpy(p, str, ln+1);
} else
-#ifdef WIN32
+#ifdef _WIN32
return _strdup(str);
#else
return strdup(str);
#endif
+
+error:
+ errno = ENOMEM;
+ return NULL;
}
void *
@@ -2704,7 +2932,7 @@ evthread_notify_drain_default(evutil_socket_t fd, short what, void *arg)
{
unsigned char buf[1024];
struct event_base *base = arg;
-#ifdef WIN32
+#ifdef _WIN32
while (recv(fd, (char*)buf, sizeof(buf), 0) > 0)
;
#else
@@ -2727,8 +2955,10 @@ evthread_make_base_notifiable(struct event_base *base)
if (!base)
return -1;
- if (base->th_notify_fd[0] >= 0)
+ if (base->th_notify_fn != NULL) {
+ /* The base is already notifiable: we're doing fine. */
return 0;
+ }
#if defined(_EVENT_HAVE_EVENTFD) && defined(_EVENT_HAVE_SYS_EVENTFD_H)
#ifndef EFD_CLOEXEC
@@ -2754,7 +2984,7 @@ evthread_make_base_notifiable(struct event_base *base)
}
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define LOCAL_SOCKETPAIR_AF AF_INET
#else
#define LOCAL_SOCKETPAIR_AF AF_UNIX
diff --git a/event.h b/event.h
index a3785b98..45250b37 100644
--- a/event.h
+++ b/event.h
@@ -56,7 +56,7 @@ extern "C" {
/* For int types. */
#include <evutil.h>
-#ifdef WIN32
+#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
diff --git a/event_iocp.c b/event_iocp.c
index 43212810..0849c8ec 100644
--- a/event_iocp.c
+++ b/event_iocp.c
@@ -23,6 +23,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "evconfig-private.h"
#ifndef _WIN32_WINNT
/* Minimum required for InitializeCriticalSectionAndSpinCount */
@@ -282,7 +283,7 @@ event_iocp_activate_overlapped(
struct event_iocp_port *
event_base_get_iocp(struct event_base *base)
{
-#ifdef WIN32
+#ifdef _WIN32
return base->iocp;
#else
return NULL;
diff --git a/event_tagging.c b/event_tagging.c
index a83e719f..6bd922bb 100644
--- a/event_tagging.c
+++ b/event_tagging.c
@@ -26,6 +26,7 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#ifdef _EVENT_HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -34,7 +35,7 @@
#include <sys/param.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <windows.h>
@@ -52,7 +53,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <syslog.h>
#endif
#ifdef _EVENT_HAVE_UNISTD_H
diff --git a/evmap-internal.h b/evmap-internal.h
index 23b5a8a0..fd842676 100644
--- a/evmap-internal.h
+++ b/evmap-internal.h
@@ -87,6 +87,9 @@ void evmap_signal_active(struct event_base *base, evutil_socket_t signum, int nc
void *evmap_io_get_fdinfo(struct event_io_map *ctx, evutil_socket_t fd);
+int evmap_io_reinit(struct event_base *base);
+int evmap_signal_reinit(struct event_base *base);
+
void evmap_check_integrity(struct event_base *base);
#endif /* _EVMAP_H_ */
diff --git a/evmap.c b/evmap.c
index 69c1067d..2d7fb9db 100644
--- a/evmap.c
+++ b/evmap.c
@@ -24,21 +24,22 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#endif
#include <sys/types.h>
-#if !defined(WIN32) && defined(_EVENT_HAVE_SYS_TIME_H)
+#if !defined(_WIN32) && defined(_EVENT_HAVE_SYS_TIME_H)
#include <sys/time.h>
#endif
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <unistd.h>
#endif
#include <errno.h>
@@ -55,7 +56,7 @@
write on a given fd, and the number of each.
*/
struct evmap_io {
- struct event_list events;
+ struct event_dlist events;
ev_uint16_t nread;
ev_uint16_t nwrite;
};
@@ -63,7 +64,7 @@ struct evmap_io {
/* An entry for an evmap_signal list: notes all the events that want to know
when a signal triggers. */
struct evmap_signal {
- struct event_list events;
+ struct event_dlist events;
};
/* On some platforms, fds start at 0 and increment by 1 as they are
@@ -251,7 +252,7 @@ evmap_signal_clear(struct event_signal_map *ctx)
static void
evmap_io_init(struct evmap_io *entry)
{
- TAILQ_INIT(&entry->events);
+ LIST_INIT(&entry->events);
entry->nread = 0;
entry->nwrite = 0;
}
@@ -305,7 +306,7 @@ evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
return -1;
}
if (EVENT_DEBUG_MODE_IS_ON() &&
- (old_ev = TAILQ_FIRST(&ctx->events)) &&
+ (old_ev = LIST_FIRST(&ctx->events)) &&
(old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {
event_warnx("Tried to mix edge-triggered and non-edge-triggered"
" events on fd %d", (int)fd);
@@ -325,7 +326,7 @@ evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
ctx->nread = (ev_uint16_t) nread;
ctx->nwrite = (ev_uint16_t) nwrite;
- TAILQ_INSERT_TAIL(&ctx->events, ev, ev_io_next);
+ LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
return (retval);
}
@@ -381,7 +382,7 @@ evmap_io_del(struct event_base *base, evutil_socket_t fd, struct event *ev)
ctx->nread = nread;
ctx->nwrite = nwrite;
- TAILQ_REMOVE(&ctx->events, ev, ev_io_next);
+ LIST_REMOVE(ev, ev_io_next);
return (retval);
}
@@ -399,7 +400,7 @@ evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
GET_IO_SLOT(ctx, io, fd, evmap_io);
EVUTIL_ASSERT(ctx);
- TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
+ LIST_FOREACH(ev, &ctx->events, ev_io_next) {
if (ev->ev_events & events)
event_active_nolock(ev, ev->ev_events & events, 1);
}
@@ -410,7 +411,7 @@ evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
static void
evmap_signal_init(struct evmap_signal *entry)
{
- TAILQ_INIT(&entry->events);
+ LIST_INIT(&entry->events);
}
@@ -429,13 +430,13 @@ evmap_signal_add(struct event_base *base, int sig, struct event *ev)
GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,
base->evsigsel->fdinfo_len);
- if (TAILQ_EMPTY(&ctx->events)) {
+ if (LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
== -1)
return (-1);
}
- TAILQ_INSERT_TAIL(&ctx->events, ev, ev_signal_next);
+ LIST_INSERT_HEAD(&ctx->events, ev, ev_signal_next);
return (1);
}
@@ -452,13 +453,13 @@ evmap_signal_del(struct event_base *base, int sig, struct event *ev)
GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
- if (TAILQ_FIRST(&ctx->events) == TAILQ_LAST(&ctx->events, event_list)) {
+ LIST_REMOVE(ev, ev_signal_next);
+
+ if (LIST_FIRST(&ctx->events) == NULL) {
if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)
return (-1);
}
- TAILQ_REMOVE(&ctx->events, ev, ev_signal_next);
-
return (1);
}
@@ -472,7 +473,7 @@ evmap_signal_active(struct event_base *base, evutil_socket_t sig, int ncalls)
EVUTIL_ASSERT(sig < map->nentries);
GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);
- TAILQ_FOREACH(ev, &ctx->events, ev_signal_next)
+ LIST_FOREACH(ev, &ctx->events, ev_signal_next)
event_active_nolock(ev, EV_SIGNAL, ncalls);
}
@@ -487,6 +488,65 @@ evmap_io_get_fdinfo(struct event_io_map *map, evutil_socket_t fd)
return NULL;
}
+int
+evmap_io_reinit(struct event_base *base)
+{
+ int res = 0;
+ evutil_socket_t i;
+ void *extra;
+ short events;
+ const struct eventop *evsel = base->evsel;
+ struct event_io_map *io = &base->io;
+
+#ifdef EVMAP_USE_HT
+ struct event_map_entry **mapent;
+ HT_FOREACH(mapent, event_io_map, io) {
+ struct evmap_io *ctx = &(*mapent)->ent.evmap_io;
+ i = (*mapent)->fd;
+#else
+ for (i = 0; i < io->nentries; ++i) {
+ struct evmap_io *ctx = io->entries[i];
+ if (!ctx)
+ continue;
+#endif
+ events = 0;
+ extra = ((char*)ctx) + sizeof(struct evmap_io);
+ if (ctx->nread)
+ events |= EV_READ;
+ if (ctx->nread)
+ events |= EV_WRITE;
+ if (evsel->fdinfo_len)
+ memset(extra, 0, evsel->fdinfo_len);
+ if (events && LIST_FIRST(&ctx->events) &&
+ (LIST_FIRST(&ctx->events)->ev_events & EV_ET))
+ events |= EV_ET;
+ if (evsel->add(base, i, 0, events, extra) == -1)
+ res = -1;
+ }
+
+ return res;
+}
+
+int
+evmap_signal_reinit(struct event_base *base)
+{
+ struct event_signal_map *sigmap = &base->sigmap;
+ const struct eventop *evsel = base->evsigsel;
+ int res = 0;
+ int i;
+
+ for (i = 0; i < sigmap->nentries; ++i) {
+ struct evmap_signal *ctx = sigmap->entries[i];
+ if (!ctx)
+ continue;
+ if (!LIST_EMPTY(&ctx->events)) {
+ if (evsel->add(base, i, 0, EV_SIGNAL, NULL) == -1)
+ res = -1;
+ }
+ }
+ return res;
+}
+
/** Per-fd structure for use with changelists. It keeps track, for each fd or
* signal using the changelist, of where its entry in the changelist is.
*/
@@ -521,10 +581,9 @@ event_change_get_fdinfo(struct event_base *base,
return (void*)ptr;
}
-#ifdef DEBUG_CHANGELIST
/** Make sure that the changelist is consistent with the evmap structures. */
static void
-event_changelist_check(struct event_base *base)
+event_changelist_assert_ok(struct event_base *base)
{
int i;
struct event_changelist *changelist = &base->changelist;
@@ -552,6 +611,9 @@ event_changelist_check(struct event_base *base)
}
}
}
+
+#ifdef DEBUG_CHANGELIST
+#define event_changelist_check(base) event_changelist_assert_ok((base))
#else
#define event_changelist_check(base) ((void)0)
#endif
@@ -688,14 +750,11 @@ event_changelist_del(struct event_base *base, evutil_socket_t fd, short old, sho
if (!change)
return -1;
- /* A delete removes any previous add, rather than replacing it:
- on those platforms where "add, delete, dispatch" is not the same
- as "no-op, dispatch", we want the no-op behavior.
-
- As well as checking the current operation we should also check
- the original set of events to make sure were not ignoring
- the case where the add operation is present on an event that
- was already set.
+ /* A delete on an event set that doesn't contain the event to be
+ deleted produces a no-op. This effectively emoves any previous
+ uncommitted add, rather than replacing it: on those platforms where
+ "add, delete, dispatch" is not the same as "no-op, dispatch", we
+ want the no-op behavior.
If we have a no-op item, we could remove it it from the list
entirely, but really there's not much point: skipping the no-op
@@ -707,15 +766,13 @@ event_changelist_del(struct event_base *base, evutil_socket_t fd, short old, sho
*/
if (events & (EV_READ|EV_SIGNAL)) {
- if (!(change->old_events & (EV_READ | EV_SIGNAL)) &&
- (change->read_change & EV_CHANGE_ADD))
+ if (!(change->old_events & (EV_READ | EV_SIGNAL)))
change->read_change = 0;
else
change->read_change = EV_CHANGE_DEL;
}
if (events & EV_WRITE) {
- if (!(change->old_events & EV_WRITE) &&
- (change->write_change & EV_CHANGE_ADD))
+ if (!(change->old_events & EV_WRITE))
change->write_change = 0;
else
change->write_change = EV_CHANGE_DEL;
@@ -759,7 +816,7 @@ evmap_check_integrity(struct event_base *base)
continue;
#endif
- TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
+ LIST_FOREACH(ev, &ctx->events, ev_io_next) {
EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_IOFOUND));
EVUTIL_ASSERT(ev->ev_fd == i);
ev->ev_flags |= EVLIST_X_IOFOUND;
@@ -772,7 +829,7 @@ evmap_check_integrity(struct event_base *base)
if (!ctx)
continue;
- TAILQ_FOREACH(ev, &ctx->events, ev_signal_next) {
+ LIST_FOREACH(ev, &ctx->events, ev_signal_next) {
EVUTIL_ASSERT(!(ev->ev_flags & EVLIST_X_SIGFOUND));
EVUTIL_ASSERT(ev->ev_fd == i);
ev->ev_flags |= EVLIST_X_SIGFOUND;
@@ -794,6 +851,8 @@ evmap_check_integrity(struct event_base *base)
EVUTIL_ASSERT(nio == 0);
EVUTIL_ASSERT(nsignals == 0);
/* There is no "EVUTIL_ASSERT(ntimers == 0)": eventqueue is only for
- * pending signals and io events.
- */
+ * pending signals and io events. */
+
+ if (base->evsel->add == event_changelist_add)
+ event_changelist_assert_ok(base);
}
diff --git a/evport.c b/evport.c
index 0d714d81..511f8d68 100644
--- a/evport.c
+++ b/evport.c
@@ -54,6 +54,7 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <sys/time.h>
#include <sys/queue.h>
@@ -75,20 +76,8 @@
#include "evsignal-internal.h"
#include "evmap-internal.h"
-/*
- * Default value for ed_nevents, which is the maximum file descriptor number we
- * can handle. If an event comes in for a file descriptor F > nevents, we will
- * grow the array of file descriptors, doubling its size.
- */
-#define DEFAULT_NFDS 16
-
-
-/*
- * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
- * any particular call. You can speed things up by increasing this, but it will
- * (obviously) require more memory.
- */
-#define EVENTS_PER_GETN 8
+#define INITIAL_EVENTS_PER_GETN 8
+#define MAX_EVENTS_PER_GETN 4096
/*
* Per-file-descriptor information about what events we're subscribed to. These
@@ -96,7 +85,12 @@
*/
struct fd_info {
- short fdi_what; /* combinations of EV_READ and EV_WRITE */
+ /* combinations of EV_READ and EV_WRITE */
+ short fdi_what;
+ /* Index of this fd within ed_pending, plus 1. Zero if this fd is
+ * not in ed_pending. (The +1 is a hack so that memset(0) will set
+ * it to a nil index. */
+ int pending_idx_plus_1;
};
#define FDI_HAS_READ(fdi) ((fdi)->fdi_what & EV_READ)
@@ -107,10 +101,15 @@ struct fd_info {
struct evport_data {
int ed_port; /* event port for system events */
- int ed_nevents; /* number of allocated fdi's */
- struct fd_info *ed_fds; /* allocated fdi table */
+ /* How many elements of ed_pending should we look at? */
+ int ed_npending;
+ /* How many elements are allocated in ed_pending and pevtlist? */
+ int ed_maxevents;
/* fdi's that we need to reassoc */
- int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */
+ int *ed_pending;
+ /* storage space for incoming events. */
+ port_event_t *ed_pevtlist;
+
};
static void* evport_init(struct event_base *);
@@ -118,6 +117,7 @@ static int evport_add(struct event_base *, int fd, short old, short events, void
static int evport_del(struct event_base *, int fd, short old, short events, void *);
static int evport_dispatch(struct event_base *, struct timeval *);
static void evport_dealloc(struct event_base *);
+static int grow(struct evport_data *, int min_events);
const struct eventop evportops = {
"evport",
@@ -128,7 +128,7 @@ const struct eventop evportops = {
evport_dealloc,
1, /* need reinit */
0, /* features */
- 0, /* fdinfo length */
+ sizeof(struct fd_info), /* fdinfo length */
};
/*
@@ -139,7 +139,6 @@ static void*
evport_init(struct event_base *base)
{
struct evport_data *evpd;
- int i;
if (!(evpd = mm_calloc(1, sizeof(struct evport_data))))
return (NULL);
@@ -149,24 +148,47 @@ evport_init(struct event_base *base)
return (NULL);
}
- /*
- * Initialize file descriptor structure
- */
- evpd->ed_fds = mm_calloc(DEFAULT_NFDS, sizeof(struct fd_info));
- if (evpd->ed_fds == NULL) {
+ if (grow(evpd, INITIAL_EVENTS_PER_GETN) < 0) {
close(evpd->ed_port);
mm_free(evpd);
- return (NULL);
+ return NULL;
}
- evpd->ed_nevents = DEFAULT_NFDS;
- for (i = 0; i < EVENTS_PER_GETN; i++)
- evpd->ed_pending[i] = -1;
+
+ evpd->ed_npending = 0;
evsig_init(base);
return (evpd);
}
+static int
+grow(struct evport_data *data, int min_events)
+{
+ int newsize;
+ int *new_pending;
+ port_event_t *new_pevtlist;
+ if (data->ed_maxevents) {
+ newsize = data->ed_maxevents;
+ do {
+ newsize *= 2;
+ } while (newsize < min_events);
+ } else {
+ newsize = min_events;
+ }
+
+ new_pending = mm_realloc(data->ed_pending, sizeof(int)*newsize);
+ if (new_pending == NULL)
+ return -1;
+ data->ed_pending = new_pending;
+ new_pevtlist = mm_realloc(data->ed_pevtlist, sizeof(port_event_t)*newsize);
+ if (new_pevtlist == NULL)
+ return -1;
+ data->ed_pevtlist = new_pevtlist;
+
+ data->ed_maxevents = newsize;
+ return 0;
+}
+
#ifdef CHECK_INVARIANTS
/*
* Checks some basic properties about the evport_data structure. Because it
@@ -178,9 +200,7 @@ static void
check_evportop(struct evport_data *evpd)
{
EVUTIL_ASSERT(evpd);
- EVUTIL_ASSERT(evpd->ed_nevents > 0);
EVUTIL_ASSERT(evpd->ed_port > 0);
- EVUTIL_ASSERT(evpd->ed_fds > 0);
}
/*
@@ -196,7 +216,6 @@ check_event(port_event_t* pevt)
* PORT_SOURCE_FD.
*/
EVUTIL_ASSERT(pevt->portev_source == PORT_SOURCE_FD);
- EVUTIL_ASSERT(pevt->portev_user == NULL);
}
#else
@@ -205,33 +224,6 @@ check_event(port_event_t* pevt)
#endif /* CHECK_INVARIANTS */
/*
- * Doubles the size of the allocated file descriptor array.
- */
-static int
-grow(struct evport_data *epdp, int factor)
-{
- struct fd_info *tmp;
- int oldsize = epdp->ed_nevents;
- int newsize = factor * oldsize;
- EVUTIL_ASSERT(factor > 1);
-
- check_evportop(epdp);
-
- tmp = mm_realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
- if (NULL == tmp)
- return -1;
- epdp->ed_fds = tmp;
- memset((char*) (epdp->ed_fds + oldsize), 0,
- (newsize - oldsize)*sizeof(struct fd_info));
- epdp->ed_nevents = newsize;
-
- check_evportop(epdp);
-
- return 0;
-}
-
-
-/*
* (Re)associates the given file descriptor with the event port. The OS events
* are specified (implicitly) from the fd_info struct.
*/
@@ -242,7 +234,7 @@ reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
if (sysevents != 0) {
if (port_associate(epdp->ed_port, PORT_SOURCE_FD,
- fd, sysevents, NULL) == -1) {
+ fd, sysevents, fdip) == -1) {
event_warn("port_associate");
return (-1);
}
@@ -263,12 +255,12 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
{
int i, res;
struct evport_data *epdp = base->evbase;
- port_event_t pevtlist[EVENTS_PER_GETN];
+ port_event_t *pevtlist = epdp->ed_pevtlist;
/*
* port_getn will block until it has at least nevents events. It will
* also return how many it's given us (which may be more than we asked
- * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
+ * for, as long as it's less than our maximum (ed_maxevents)) in
* nevents.
*/
int nevents = 1;
@@ -291,22 +283,25 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
* last time which need reassociation. See comment at the end of the
* loop below.
*/
- for (i = 0; i < EVENTS_PER_GETN; ++i) {
+ for (i = 0; i < epdp->ed_npending; ++i) {
struct fd_info *fdi = NULL;
- if (epdp->ed_pending[i] != -1) {
- fdi = &(epdp->ed_fds[epdp->ed_pending[i]]);
+ const int fd = epdp->ed_pending[i];
+ if (fd != -1) {
+ /* We might have cleared out this event; we need
+ * to be sure that it's still set. */
+ fdi = evmap_io_get_fdinfo(&base->io, fd);
}
if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
- int fd = epdp->ed_pending[i];
reassociate(epdp, fdi, fd);
- epdp->ed_pending[i] = -1;
+ /* epdp->ed_pending[i] = -1; */
+ fdi->pending_idx_plus_1 = 0;
}
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
- res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
+ res = port_getn(epdp->ed_port, pevtlist, epdp->ed_maxevents,
(unsigned int *) &nevents, ts_p);
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
@@ -326,13 +321,15 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
event_debug(("%s: port_getn reports %d events", __func__, nevents));
for (i = 0; i < nevents; ++i) {
- struct fd_info *fdi;
port_event_t *pevt = &pevtlist[i];
int fd = (int) pevt->portev_object;
+ struct fd_info *fdi = pevt->portev_user;
+ /*EVUTIL_ASSERT(evmap_io_get_fdinfo(&base->io, fd) == fdi);*/
check_evportop(epdp);
check_event(pevt);
epdp->ed_pending[i] = fd;
+ fdi->pending_idx_plus_1 = i + 1;
/*
* Figure out what kind of event it was
@@ -354,11 +351,16 @@ evport_dispatch(struct event_base *base, struct timeval *tv)
if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL))
res |= EV_READ|EV_WRITE;
- EVUTIL_ASSERT(epdp->ed_nevents > fd);
- fdi = &(epdp->ed_fds[fd]);
-
evmap_io_active(base, fd, res);
} /* end of all events gotten */
+ epdp->ed_npending = nevents;
+
+ if (nevents == epdp->ed_maxevents &&
+ epdp->ed_maxevents < MAX_EVENTS_PER_GETN) {
+ /* we used all the space this time. We should be ready
+ * for more events next time around. */
+ grow(epdp, epdp->ed_maxevents * 2);
+ }
check_evportop(epdp);
@@ -375,27 +377,10 @@ static int
evport_add(struct event_base *base, int fd, short old, short events, void *p)
{
struct evport_data *evpd = base->evbase;
- struct fd_info *fdi;
- int factor;
- (void)p;
+ struct fd_info *fdi = p;
check_evportop(evpd);
- /*
- * If necessary, grow the file descriptor info table
- */
-
- factor = 1;
- while (fd >= factor * evpd->ed_nevents)
- factor *= 2;
-
- if (factor > 1) {
- if (-1 == grow(evpd, factor)) {
- return (-1);
- }
- }
-
- fdi = &evpd->ed_fds[fd];
fdi->fdi_what |= events;
return reassociate(evpd, fdi, fd);
@@ -409,29 +394,12 @@ static int
evport_del(struct event_base *base, int fd, short old, short events, void *p)
{
struct evport_data *evpd = base->evbase;
- struct fd_info *fdi;
- int i;
- int associated = 1;
- (void)p;
+ struct fd_info *fdi = p;
+ int associated = ! fdi->pending_idx_plus_1;
check_evportop(evpd);
- if (evpd->ed_nevents < fd) {
- return (-1);
- }
-
- for (i = 0; i < EVENTS_PER_GETN; ++i) {
- if (evpd->ed_pending[i] == fd) {
- associated = 0;
- break;
- }
- }
-
- fdi = &evpd->ed_fds[fd];
- if (events & EV_READ)
- fdi->fdi_what &= ~EV_READ;
- if (events & EV_WRITE)
- fdi->fdi_what &= ~EV_WRITE;
+ fdi->fdi_what &= ~(events &(EV_READ|EV_WRITE));
if (associated) {
if (!FDI_HAS_EVENTS(fdi) &&
@@ -451,7 +419,10 @@ evport_del(struct event_base *base, int fd, short old, short events, void *p)
}
} else {
if ((fdi->fdi_what & (EV_READ|EV_WRITE)) == 0) {
+ const int i = fdi->pending_idx_plus_1 - 1;
+ EVUTIL_ASSERT(evpd->ed_pending[i] == fd);
evpd->ed_pending[i] = -1;
+ fdi->pending_idx_plus_1 = 0;
}
}
return 0;
@@ -467,7 +438,10 @@ evport_dealloc(struct event_base *base)
close(evpd->ed_port);
- if (evpd->ed_fds)
- mm_free(evpd->ed_fds);
+ if (evpd->ed_pending)
+ mm_free(evpd->ed_pending);
+ if (evpd->ed_pevtlist)
+ mm_free(evpd->ed_pevtlist);
+
mm_free(evpd);
}
diff --git a/evrpc.c b/evrpc.c
index 4bf3d2b4..03bd3754 100644
--- a/evrpc.c
+++ b/evrpc.c
@@ -25,8 +25,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <windows.h>
@@ -34,7 +35,7 @@
#endif
#include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#endif
#ifdef _EVENT_HAVE_SYS_TIME_H
@@ -43,7 +44,7 @@
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <unistd.h>
#endif
#include <errno.h>
@@ -595,8 +596,8 @@ evrpc_pool_add_connection(struct evrpc_pool *pool,
* unless a timeout was specifically set for a connection,
* the connection inherits the timeout from the pool.
*/
- if (connection->timeout == -1)
- connection->timeout = pool->timeout;
+ if (!evutil_timerisset(&connection->timeout))
+ evhttp_connection_set_timeout(connection, pool->timeout);
/*
* if we have any requests pending, schedule them with the new
@@ -623,7 +624,7 @@ evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs)
{
struct evhttp_connection *evcon;
TAILQ_FOREACH(evcon, &pool->connections, next) {
- evcon->timeout = timeout_in_secs;
+ evhttp_connection_set_timeout(evcon, timeout_in_secs);
}
pool->timeout = timeout_in_secs;
}
diff --git a/evthread-internal.h b/evthread-internal.h
index ccfcdde8..26a26da1 100644
--- a/evthread-internal.h
+++ b/evthread-internal.h
@@ -30,13 +30,15 @@
extern "C" {
#endif
-#include "event2/thread.h"
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include "event2/thread.h"
#include "util-internal.h"
struct event_base;
-#ifndef WIN32
+#ifndef _WIN32
/* On Windows, the way we currently make DLLs, it's not allowed for us to
* have shared global structures. Thus, we only do the direct-call-to-function
* code path if we know that the local shared library system supports it.
diff --git a/evthread.c b/evthread.c
index 90e195d5..c20ab243 100644
--- a/evthread.c
+++ b/evthread.c
@@ -25,6 +25,7 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
@@ -146,7 +147,10 @@ evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs)
return 0;
}
+#define DEBUG_LOCK_SIG 0xdeb0b10c
+
struct debug_lock {
+ unsigned signature;
unsigned locktype;
unsigned long held_by;
/* XXXX if we ever use read-write locks, we will need a separate
@@ -170,6 +174,7 @@ debug_lock_alloc(unsigned locktype)
} else {
result->lock = NULL;
}
+ result->signature = DEBUG_LOCK_SIG;
result->locktype = locktype;
result->count = 0;
result->held_by = 0;
@@ -182,18 +187,21 @@ debug_lock_free(void *lock_, unsigned locktype)
struct debug_lock *lock = lock_;
EVUTIL_ASSERT(lock->count == 0);
EVUTIL_ASSERT(locktype == lock->locktype);
+ EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
if (_original_lock_fns.free) {
_original_lock_fns.free(lock->lock,
lock->locktype|EVTHREAD_LOCKTYPE_RECURSIVE);
}
lock->lock = NULL;
lock->count = -100;
+ lock->signature = 0x12300fda;
mm_free(lock);
}
static void
evthread_debug_lock_mark_locked(unsigned mode, struct debug_lock *lock)
{
+ EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
++lock->count;
if (!(lock->locktype & EVTHREAD_LOCKTYPE_RECURSIVE))
EVUTIL_ASSERT(lock->count == 1);
@@ -226,12 +234,15 @@ debug_lock_lock(unsigned mode, void *lock_)
static void
evthread_debug_lock_mark_unlocked(unsigned mode, struct debug_lock *lock)
{
+ EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE)
EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE));
else
EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0);
if (_evthread_id_fn) {
- EVUTIL_ASSERT(lock->held_by == _evthread_id_fn());
+ unsigned long me;
+ me = _evthread_id_fn();
+ EVUTIL_ASSERT(lock->held_by == me);
if (lock->count == 1)
lock->held_by = 0;
}
@@ -256,6 +267,7 @@ debug_cond_wait(void *_cond, void *_lock, const struct timeval *tv)
int r;
struct debug_lock *lock = _lock;
EVUTIL_ASSERT(lock);
+ EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
EVLOCK_ASSERT_LOCKED(_lock);
evthread_debug_lock_mark_unlocked(0, lock);
r = _original_cond_fns.wait_condition(_cond, lock->lock, tv);
diff --git a/evthread_pthread.c b/evthread_pthread.c
index c4d06ea5..24a8639e 100644
--- a/evthread_pthread.c
+++ b/evthread_pthread.c
@@ -24,9 +24,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-/* With glibc we need to define this to get PTHREAD_MUTEX_RECURSIVE. */
-#define _GNU_SOURCE
+/* With glibc we need to define _GNU_SOURCE to get PTHREAD_MUTEX_RECURSIVE.
+ * This comes from evconfig-private.h
+ */
#include <pthread.h>
struct event_base;
diff --git a/evthread_win32.c b/evthread_win32.c
index 2389d721..20f4f2cc 100644
--- a/evthread_win32.c
+++ b/evthread_win32.c
@@ -24,8 +24,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#ifndef _WIN32_WINNT
/* Minimum required for InitializeCriticalSectionAndSpinCount */
#define _WIN32_WINNT 0x0403
diff --git a/evutil.c b/evutil.c
index 56c99097..306f037c 100644
--- a/evutil.c
+++ b/evutil.c
@@ -25,10 +25,9 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#define _GNU_SOURCE
-
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define WIN32_LEAN_AND_MEAN
@@ -36,6 +35,10 @@
#undef WIN32_LEAN_AND_MEAN
#include <io.h>
#include <tchar.h>
+#undef _WIN32_WINNT
+/* For structs needed by GetAdaptersAddresses */
+#define _WIN32_WINNT 0x0501
+#include <iphlpapi.h>
#endif
#include <sys/types.h>
@@ -64,12 +67,18 @@
#ifdef _EVENT_HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
-
+#if !defined(_EVENT_HAVE_NANOSLEEP) && !defined(EVENT_HAVE_USLEEP) && \
+ !defined(_WIN32)
+#include <sys/select.h>
+#endif
#ifndef _EVENT_HAVE_GETTIMEOFDAY
#include <sys/timeb.h>
#include <time.h>
#endif
#include <sys/stat.h>
+#ifdef _EVENT_HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
#include "event2/util.h"
#include "util-internal.h"
@@ -79,7 +88,7 @@
#include "strlcpy-internal.h"
#include "ipv6-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#define open _open
#define read _read
#define close _close
@@ -131,7 +140,7 @@ evutil_read_file(const char *filename, char **content_out, size_t *len_out,
return -2;
}
read_so_far = 0;
-#ifdef WIN32
+#ifdef _WIN32
#define N_TO_READ(x) ((x) > INT_MAX) ? INT_MAX : ((int)(x))
#else
#define N_TO_READ(x) (x)
@@ -157,7 +166,7 @@ evutil_read_file(const char *filename, char **content_out, size_t *len_out,
int
evutil_socketpair(int family, int type, int protocol, evutil_socket_t fd[2])
{
-#ifndef WIN32
+#ifndef _WIN32
return socketpair(family, type, protocol, fd);
#else
return evutil_ersatz_socketpair(family, type, protocol, fd);
@@ -175,7 +184,7 @@ evutil_ersatz_socketpair(int family, int type, int protocol,
* for now, and really, when localhost is down sometimes, we
* have other problems too.
*/
-#ifdef WIN32
+#ifdef _WIN32
#define ERR(e) WSA##e
#else
#define ERR(e) e
@@ -269,7 +278,7 @@ evutil_ersatz_socketpair(int family, int type, int protocol,
int
evutil_make_socket_nonblocking(evutil_socket_t fd)
{
-#ifdef WIN32
+#ifdef _WIN32
{
u_long nonblocking = 1;
if (ioctlsocket(fd, FIONBIO, &nonblocking) == SOCKET_ERROR) {
@@ -296,7 +305,7 @@ evutil_make_socket_nonblocking(evutil_socket_t fd)
int
evutil_make_listen_socket_reuseable(evutil_socket_t sock)
{
-#ifndef WIN32
+#ifndef _WIN32
int one = 1;
/* REUSEADDR on Unix means, "don't hang on to this address after the
* listener is closed." On Windows, though, it means "don't keep other
@@ -311,7 +320,7 @@ evutil_make_listen_socket_reuseable(evutil_socket_t sock)
int
evutil_make_socket_closeonexec(evutil_socket_t fd)
{
-#if !defined(WIN32) && defined(_EVENT_HAVE_SETFD)
+#if !defined(_WIN32) && defined(_EVENT_HAVE_SETFD)
int flags;
if ((flags = fcntl(fd, F_GETFD, NULL)) < 0) {
event_warn("fcntl(%d, F_GETFD)", fd);
@@ -328,7 +337,7 @@ evutil_make_socket_closeonexec(evutil_socket_t fd)
int
evutil_closesocket(evutil_socket_t sock)
{
-#ifndef WIN32
+#ifndef _WIN32
return close(sock);
#else
return closesocket(sock);
@@ -342,7 +351,7 @@ evutil_strtoll(const char *s, char **endptr, int base)
return (ev_int64_t)strtoll(s, endptr, base);
#elif _EVENT_SIZEOF_LONG == 8
return (ev_int64_t)strtol(s, endptr, base);
-#elif defined(WIN32) && defined(_MSC_VER) && _MSC_VER < 1300
+#elif defined(_WIN32) && defined(_MSC_VER) && _MSC_VER < 1300
/* XXXX on old versions of MS APIs, we only support base
* 10. */
ev_int64_t r;
@@ -358,7 +367,7 @@ evutil_strtoll(const char *s, char **endptr, int base)
if (endptr)
*endptr = (char*) s;
return r;
-#elif defined(WIN32)
+#elif defined(_WIN32)
return (ev_int64_t) _strtoi64(s, endptr, base);
#elif defined(_EVENT_SIZEOF_LONG_LONG) && _EVENT_SIZEOF_LONG_LONG == 8
long long r;
@@ -396,31 +405,46 @@ evutil_strtoll(const char *s, char **endptr, int base)
}
#ifndef _EVENT_HAVE_GETTIMEOFDAY
-/* No gettimeofday; this muse be windows. */
+/* No gettimeofday; this must be windows. */
int
evutil_gettimeofday(struct timeval *tv, struct timezone *tz)
{
- struct _timeb tb;
+#ifdef _MSC_VER
+#define U64_LITERAL(n) n##ui64
+#else
+#define U64_LITERAL(n) n##llu
+#endif
+
+ /* Conversion logic taken from Tor, which in turn took it
+ * from Perl. GetSystemTimeAsFileTime returns its value as
+ * an unaligned (!) 64-bit value containing the number of
+ * 100-nanosecond intervals since 1 January 1601 UTC. */
+#define EPOCH_BIAS U64_LITERAL(116444736000000000)
+#define UNITS_PER_SEC U64_LITERAL(10000000)
+#define USEC_PER_SEC U64_LITERAL(1000000)
+#define UNITS_PER_USEC U64_LITERAL(10)
+ union {
+ FILETIME ft_ft;
+ ev_uint64_t ft_64;
+ } ft;
if (tv == NULL)
return -1;
- /* XXXX
- * _ftime is not the greatest interface here; GetSystemTimeAsFileTime
- * would give us better resolution, whereas something cobbled together
- * with GetTickCount could maybe give us monotonic behavior.
- *
- * Either way, I think this value might be skewed to ignore the
- * timezone, and just return local time. That's not so good.
- */
- _ftime(&tb);
- tv->tv_sec = (long) tb.time;
- tv->tv_usec = ((int) tb.millitm) * 1000;
+ GetSystemTimeAsFileTime(&ft.ft_ft);
+
+ if (EVUTIL_UNLIKELY(ft.ft_64 < EPOCH_BIAS)) {
+ /* Time before the unix epoch. */
+ return -1;
+ }
+ ft.ft_64 -= EPOCH_BIAS;
+ tv->tv_sec = (long) (ft.ft_64 / UNITS_PER_SEC);
+ tv->tv_usec = (long) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC);
return 0;
}
#endif
-#ifdef WIN32
+#ifdef _WIN32
int
evutil_socket_geterror(evutil_socket_t sock)
{
@@ -520,28 +544,150 @@ static int have_checked_interfaces, had_ipv4_address, had_ipv6_address;
*/
#define EVUTIL_V4ADDR_IS_CLASSD(addr) ((((addr)>>24) & 0xf0) == 0xe0)
+static void
+evutil_found_ifaddr(const struct sockaddr *sa)
+{
+ const char ZEROES[] = "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00";
+
+ if (sa->sa_family == AF_INET) {
+ const struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ ev_uint32_t addr = ntohl(sin->sin_addr.s_addr);
+ if (addr == 0 ||
+ EVUTIL_V4ADDR_IS_LOCALHOST(addr) ||
+ EVUTIL_V4ADDR_IS_CLASSD(addr)) {
+ /* Not actually a usable external address. */
+ } else {
+ event_debug(("Detected an IPv4 interface"));
+ had_ipv4_address = 1;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ const unsigned char *addr =
+ (unsigned char*)sin6->sin6_addr.s6_addr;
+ if (!memcmp(addr, ZEROES, 8) ||
+ ((addr[0] & 0xfe) == 0xfc) ||
+ (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80) ||
+ (addr[0] == 0xfe && (addr[1] & 0xc0) == 0xc0) ||
+ (addr[0] == 0xff)) {
+ /* This is a reserved, ipv4compat, ipv4map, loopback,
+ * link-local, multicast, or unspecified address. */
+ } else {
+ event_debug(("Detected an IPv6 interface"));
+ had_ipv6_address = 1;
+ }
+ }
+}
+
+#ifdef _WIN32
+typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)(
+ ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG);
+#endif
+
+static int
+evutil_check_ifaddrs(void)
+{
+#if defined(_EVENT_HAVE_GETIFADDRS)
+ /* Most free Unixy systems provide getifaddrs, which gives us a linked list
+ * of struct ifaddrs. */
+ struct ifaddrs *ifa = NULL;
+ const struct ifaddrs *i;
+ if (getifaddrs(&ifa) < 0) {
+ event_warn("Unable to call getifaddrs()");
+ return -1;
+ }
+
+ for (i = ifa; i; i = i->ifa_next) {
+ if (!i->ifa_addr)
+ continue;
+ evutil_found_ifaddr(i->ifa_addr);
+ }
+
+ freeifaddrs(ifa);
+ return 0;
+#elif defined(_WIN32)
+ /* Windows XP began to provide GetAdaptersAddresses. Windows 2000 had a
+ "GetAdaptersInfo", but that's deprecated; let's just try
+ GetAdaptersAddresses and fall back to connect+getsockname.
+ */
+ HANDLE lib = evutil_load_windows_system_library(TEXT("ihplapi.dll"));
+ GetAdaptersAddresses_fn_t fn;
+ ULONG size, res;
+ IP_ADAPTER_ADDRESSES *addresses = NULL, *address;
+ int result = -1;
+
+#define FLAGS (GAA_FLAG_SKIP_ANYCAST | \
+ GAA_FLAG_SKIP_MULTICAST | \
+ GAA_FLAG_SKIP_DNS_SERVER)
+
+ if (!lib)
+ goto done;
+
+ if (!(fn = (GetAdaptersAddresses_fn_t) GetProcAddress(lib, "GetAdaptersAddresses")))
+ goto done;
+
+ /* Guess how much space we need. */
+ size = 15*1024;
+ addresses = mm_malloc(size);
+ if (!addresses)
+ goto done;
+ res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size);
+ if (res == ERROR_BUFFER_OVERFLOW) {
+ /* we didn't guess that we needed enough space; try again */
+ mm_free(addresses);
+ addresses = mm_malloc(size);
+ if (!addresses)
+ goto done;
+ res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size);
+ }
+ if (res != NO_ERROR)
+ goto done;
+
+ for (address = addresses; address; address = address->Next) {
+ IP_ADAPTER_UNICAST_ADDRESS *a;
+ for (a = address->FirstUnicastAddress; a; a = a->Next) {
+ /* Yes, it's a linked list inside a linked list */
+ struct sockaddr *sa = a->Address.lpSockaddr;
+ evutil_found_ifaddr(sa);
+ }
+ }
+
+ result = 0;
+done:
+ if (lib)
+ FreeLibrary(lib);
+ if (addresses)
+ mm_free(addresses);
+ return result;
+#else
+ return -1;
+#endif
+}
+
/* Test whether we have an ipv4 interface and an ipv6 interface. Return 0 if
* the test seemed successful. */
static int
evutil_check_interfaces(int force_recheck)
{
- const char ZEROES[] = "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00";
evutil_socket_t fd = -1;
struct sockaddr_in sin, sin_out;
struct sockaddr_in6 sin6, sin6_out;
ev_socklen_t sin_out_len = sizeof(sin_out);
ev_socklen_t sin6_out_len = sizeof(sin6_out);
int r;
- char buf[128];
if (have_checked_interfaces && !force_recheck)
return 0;
- /* To check whether we have an interface open for a given protocol, we
- * try to make a UDP 'connection' to a remote host on the internet.
- * We don't actually use it, so the address doesn't matter, but we
- * want to pick one that keep us from using a host- or link-local
- * interface. */
+ if (evutil_check_ifaddrs() == 0) {
+ /* Use a nice sane interface, if this system has one. */
+ return 0;
+ }
+
+ /* Ugh. There was no nice sane interface. So to check whether we have
+ * an interface open for a given protocol, will try to make a UDP
+ * 'connection' to a remote host on the internet. We don't actually
+ * use it, so the address doesn't matter, but we want to pick one that
+ * keep us from using a host- or link-local interface. */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(53);
@@ -562,21 +708,7 @@ evutil_check_interfaces(int force_recheck)
connect(fd, (struct sockaddr*)&sin, sizeof(sin)) == 0 &&
getsockname(fd, (struct sockaddr*)&sin_out, &sin_out_len) == 0) {
/* We might have an IPv4 interface. */
- ev_uint32_t addr = ntohl(sin_out.sin_addr.s_addr);
- if (addr == 0 ||
- EVUTIL_V4ADDR_IS_LOCALHOST(addr) ||
- EVUTIL_V4ADDR_IS_CLASSD(addr)) {
- evutil_inet_ntop(AF_INET, &sin_out.sin_addr,
- buf, sizeof(buf));
- /* This is a reserved, ipv4compat, ipv4map, loopback,
- * link-local or unspecified address. The host should
- * never have given it to us; it could never connect
- * to sin. */
- event_warnx("Got a strange local ipv4 address %s",buf);
- } else {
- event_debug(("Detected an IPv4 interface"));
- had_ipv4_address = 1;
- }
+ evutil_found_ifaddr((struct sockaddr*) &sin_out);
}
if (fd >= 0)
evutil_closesocket(fd);
@@ -585,21 +717,7 @@ evutil_check_interfaces(int force_recheck)
connect(fd, (struct sockaddr*)&sin6, sizeof(sin6)) == 0 &&
getsockname(fd, (struct sockaddr*)&sin6_out, &sin6_out_len) == 0) {
/* We might have an IPv6 interface. */
- const unsigned char *addr =
- (unsigned char*)sin6_out.sin6_addr.s6_addr;
- if (!memcmp(addr, ZEROES, 8) ||
- (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80)) {
- /* This is a reserved, ipv4compat, ipv4map, loopback,
- * link-local or unspecified address. The host should
- * never have given it to us; it could never connect
- * to sin6. */
- evutil_inet_ntop(AF_INET6, &sin6_out.sin6_addr,
- buf, sizeof(buf));
- event_warnx("Got a strange local ipv6 address %s",buf);
- } else {
- event_debug(("Detected an IPv4 interface"));
- had_ipv6_address = 1;
- }
+ evutil_found_ifaddr((struct sockaddr*) &sin6_out);
}
if (fd >= 0)
@@ -700,7 +818,7 @@ evutil_parse_servname(const char *servname, const char *protocol,
int n = parse_numeric_servname(servname);
if (n>=0)
return n;
-#if defined(_EVENT_HAVE_GETSERVBYNAME) || defined(WIN32)
+#if defined(_EVENT_HAVE_GETSERVBYNAME) || defined(_WIN32)
if (!(hints->ai_flags & EVUTIL_AI_NUMERICSERV)) {
struct servent *ent = getservbyname(servname, protocol);
if (ent) {
@@ -1200,7 +1318,7 @@ evutil_getaddrinfo(const char *nodename, const char *servname,
* ever resolving even a literal IPv6 address when
* ai_addrtype is PF_UNSPEC.
*/
-#ifdef WIN32
+#ifdef _WIN32
{
int tmp_port;
err = evutil_getaddrinfo_common(nodename,servname,&hints,
@@ -1297,7 +1415,7 @@ evutil_getaddrinfo(const char *nodename, const char *servname,
/* fall back to gethostbyname. */
/* XXXX This needs a lock everywhere but Windows. */
ent = gethostbyname(nodename);
-#ifdef WIN32
+#ifdef _WIN32
err = WSAGetLastError();
#else
err = h_errno;
@@ -1433,7 +1551,7 @@ evutil_gai_strerror(int err)
case EVUTIL_EAI_SYSTEM:
return "system error";
default:
-#if defined(USE_NATIVE_GETADDRINFO) && defined(WIN32)
+#if defined(USE_NATIVE_GETADDRINFO) && defined(_WIN32)
return gai_strerrorA(err);
#elif defined(USE_NATIVE_GETADDRINFO)
return gai_strerror(err);
@@ -1443,7 +1561,7 @@ evutil_gai_strerror(int err)
}
}
-#ifdef WIN32
+#ifdef _WIN32
#define E(code, s) { code, (s " [" #code " ]") }
static struct { int code; const char *msg; } windows_socket_errors[] = {
E(WSAEINTR, "Interrupted function call"),
@@ -2077,7 +2195,7 @@ evutil_getenv(const char *varname)
long
_evutil_weakrand(void)
{
-#ifdef WIN32
+#ifdef _WIN32
return rand();
#else
return random();
@@ -2136,7 +2254,7 @@ evutil_hex_char_to_int(char c)
return -1;
}
-#ifdef WIN32
+#ifdef _WIN32
HANDLE
evutil_load_windows_system_library(const TCHAR *library_name)
{
@@ -2151,3 +2269,28 @@ evutil_load_windows_system_library(const TCHAR *library_name)
}
#endif
+void
+evutil_usleep(const struct timeval *tv)
+{
+ if (!tv)
+ return;
+#if defined(_WIN32)
+ {
+ long msec = evutil_tv_to_msec(tv);
+ Sleep((DWORD)msec);
+ }
+#elif defined(_EVENT_HAVE_NANOSLEEP_X)
+ {
+ struct timespec ts;
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec*1000;
+ nanosleep(&ts, NULL);
+ }
+#elif defined(_EVENT_HAVE_USLEEP)
+ /* Some systems don't like to usleep more than 999999 usec */
+ sleep(tv->tv_sec);
+ usleep(tv->tv_usec);
+#else
+ select(0, NULL, NULL, NULL, tv);
+#endif
+}
diff --git a/evutil_rand.c b/evutil_rand.c
index 3cc163d3..b8623876 100644
--- a/evutil_rand.c
+++ b/evutil_rand.c
@@ -33,6 +33,7 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <limits.h>
diff --git a/http-internal.h b/http-internal.h
index 5f66673e..6910b350 100644
--- a/http-internal.h
+++ b/http-internal.h
@@ -83,9 +83,12 @@ struct evhttp_connection {
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */
- int timeout; /* timeout in seconds for events */
+ struct timeval timeout; /* timeout for events */
int retry_cnt; /* retry count */
int retry_max; /* maximum number of retries */
+ struct timeval initial_retry_timeout; /* Timeout for low long to wait
+ * after first failing attempt
+ * before retry */
enum evhttp_connection_state state;
@@ -152,7 +155,7 @@ struct evhttp {
/* NULL if this server is not a vhost */
char *vhost_pattern;
- int timeout;
+ struct timeval timeout;
size_t default_max_headers_size;
ev_uint64_t default_max_body_size;
@@ -165,6 +168,8 @@ struct evhttp {
don't match. */
void (*gencb)(struct evhttp_request *req, void *);
void *gencbarg;
+ struct bufferevent* (*bevcb)(struct event_base *, void *);
+ void *bevcbarg;
struct event_base *base;
};
diff --git a/http.c b/http.c
index b9687df6..cf088927 100644
--- a/http.c
+++ b/http.c
@@ -26,6 +26,7 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#ifdef _EVENT_HAVE_SYS_PARAM_H
#include <sys/param.h>
@@ -41,7 +42,7 @@
#include <sys/ioccom.h>
#endif
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -63,7 +64,7 @@
#include <netdb.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
@@ -71,7 +72,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <syslog.h>
#endif
#include <signal.h>
@@ -91,7 +92,6 @@
#include "event2/event.h"
#include "event2/buffer.h"
#include "event2/bufferevent.h"
-#include "event2/bufferevent_compat.h"
#include "event2/http_struct.h"
#include "event2/http_compat.h"
#include "event2/util.h"
@@ -352,23 +352,6 @@ evhttp_response_needs_body(struct evhttp_request *req)
req->type != EVHTTP_REQ_HEAD);
}
-/** Helper: adds the event 'ev' with the timeout 'timeout', or with
- * default_timeout if timeout is -1.
- */
-static int
-evhttp_add_event(struct event *ev, int timeout, int default_timeout)
-{
- if (timeout != 0) {
- struct timeval tv;
-
- evutil_timerclear(&tv);
- tv.tv_sec = timeout != -1 ? timeout : default_timeout;
- return event_add(ev, &tv);
- } else {
- return event_add(ev, NULL);
- }
-}
-
/** Helper: called after we've added some data to an evcon's bufferevent's
* output buffer. Sets the evconn's writing-is-done callback, and puts
* the bufferevent into writing mode.
@@ -496,12 +479,12 @@ evhttp_maybe_add_date_header(struct evkeyvalq *headers)
{
if (evhttp_find_header(headers, "Date") == NULL) {
char date[50];
-#ifndef WIN32
+#ifndef _WIN32
struct tm cur;
#endif
struct tm *cur_p;
time_t t = time(NULL);
-#ifdef WIN32
+#ifdef _WIN32
cur_p = gmtime(&t);
#else
gmtime_r(&t, &cur);
@@ -1284,11 +1267,23 @@ evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
struct evcon_requestq requests;
if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
+ struct timeval tv_retry = evcon->initial_retry_timeout;
+ int i;
evtimer_assign(&evcon->retry_ev, evcon->base, evhttp_connection_retry, evcon);
/* XXXX handle failure from evhttp_add_event */
- evhttp_add_event(&evcon->retry_ev,
- MIN(3600, 2 << evcon->retry_cnt),
- HTTP_CONNECT_TIMEOUT);
+ for (i=0; i < evcon->retry_cnt; ++i) {
+ tv_retry.tv_usec *= 2;
+ if (tv_retry.tv_usec > 1000000) {
+ tv_retry.tv_usec -= 1000000;
+ tv_retry.tv_sec += 1;
+ }
+ tv_retry.tv_sec *= 2;
+ if (tv_retry.tv_sec > 3600) {
+ tv_retry.tv_sec = 3600;
+ tv_retry.tv_usec = 0;
+ }
+ }
+ event_add(&evcon->retry_ev, &tv_retry);
evcon->retry_cnt++;
return;
}
@@ -1374,6 +1369,7 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
} else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
+ } else if (what == BEV_EVENT_CONNECTED) {
} else {
evhttp_connection_fail(evcon, EVCON_HTTP_BUFFER_ERROR);
}
@@ -1394,7 +1390,7 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
* when connecting to a local address. the cleanup is going
* to reschedule this function call.
*/
-#ifndef WIN32
+#ifndef _WIN32
if (errno == ECONNREFUSED)
goto cleanup;
#endif
@@ -1432,14 +1428,12 @@ evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
evhttp_error_cb,
evcon);
- if (evcon->timeout == -1)
- bufferevent_settimeout(evcon->bufev,
- HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
- else {
- struct timeval tv;
- tv.tv_sec = evcon->timeout;
- tv.tv_usec = 0;
- bufferevent_set_timeouts(evcon->bufev, &tv, &tv);
+ if (!evutil_timerisset(&evcon->timeout)) {
+ const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
+ const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
+ bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
+ } else {
+ bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
}
/* try to start requests that have queued up on this connection */
@@ -1523,6 +1517,8 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
char *version;
const char *hostname;
const char *scheme;
+ size_t method_len;
+ enum evhttp_cmd_type type;
/* Parse the request line */
method = strsep(&line, " ");
@@ -1535,30 +1531,122 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
if (line != NULL)
return (-1);
+ method_len = (uri - method) - 1;
+ type = _EVHTTP_REQ_UNKNOWN;
+
/* First line */
- if (strcmp(method, "GET") == 0) {
- req->type = EVHTTP_REQ_GET;
- } else if (strcmp(method, "POST") == 0) {
- req->type = EVHTTP_REQ_POST;
- } else if (strcmp(method, "HEAD") == 0) {
- req->type = EVHTTP_REQ_HEAD;
- } else if (strcmp(method, "PUT") == 0) {
- req->type = EVHTTP_REQ_PUT;
- } else if (strcmp(method, "DELETE") == 0) {
- req->type = EVHTTP_REQ_DELETE;
- } else if (strcmp(method, "OPTIONS") == 0) {
- req->type = EVHTTP_REQ_OPTIONS;
- } else if (strcmp(method, "TRACE") == 0) {
- req->type = EVHTTP_REQ_TRACE;
- } else if (strcmp(method, "PATCH") == 0) {
- req->type = EVHTTP_REQ_PATCH;
- } else {
- req->type = _EVHTTP_REQ_UNKNOWN;
- event_debug(("%s: bad method %s on request %p from %s",
+ switch (method_len) {
+ case 3:
+ /* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */
+
+ /* Since both GET and PUT share the same character 'T' at the end,
+ * if the string doesn't have 'T', we can immediately determine this
+ * is an invalid HTTP method */
+
+ if (method[2] != 'T') {
+ break;
+ }
+
+ switch (*method) {
+ case 'G':
+ /* This first byte is 'G', so make sure the next byte is
+ * 'E', if it isn't then this isn't a valid method */
+
+ if (method[1] == 'E') {
+ type = EVHTTP_REQ_GET;
+ }
+
+ break;
+ case 'P':
+ /* First byte is P, check second byte for 'U', if not,
+ * we know it's an invalid method */
+ if (method[1] == 'U') {
+ type = EVHTTP_REQ_PUT;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 4:
+ /* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */
+ switch (*method) {
+ case 'P':
+ if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
+ type = EVHTTP_REQ_POST;
+ }
+ break;
+ case 'H':
+ if (method[3] == 'D' && method[2] == 'A' && method[1] == 'E') {
+ type = EVHTTP_REQ_HEAD;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 5:
+ /* Method length is 5 bytes, which can only encompass PATCH and TRACE */
+ switch (*method) {
+ case 'P':
+ if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
+ type = EVHTTP_REQ_PATCH;
+ }
+ break;
+ case 'T':
+ if (method[4] == 'E' && method[3] == 'C' && method[2] == 'A' && method[1] == 'R') {
+ type = EVHTTP_REQ_TRACE;
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ case 6:
+ /* Method length is 6, only valid method 6 bytes in length is DELEte */
+
+ /* If the first byte isn't 'D' then it's invalid */
+ if (*method != 'D') {
+ break;
+ }
+
+ if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
+ type = EVHTTP_REQ_DELETE;
+ }
+
+ break;
+ case 7:
+ /* Method length is 7, only valid methods are "OPTIONS" and "CONNECT" */
+ switch (*method) {
+ case 'O':
+ if (method[6] == 'S' && method[5] == 'N' && method[4] == 'O' &&
+ method[3] == 'I' && method[2] == 'T' && method[1] == 'P') {
+ type = EVHTTP_REQ_OPTIONS;
+ }
+
+ break;
+ case 'C':
+ if (method[6] == 'T' && method[5] == 'C' && method[4] == 'E' &&
+ method[3] == 'N' && method[2] == 'N' && method[1] == 'O') {
+ type = EVHTTP_REQ_CONNECT;
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ } /* switch */
+
+ if (type == _EVHTTP_REQ_UNKNOWN) {
+ event_debug(("%s: bad method %s on request %p from %s",
__func__, method, req, req->remote_host));
- /* No error yet; we'll give a better error later when
- * we see that req->type is unsupported. */
+ /* No error yet; we'll give a better error later when
+ * we see that req->type is unsupported. */
}
+
+ req->type = type;
if (evhttp_parse_http_version(version, req) < 0)
return (-1);
@@ -2061,7 +2149,7 @@ evhttp_connection_new(const char *address, unsigned short port)
}
struct evhttp_connection *
-evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
+evhttp_connection_base_bufferevent_new(struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev,
const char *address, unsigned short port)
{
struct evhttp_connection *evcon = NULL;
@@ -2079,7 +2167,7 @@ evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
evcon->max_headers_size = EV_SIZE_MAX;
evcon->max_body_size = EV_SIZE_MAX;
- evcon->timeout = -1;
+ evutil_timerclear(&evcon->timeout);
evcon->retry_cnt = evcon->retry_max = 0;
if ((evcon->address = mm_strdup(address)) == NULL) {
@@ -2087,23 +2175,28 @@ evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
goto error;
}
- if ((evcon->bufev = bufferevent_new(-1,
- evhttp_read_cb,
- evhttp_write_cb,
- evhttp_error_cb, evcon)) == NULL) {
- event_warn("%s: bufferevent_new failed", __func__);
- goto error;
+ if (bev == NULL) {
+ if (!(bev = bufferevent_socket_new(base, -1, 0))) {
+ event_warn("%s: bufferevent_socket_new failed", __func__);
+ goto error;
+ }
}
+ bufferevent_setcb(bev, evhttp_read_cb, evhttp_write_cb, evhttp_error_cb, evcon);
+ evcon->bufev = bev;
+
evcon->state = EVCON_DISCONNECTED;
TAILQ_INIT(&evcon->requests);
+ evcon->initial_retry_timeout.tv_sec = 2;
+ evcon->initial_retry_timeout.tv_usec = 0;
+
if (base != NULL) {
evcon->base = base;
- bufferevent_base_set(base, evcon->bufev);
+ if (bufferevent_get_base(bev) != base)
+ bufferevent_base_set(base, evcon->bufev);
}
-
event_deferred_cb_init(&evcon->read_more_deferred_cb,
evhttp_deferred_read_cb, evcon);
@@ -2117,12 +2210,18 @@ evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
return (NULL);
}
-struct bufferevent *
-evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
+struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
{
return evcon->bufev;
}
+struct evhttp_connection *
+evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
+ const char *address, unsigned short port)
+{
+ return evhttp_connection_base_bufferevent_new(base, dnsbase, NULL, address, port);
+}
+
void
evhttp_connection_set_base(struct evhttp_connection *evcon,
struct event_base *base)
@@ -2137,14 +2236,41 @@ void
evhttp_connection_set_timeout(struct evhttp_connection *evcon,
int timeout_in_secs)
{
- evcon->timeout = timeout_in_secs;
+ if (timeout_in_secs == -1)
+ evhttp_connection_set_timeout_tv(evcon, NULL);
+ else {
+ struct timeval tv;
+ tv.tv_sec = timeout_in_secs;
+ tv.tv_usec = 0;
+ evhttp_connection_set_timeout_tv(evcon, &tv);
+ }
+}
- if (evcon->timeout == -1)
- bufferevent_settimeout(evcon->bufev,
- HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
- else
- bufferevent_settimeout(evcon->bufev,
- evcon->timeout, evcon->timeout);
+void
+evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon,
+ const struct timeval* tv)
+{
+ if (tv) {
+ evcon->timeout = *tv;
+ bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout);
+ } else {
+ const struct timeval read_tv = { HTTP_READ_TIMEOUT, 0 };
+ const struct timeval write_tv = { HTTP_WRITE_TIMEOUT, 0 };
+ evutil_timerclear(&evcon->timeout);
+ bufferevent_set_timeouts(evcon->bufev, &read_tv, &write_tv);
+ }
+}
+
+void
+evhttp_connection_set_initial_retry_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv)
+{
+ if (tv) {
+ evcon->initial_retry_timeout = *tv;
+ } else {
+ evutil_timerclear(&evcon->initial_retry_timeout);
+ evcon->initial_retry_timeout.tv_sec = 2;
+ }
}
void
@@ -2196,8 +2322,12 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
NULL /* evhttp_write_cb */,
evhttp_connection_cb,
evcon);
- bufferevent_settimeout(evcon->bufev, 0,
- evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT);
+ if (!evutil_timerisset(&evcon->timeout)) {
+ const struct timeval conn_tv = { HTTP_CONNECT_TIMEOUT, 0 };
+ bufferevent_set_timeouts(evcon->bufev, NULL, &conn_tv);
+ } else {
+ bufferevent_set_timeouts(evcon->bufev, NULL, &evcon->timeout);
+ }
/* make sure that we get a write callback */
bufferevent_enable(evcon->bufev, EV_WRITE);
@@ -3119,6 +3249,16 @@ evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd)
return (0);
}
+void
+evhttp_foreach_bound_socket(struct evhttp *http,
+ evhttp_bound_socket_foreach_fn *function,
+ void *argument)
+{
+ struct evhttp_bound_socket *bound;
+
+ TAILQ_FOREACH(bound, &http->sockets, next)
+ function(bound, argument);
+}
struct evhttp_bound_socket *
evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd)
@@ -3189,7 +3329,7 @@ evhttp_new_object(void)
return (NULL);
}
- http->timeout = -1;
+ evutil_timerclear(&http->timeout);
evhttp_set_max_headers_size(http, EV_SIZE_MAX);
evhttp_set_max_body_size(http, EV_SIZE_MAX);
evhttp_set_allowed_methods(http,
@@ -3360,7 +3500,24 @@ evhttp_remove_server_alias(struct evhttp *http, const char *alias)
void
evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
{
- http->timeout = timeout_in_secs;
+ if (timeout_in_secs == -1) {
+ evhttp_set_timeout_tv(http, NULL);
+ } else {
+ struct timeval tv;
+ tv.tv_sec = timeout_in_secs;
+ tv.tv_usec = 0;
+ evhttp_set_timeout_tv(http, &tv);
+ }
+}
+
+void
+evhttp_set_timeout_tv(struct evhttp* http, const struct timeval* tv)
+{
+ if (tv) {
+ http->timeout = *tv;
+ } else {
+ evutil_timerclear(&http->timeout);
+ }
}
void
@@ -3444,6 +3601,14 @@ evhttp_set_gencb(struct evhttp *http,
http->gencbarg = cbarg;
}
+void
+evhttp_set_bevcb(struct evhttp *http,
+ struct bufferevent* (*cb)(struct event_base *, void *), void *cbarg)
+{
+ http->bevcb = cb;
+ http->bevcbarg = cbarg;
+}
+
/*
* Request related functions
*/
@@ -3668,6 +3833,7 @@ evhttp_get_request_connection(
{
struct evhttp_connection *evcon;
char *hostname = NULL, *portname = NULL;
+ struct bufferevent* bev = NULL;
name_from_addr(sa, salen, &hostname, &portname);
if (hostname == NULL || portname == NULL) {
@@ -3680,8 +3846,11 @@ evhttp_get_request_connection(
__func__, hostname, portname, fd));
/* we need a connection object to put the http request on */
- evcon = evhttp_connection_base_new(
- http->base, NULL, hostname, atoi(portname));
+ if (http->bevcb != NULL) {
+ bev = (*http->bevcb)(http->base, http->bevcbarg);
+ }
+ evcon = evhttp_connection_base_bufferevent_new(
+ http->base, NULL, bev, hostname, atoi(portname));
mm_free(hostname);
mm_free(portname);
if (evcon == NULL)
@@ -3749,8 +3918,8 @@ evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
}
/* the timeout can be used by the server to close idle connections */
- if (http->timeout != -1)
- evhttp_connection_set_timeout(evcon, http->timeout);
+ if (evutil_timerisset(&http->timeout))
+ evhttp_connection_set_timeout_tv(evcon, &http->timeout);
/*
* if we want to accept more than one request on a connection,
diff --git a/include/event2/buffer.h b/include/event2/buffer.h
index 16236c4b..d152f3fe 100644
--- a/include/event2/buffer.h
+++ b/include/event2/buffer.h
@@ -105,11 +105,22 @@ struct evbuffer
that modifies or re-packs the buffer contents may invalidate all
evbuffer_ptrs for that buffer. Do not modify these values except with
evbuffer_ptr_set.
+
+ Used when repeatedly searching through a buffer. Calls to any function
+ that modifies or re-packs the buffer contents may invalidate all
+ evbuffer_ptrs for that buffer. Do not modify these values except with
+ evbuffer_ptr_set.
+
+ An evbuffer_ptr can represent any position from the start of a buffer up
+ to a position immediately after the end of a buffer.
+
+ @see evbuffer_ptr_set()
*/
struct evbuffer_ptr {
ev_ssize_t pos;
- /* Do not alter the values of fields. */
+ /* Do not alter or rely on the values of fields: they are for internal
+ * use */
struct {
void *chain;
size_t pos_in_chain;
@@ -342,6 +353,20 @@ int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);
ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen);
/**
+ Read data from the middle of an evbuffer, and leave the buffer unchanged.
+
+ If more bytes are requested than are available in the evbuffer, we
+ only extract as many bytes as were available.
+
+ @param buf the evbuffer to be read from
+ @param pos the position to start reading from
+ @param data_out the destination buffer to store the result
+ @param datlen the maximum size of the destination buffer
+ @return the number of bytes read, or -1 if we can't drain the buffer.
+ */
+ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen);
+
+/**
Read data from an evbuffer into another evbuffer, draining
the bytes from the source buffer. This function avoids copy
operations to the extent possible.
@@ -376,7 +401,9 @@ enum evbuffer_eol_style {
/** An EOL is a CR followed by an LF. */
EVBUFFER_EOL_CRLF_STRICT,
/** An EOL is a LF. */
- EVBUFFER_EOL_LF
+ EVBUFFER_EOL_LF,
+ /** An EOL is a NUL character (that is, a single byte with value 0) */
+ EVBUFFER_EOL_NUL
};
/**
@@ -411,6 +438,22 @@ char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf);
/**
+ Copy data from one evbuffer into another evbuffer.
+
+ This is a non-destructive add. The data from one buffer is copied
+ into the other buffer. However, no unnecessary memory copies occur.
+
+ Note that buffers already containing buffer references can't be added
+ to other buffers.
+
+ @param outbuf the output buffer
+ @param inbuf the input buffer
+ @return 0 if successful, or -1 if an error occurred
+ */
+int evbuffer_add_buffer_reference(struct evbuffer *outbuf,
+ struct evbuffer *inbuf);
+
+/**
A cleanup function for a piece of memory added to an evbuffer by
reference.
@@ -452,10 +495,13 @@ int evbuffer_add_reference(struct evbuffer *outbuf,
The results of using evbuffer_remove() or evbuffer_pullup() on
evbuffers whose data was added using this function are undefined.
+ For more fine-grained control, use evbuffer_add_file_segment.
+
@param outbuf the output buffer
@param fd the file descriptor
@param offset the offset from which to read data
- @param length how much data to read
+ @param length how much data to read, or -1 to read as much as possible.
+ (-1 requires that 'fd' support fstat.)
@return 0 if successful, or -1 if an error occurred
*/
@@ -463,6 +509,104 @@ int evbuffer_add_file(struct evbuffer *outbuf, int fd, ev_off_t offset,
ev_off_t length);
/**
+ An evbuffer_file_segment holds a reference to a range of a file --
+ possibly the whole file! -- for use in writing from an evbuffer to a
+ socket. It could be implemented with mmap, sendfile, splice, or (if all
+ else fails) by just pulling all the data into RAM. A single
+ evbuffer_file_segment can be added more than once, and to more than one
+ evbuffer.
+ */
+struct evbuffer_file_segment;
+
+/**
+ Flag for creating evbuffer_file_segment: If this flag is set, then when
+ the evbuffer_file_segment is freed and no longer in use by any
+ evbuffer, the underlying fd is closed.
+ */
+#define EVBUF_FS_CLOSE_ON_FREE 0x01
+/**
+ Flag for creating evbuffer_file_segment: Disable memory-map based
+ implementations.
+ */
+#define EVBUF_FS_DISABLE_MMAP 0x02
+/**
+ Flag for creating evbuffer_file_segment: Disable direct fd-to-fd
+ implementations (including sendfile and splice).
+
+ You might want to use this option if data needs to be taken from the
+ evbuffer by any means other than writing it to the network: the sendfile
+ backend is fast, but it only works for sending files directly to the
+ network.
+ */
+#define EVBUF_FS_DISABLE_SENDFILE 0x04
+/**
+ Flag for creating evbuffer_file_segment: Do not allocate a lock for this
+ segment. If this option is set, then neither the segment nor any
+ evbuffer it is added to may ever be accessed from more than one thread
+ at a time.
+ */
+#define EVBUF_FS_DISABLE_LOCKING 0x08
+
+/**
+ Create and return a new evbuffer_file_segment for reading data from a
+ file and sending it out via an evbuffer.
+
+ This function avoids unnecessary data copies between userland and
+ kernel. Where available, it uses sendfile or splice.
+
+ The file descriptor must not be closed so long as any evbuffer is using
+ this segment.
+
+ The results of using evbuffer_remove() or evbuffer_pullup() or any other
+ function that reads bytes from an evbuffer on any evbuffer containing
+ the newly returned segment are undefined, unless you pass the
+ EVBUF_FS_DISABLE_SENDFILE flag to this function.
+
+ @param fd an open file to read from.
+ @param offset an index within the file at which to start reading
+ @param length how much data to read, or -1 to read as much as possible.
+ (-1 requires that 'fd' support fstat.)
+ @param flags any number of the EVBUF_FS_* flags
+ @return a new evbuffer_file_segment, or NULL on failure.
+ **/
+struct evbuffer_file_segment *evbuffer_file_segment_new(
+ int fd, ev_off_t offset, ev_off_t length, unsigned flags);
+
+/**
+ Free an evbuffer_file_segment
+
+ It is safe to call this function even if the segment has been added to
+ one or more evbuffers. The evbuffer_file_segment will not be freed
+ until no more references to it exist.
+ */
+void evbuffer_file_segment_free(struct evbuffer_file_segment *seg);
+
+/**
+ Insert some or all of an evbuffer_file_segment at the end of an evbuffer
+
+ Note that the offset and length parameters of this function have a
+ different meaning from those provided to evbuffer_file_segment_new: When
+ you create the segment, the offset is the offset _within the file_, and
+ the length is the length _of the segment_, whereas when you add a
+ segment to an evbuffer, the offset is _within the segment_ and the
+ length is the length of the _part of the segment you want to use.
+
+ In other words, if you have a 10 KiB file, and you create an
+ evbuffer_file_segment for it with offset 20 and length 1000, it will
+ refer to bytes 20..1019 inclusive. If you then pass this segment to
+ evbuffer_add_file_segment and specify an offset of 20 and a length of
+ 50, you will be adding bytes 40..99 inclusive.
+
+ @param buf the evbuffer to append to
+ @param seg the segment to add
+ @param offset the offset within the segment to start from
+ @param length the amount of data to add, or -1 to add it all.
+ @return 0 on success, -1 on failure.
+ */
+int evbuffer_add_file_segment(struct evbuffer *buf,
+ struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length);
+
+/**
Append a formatted string to the end of an evbuffer.
The string is formated as printf.
@@ -583,9 +727,18 @@ enum evbuffer_ptr_how {
/**
Sets the search pointer in the buffer to position.
- If evbuffer_ptr is not initialized. This function can only be called
+ There are two ways to use this function: you can call
+ evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET)
+ to move 'pos' to a position 'N' bytes after the start of the buffer, or
+ evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET)
+ to move 'pos' forward by 'N' bytes.
+
+ If evbuffer_ptr is not initialized, this function can only be called
with EVBUFFER_PTR_SET.
+ An evbuffer_ptr can represent any position from the start of the buffer to
+ a position immediately after the end of the buffer.
+
@param buffer the evbuffer to be search
@param ptr a pointer to a struct evbuffer_ptr
@param position the position at which to start the next search
@@ -831,6 +984,21 @@ struct event_base;
*/
int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base);
+/**
+ Append data from 1 or more iovec's to an evbuffer
+
+ Calculates the number of bytes needed for an iovec structure and guarantees
+ all data will fit into a single chain. Can be used in lieu of functionality
+ which calls evbuffer_add() constantly before being used to increase
+ performance.
+
+ @param buffer the destination buffer
+ @param vec the source iovec
+ @param n_vec the number of iovec structures.
+ @return the number of bytes successfully written to the output buffer.
+*/
+size_t evbuffer_add_iovec(struct evbuffer * buffer, struct evbuffer_iovec * vec, int n_vec);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/event2/bufferevent.h b/include/event2/bufferevent.h
index f3d8b1a0..82953db7 100644
--- a/include/event2/bufferevent.h
+++ b/include/event2/bufferevent.h
@@ -725,6 +725,30 @@ int bufferevent_add_to_rate_limit_group(struct bufferevent *bev,
int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev);
/**
+ Set the size limit for single read operation.
+
+ Set to 0 for a reasonable default.
+
+ Return 0 on success and -1 on failure.
+ */
+int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size);
+
+/**
+ Set the size limit for single write operation.
+
+ Set to 0 for a reasonable default.
+
+ Return 0 on success and -1 on failure.
+ */
+int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);
+
+/** Get the current size limit for single read operation. */
+ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev);
+
+/** Get the current size limit for single write operation. */
+ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev);
+
+/**
@name Rate limit inspection
Return the current read or write bucket size for a bufferevent.
diff --git a/include/event2/bufferevent_ssl.h b/include/event2/bufferevent_ssl.h
index c612e99e..07608faf 100644
--- a/include/event2/bufferevent_ssl.h
+++ b/include/event2/bufferevent_ssl.h
@@ -88,6 +88,26 @@ bufferevent_openssl_socket_new(struct event_base *base,
enum bufferevent_ssl_state state,
int options);
+/** Control how to report dirty SSL shutdowns.
+
+ If the peer (or the network, or an attacker) closes the TCP
+ connection before closing the SSL channel, and the protocol is SSL >= v3,
+ this is a "dirty" shutdown. If allow_dirty_shutdown is 0 (default),
+ this is reported as BEV_EVENT_ERROR.
+
+ If instead allow_dirty_shutdown=1, a dirty shutdown is reported as
+ BEV_EVENT_EOF.
+
+ (Note that if the protocol is < SSLv3, you will always receive
+ BEV_EVENT_EOF, since SSL 2 and earlier cannot distinguish a secure
+ connection close from a dirty one. This is one reason (among many)
+ not to use SSL 2.)
+*/
+
+int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev);
+void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev,
+ int allow_dirty_shutdown);
+
/** Return the underlying openssl SSL * object for an SSL bufferevent. */
struct ssl_st *
bufferevent_openssl_get_ssl(struct bufferevent *bufev);
diff --git a/include/event2/dns.h b/include/event2/dns.h
index ca0da3c9..ad7227f5 100644
--- a/include/event2/dns.h
+++ b/include/event2/dns.h
@@ -450,7 +450,7 @@ int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname);
@return 0 if successful, or -1 if an error occurred
@see evdns_resolv_conf_parse()
*/
-#ifdef WIN32
+#ifdef _WIN32
int evdns_base_config_windows_nameservers(struct evdns_base *);
#define EVDNS_BASE_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED
#endif
diff --git a/include/event2/dns_compat.h b/include/event2/dns_compat.h
index 7c1c7761..88bf97b5 100644
--- a/include/event2/dns_compat.h
+++ b/include/event2/dns_compat.h
@@ -324,7 +324,7 @@ void evdns_search_ndots_set(const int ndots);
*/
struct evdns_server_port *evdns_add_server_port(evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data);
-#ifdef WIN32
+#ifdef _WIN32
int evdns_config_windows_nameservers(void);
#define EVDNS_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED
#endif
diff --git a/include/event2/event.h b/include/event2/event.h
index 39937072..a09e3a76 100644
--- a/include/event2/event.h
+++ b/include/event2/event.h
@@ -537,6 +537,38 @@ int event_config_set_flag(struct event_config *cfg, int flag);
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus);
/**
+ * Record an interval and/or a number of callbacks after which the event base
+ * should check for new events. By default, the event base will run as many
+ * events are as activated at the higest activated priority before checking
+ * for new events. If you configure it by setting max_interval, it will check
+ * the time after each callback, and not allow more than max_interval to
+ * elapse before checking for new events. If you configure it by setting
+ * max_callbacks to a value >= 0, it will run no more than max_callbacks
+ * callbacks before checking for new events.
+ *
+ * This option can decrease the latency of high-priority events, and
+ * avoid priority inversions where multiple low-priority events keep us from
+ * polling for high-priority events, but at the expense of slightly decreasing
+ * the throughput. Use it with caution!
+ *
+ * @param cfg The event_base configuration object.
+ * @param max_interval An interval after which Libevent should stop running
+ * callbacks and check for more events, or NULL if there should be
+ * no such interval.
+ * @param max_callbacks A number of callbacks after which Libevent should
+ * stop running callbacks and check for more events, or -1 if there
+ * should be no such limit.
+ * @param min_priority A priority below which max_interval and max_callbacks
+ * should not be enforced. If this is set to 0, they are enforced
+ * for events of every priority; if it's set to 1, they're enforced
+ * for events of priority 1 and above, and so on.
+ * @return 0 on success, -1 on failure.
+ **/
+int event_config_set_max_dispatch_interval(struct event_config *cfg,
+ const struct timeval *max_interval, int max_callbacks,
+ int min_priority);
+
+/**
Initialize the event API.
Use event_base_new_with_config() to initialize a new event base, taking
@@ -608,6 +640,25 @@ typedef void (*event_fatal_cb)(int err);
*/
void event_set_fatal_callback(event_fatal_cb cb);
+#define EVENT_DBG_ALL 0xffffffffu
+#define EVENT_DBG_NONE 0
+
+/**
+ Turn on debugging logs and have them sent to the default log handler.
+
+ This is a global setting; if you are going to call it, you must call this
+ before any calls that create an event-base. You must call it before any
+ multithreaded use of Libevent.
+
+ Debug logs are verbose.
+
+ @param which Controls which debug messages are turned on. This option is
+ unused for now; for forward compatibility, you must pass in the constant
+ "EVENT_DBG_ALL" to turn debugging logs on, or "EVENT_DBG_NONE" to turn
+ debugging logs off.
+ */
+void event_enable_debug_logging(ev_uint32_t which);
+
/**
Associate a different event base with an event.
@@ -630,6 +681,11 @@ int event_base_set(struct event_base *, struct event *);
/** Do not block: see which events are ready now, run the callbacks
* of the highest-priority ones, then exit. */
#define EVLOOP_NONBLOCK 0x02
+/** Do not exit the loop because we have no pending events. Instead, keep
+ * running until event_base_loopexit() or event_base_loopbreak() makes us
+ * stop.
+ */
+#define EVLOOP_NO_EXIT_ON_EMPTY 0x04
/**@}*/
/**
@@ -1111,6 +1167,15 @@ ev_uint32_t event_get_version_number(void);
int event_base_priority_init(struct event_base *, int);
/**
+ Get the number of different event priorities.
+
+ @param eb the event_base structure returned by event_base_new()
+ @return Number of different event priorities
+ @see event_base_priority_init()
+*/
+int event_base_get_npriorities(struct event_base *eb);
+
+/**
Assign a priority to an event.
@param ev an event struct
@@ -1190,6 +1255,20 @@ void event_base_dump_events(struct event_base *, FILE *);
int event_base_gettimeofday_cached(struct event_base *base,
struct timeval *tv);
+/** Update cached_tv in the 'base' to the current time
+ *
+ * You can use this function is useful for selectively increasing
+ * the accuracy of the cached time value in 'base' during callbacks
+ * that take a long time to execute.
+ *
+ * This function has no effect if the base is currently not in its
+ * event loop, or if timeval caching is disabled via
+ * EVENT_BASE_FLAG_NO_CACHE_TIME.
+ *
+ * @return 0 on success, -1 on failure
+ */
+int event_base_update_cache_time(struct event_base *base);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/event2/event_struct.h b/include/event2/event_struct.h
index 2ed4272c..aa93ae7d 100644
--- a/include/event2/event_struct.h
+++ b/include/event2/event_struct.h
@@ -83,6 +83,16 @@ struct name { \
}
#endif
+/* Fix so that people don't have to run with <sys/queue.h> */
+#ifndef LIST_ENTRY
+#define _EVENT_DEFINED_LISTENTRY
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+#endif /* !TAILQ_ENTRY */
+
struct event_base;
struct event {
TAILQ_ENTRY(event) ev_active_next;
@@ -99,13 +109,13 @@ struct event {
union {
/* used for io events */
struct {
- TAILQ_ENTRY(event) ev_io_next;
+ LIST_ENTRY (event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
/* used by signal events */
struct {
- TAILQ_ENTRY(event) ev_signal_next;
+ LIST_ENTRY (event) ev_signal_next;
short ev_ncalls;
/* Allows deletes in callback */
short *ev_pncalls;
@@ -134,6 +144,14 @@ TAILQ_HEAD (event_list, event);
#undef TAILQ_HEAD
#endif
+#ifdef _EVENT_DEFINED_LISTENTRY
+#undef LIST_ENTRY
+struct event_dlist;
+#undef _EVENT_DEFINED_LISTENTRY
+#else
+LIST_HEAD (event_dlist, event);
+#endif /* _EVENT_DEFINED_LISTENTRY */
+
#ifdef __cplusplus
}
#endif
diff --git a/include/event2/http.h b/include/event2/http.h
index c6ee1dbe..144661b3 100644
--- a/include/event2/http.h
+++ b/include/event2/http.h
@@ -37,6 +37,7 @@ extern "C" {
/* In case we haven't included the right headers yet. */
struct evbuffer;
struct event_base;
+struct bufferevent;
/** @file event2/http.h
*
@@ -69,6 +70,7 @@ struct evhttp_request;
struct evkeyvalq;
struct evhttp_bound_socket;
struct evconnlistener;
+struct evdns_base;
/**
* Create a new HTTP server.
@@ -148,6 +150,19 @@ struct evhttp_bound_socket *evhttp_bind_listener(struct evhttp *http, struct evc
*/
struct evconnlistener *evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound);
+typedef void evhttp_bound_socket_foreach_fn(struct evhttp_bound_socket *, void *);
+/**
+ * Applies the function specified in the first argument to all
+ * evhttp_bound_sockets associated with "http". The user must not
+ * attempt to free or remove any connections, sockets or listeners
+ * in the callback "function".
+ *
+ * @param http pointer to an evhttp object
+ * @param function function to apply to every bound socket
+ * @param argument pointer value passed to function for every socket iterated
+ */
+void evhttp_foreach_bound_socket(struct evhttp *http, evhttp_bound_socket_foreach_fn *function, void *argument);
+
/**
* Makes an HTTP server stop accepting connections on the specified socket
*
@@ -234,6 +249,23 @@ void evhttp_set_gencb(struct evhttp *http,
void (*cb)(struct evhttp_request *, void *), void *arg);
/**
+ Set a callback used to create new bufferevents for connections
+ to a given evhttp object.
+
+ You can use this to override the default bufferevent type -- for example,
+ to make this evhttp object use SSL bufferevents rather than unencrypted
+ ones.
+
+ New bufferevents must be allocated with no fd set on them.
+
+ @param http the evhttp server object for which to set the callback
+ @param cb the callback to invoke for incoming connections
+ @param arg an context argument for the callback
+ */
+void evhttp_set_bevcb(struct evhttp *http,
+ struct bufferevent *(*cb)(struct event_base *, void *), void *arg);
+
+/**
Adds a virtual host to the http server.
A virtual host is a newly initialized evhttp object that has request
@@ -295,6 +327,14 @@ int evhttp_remove_server_alias(struct evhttp *http, const char *alias);
*/
void evhttp_set_timeout(struct evhttp *http, int timeout_in_secs);
+/**
+ * Set the timeout for an HTTP request.
+ *
+ * @param http an evhttp object
+ * @param tv the timeout, or NULL
+ */
+void evhttp_set_timeout_tv(struct evhttp *http, const struct timeval* tv);
+
/* Request/Response functionality */
/**
@@ -390,6 +430,29 @@ enum evhttp_cmd_type {
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
/**
+ * Create and return a connection object that can be used to for making HTTP
+ * requests. The connection object tries to resolve address and establish the
+ * connection when it is given an http request object.
+ *
+ * @param base the event_base to use for handling the connection
+ * @param dnsbase the dns_base to use for resolving host names; if not
+ * specified host name resolution will block.
+ * @param bev a bufferevent to use for connecting to the server; if NULL, a
+ * socket-based bufferevent will be created. This buffrevent will be freed
+ * when the connection closes. It must have no fd set on it.
+ * @param address the address to which to connect
+ * @param port the port to connect to
+ * @return an evhttp_connection object that can be used for making requests
+ */
+struct evhttp_connection *evhttp_connection_base_bufferevent_new(
+ struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev, const char *address, unsigned short port);
+
+/**
+ * Return the bufferevent that an evhttp_connection is using.
+ */
+struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon);
+
+/**
* Creates a new request object that needs to be filled in with the request
* parameters. The callback is executed when the request completed or an
* error occurred.
@@ -410,12 +473,10 @@ void evhttp_request_set_chunked_cb(struct evhttp_request *,
/** Frees the request object and removes associated events. */
void evhttp_request_free(struct evhttp_request *req);
-struct evdns_base;
-
/**
- * A connection object that can be used to for making HTTP requests. The
- * connection object tries to resolve address and establish the connection
- * when it is given an http request object.
+ * Create and return a connection object that can be used to for making HTTP
+ * requests. The connection object tries to resolve address and establish the
+ * connection when it is given an http request object.
*
* @param base the event_base to use for handling the connection
* @param dnsbase the dns_base to use for resolving host names; if not
@@ -428,12 +489,6 @@ struct evhttp_connection *evhttp_connection_base_new(
struct event_base *base, struct evdns_base *dnsbase,
const char *address, unsigned short port);
-/**
- * Return the bufferevent that an evhttp_connection is using.
- */
-struct bufferevent *evhttp_connection_get_bufferevent(
- struct evhttp_connection *evcon);
-
/** Takes ownership of the request object
*
* Can be used in a request callback to keep onto the request until
@@ -474,10 +529,22 @@ void evhttp_connection_set_local_address(struct evhttp_connection *evcon,
void evhttp_connection_set_local_port(struct evhttp_connection *evcon,
ev_uint16_t port);
-/** Sets the timeout for events related to this connection */
+/** Sets the timeout in seconds for events related to this connection */
void evhttp_connection_set_timeout(struct evhttp_connection *evcon,
int timeout_in_secs);
+/** Sets the timeout for events related to this connection. Takes a struct
+ * timeval. */
+void evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv);
+
+/** Sets the delay before retrying requests on this connection. This is only
+ * used if evhttp_connection_set_retries is used to make the number of retries
+ * at least one. Each retry after the first is twice as long as the one before
+ * it. */
+void evhttp_connection_set_initial_retry_tv(struct evhttp_connection *evcon,
+ const struct timeval *tv);
+
/** Sets the retry limit for this connection - -1 repeats indefinitely */
void evhttp_connection_set_retries(struct evhttp_connection *evcon,
int retry_max);
diff --git a/include/event2/listener.h b/include/event2/listener.h
index 2dc64be4..14d1aec1 100644
--- a/include/event2/listener.h
+++ b/include/event2/listener.h
@@ -69,6 +69,9 @@ typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
/** Flag: Indicates that the listener should be locked so it's safe to use
* from multiple threadcs at once. */
#define LEV_OPT_THREADSAFE (1u<<4)
+/** Flag: Indicates that the listener should be created in disabled
+ * state. Use evconnlistener_enable() to enable it later. */
+#define LEV_OPT_DISABLED (1u<<5)
/**
Allocate a new evconnlistener object to listen for incoming TCP connections
diff --git a/include/event2/rpc.h b/include/event2/rpc.h
index 5272edc2..2ac7ad2b 100644
--- a/include/event2/rpc.h
+++ b/include/event2/rpc.h
@@ -454,7 +454,7 @@ enum EVRPC_HOOK_TYPE {
EVRPC_OUTPUT /**< apply the function to an output hook */
};
-#ifndef WIN32
+#ifndef _WIN32
/** Deprecated alias for EVRPC_INPUT. Not available on windows, where it
* conflicts with platform headers. */
#define INPUT EVRPC_INPUT
diff --git a/include/event2/thread.h b/include/event2/thread.h
index 0b638f0f..01d7f00b 100644
--- a/include/event2/thread.h
+++ b/include/event2/thread.h
@@ -187,7 +187,7 @@ int evthread_set_condition_callbacks(
void evthread_set_id_callback(
unsigned long (*id_fn)(void));
-#if (defined(WIN32) && !defined(_EVENT_DISABLE_THREAD_SUPPORT)) || defined(_EVENT_IN_DOXYGEN)
+#if (defined(_WIN32) && !defined(_EVENT_DISABLE_THREAD_SUPPORT)) || defined(_EVENT_IN_DOXYGEN)
/** Sets up Libevent for use with Windows builtin locking and thread ID
functions. Unavailable if Libevent is not built for Windows.
diff --git a/include/event2/util.h b/include/event2/util.h
index 45d8f1de..e5e0bc47 100644
--- a/include/event2/util.h
+++ b/include/event2/util.h
@@ -63,7 +63,7 @@ extern "C" {
#include <netdb.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
@@ -106,7 +106,7 @@ extern "C" {
#ifdef _EVENT_HAVE_UINT64_T
#define ev_uint64_t uint64_t
#define ev_int64_t int64_t
-#elif defined(WIN32)
+#elif defined(_WIN32)
#define ev_uint64_t unsigned __int64
#define ev_int64_t signed __int64
#elif _EVENT_SIZEOF_LONG_LONG == 8
@@ -125,7 +125,7 @@ extern "C" {
#ifdef _EVENT_HAVE_UINT32_T
#define ev_uint32_t uint32_t
#define ev_int32_t int32_t
-#elif defined(WIN32)
+#elif defined(_WIN32)
#define ev_uint32_t unsigned int
#define ev_int32_t signed int
#elif _EVENT_SIZEOF_LONG == 4
@@ -144,7 +144,7 @@ extern "C" {
#ifdef _EVENT_HAVE_UINT16_T
#define ev_uint16_t uint16_t
#define ev_int16_t int16_t
-#elif defined(WIN32)
+#elif defined(_WIN32)
#define ev_uint16_t unsigned short
#define ev_int16_t signed short
#elif _EVENT_SIZEOF_INT == 2
@@ -193,8 +193,21 @@ extern "C" {
#define ev_ssize_t ssize_t
#endif
-#ifdef WIN32
+/* Note that we define ev_off_t based on the compile-time size of off_t that
+ * we used to build Libevent, and not based on the current size of off_t.
+ * (For example, we don't define ev_off_t to off_t.). We do this because
+ * some systems let you build your software with different off_t sizes
+ * at runtime, and so putting in any dependency on off_t would risk API
+ * mismatch.
+ */
+#ifdef _WIN32
+#define ev_off_t ev_int64_t
+#elif _EVENT_SIZEOF_OFF_T == 8
#define ev_off_t ev_int64_t
+#elif _EVENT_SIZEOF_OFF_T == 4
+#define ev_off_t ev_int32_t
+#elif defined(_EVENT_IN_DOXYGEN)
+#define ev_off_t ...
#else
#define ev_off_t off_t
#endif
@@ -250,7 +263,7 @@ extern "C" {
#define EV_SSIZE_MIN ((-EV_SSIZE_MAX) - 1)
/**@}*/
-#ifdef WIN32
+#ifdef _WIN32
#define ev_socklen_t int
#elif defined(_EVENT_socklen_t)
#define ev_socklen_t _EVENT_socklen_t
@@ -268,7 +281,7 @@ extern "C" {
/**
* A type wide enough to hold the output of "socket()" or "accept()". On
* Windows, this is an intptr_t; elsewhere, it is an int. */
-#ifdef WIN32
+#ifdef _WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
@@ -324,7 +337,7 @@ int evutil_closesocket(evutil_socket_t sock);
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
-#ifdef WIN32
+#ifdef _WIN32
/** Return the most recent socket error. Not idempotent on all platforms. */
#define EVUTIL_SOCKET_ERROR() WSAGetLastError()
/** Replace the most recent socket error with errcode */
diff --git a/iocp-internal.h b/iocp-internal.h
index ae79cbc7..22321e45 100644
--- a/iocp-internal.h
+++ b/iocp-internal.h
@@ -39,7 +39,7 @@ typedef void (*iocp_callback)(struct event_overlapped *, ev_uintptr_t, ev_ssize_
/* This whole file is actually win32 only. We wrap the structures in a win32
* ifdef so that we can test-compile code that uses these interfaces on
* non-win32 platforms. */
-#ifdef WIN32
+#ifdef _WIN32
/**
Internal use only. Wraps an OVERLAPPED that we're using for libevent
diff --git a/ipv6-internal.h b/ipv6-internal.h
index e3737118..e0d4109b 100644
--- a/ipv6-internal.h
+++ b/ipv6-internal.h
@@ -30,11 +30,13 @@
#ifndef _EVENT_IPV6_INTERNAL_H
#define _EVENT_IPV6_INTERNAL_H
+#include "event2/event-config.h"
+#include "evconfig-private.h"
+
#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
-#include "event2/event-config.h"
#include "event2/util.h"
#ifdef __cplusplus
diff --git a/kqueue.c b/kqueue.c
index 8276f047..fc5d82fa 100644
--- a/kqueue.c
+++ b/kqueue.c
@@ -27,8 +27,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
-
-#define _GNU_SOURCE
+#include "evconfig-private.h"
#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
@@ -165,12 +164,6 @@ err:
return (NULL);
}
-static void
-kq_sighandler(int sig)
-{
- /* Do nothing here */
-}
-
#define ADD_UDATA 0x30303
static void
@@ -438,9 +431,13 @@ kq_sig_add(struct event_base *base, int nsignal, short old, short events, void *
if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
return (-1);
- /* XXXX The manpage suggest we could use SIG_IGN instead of a
- * do-nothing handler */
- if (_evsig_set_handler(base, nsignal, kq_sighandler) == -1)
+ /* We can set the handler for most signals to SIG_IGN and
+ * still have them reported to us in the queue. However,
+ * if the handler for SIGCHLD is SIG_IGN, the system reaps
+ * zombie processes for us, and we don't get any notification.
+ * This appears to be the only signal with this quirk. */
+ if (_evsig_set_handler(base, nsignal,
+ nsignal == SIGCHLD ? SIG_DFL : SIG_IGN) == -1)
return (-1);
return (0);
diff --git a/listener.c b/listener.c
index 9941e1d8..34be3497 100644
--- a/listener.c
+++ b/listener.c
@@ -24,11 +24,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/types.h>
-
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
-#ifdef WIN32
+#ifdef _WIN32
#ifndef _WIN32_WINNT
/* Minimum required for InitializeCriticalSectionAndSpinCount */
#define _WIN32_WINNT 0x0403
@@ -56,7 +57,7 @@
#include "util-internal.h"
#include "log-internal.h"
#include "evthread-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "iocp-internal.h"
#include "defer-internal.h"
#include "event-internal.h"
@@ -87,7 +88,7 @@ struct evconnlistener_event {
struct event listener;
};
-#ifdef WIN32
+#ifdef _WIN32
struct evconnlistener_iocp {
struct evconnlistener base;
evutil_socket_t fd;
@@ -157,7 +158,7 @@ evconnlistener_new(struct event_base *base,
{
struct evconnlistener_event *lev;
-#ifdef WIN32
+#ifdef _WIN32
if (base && event_base_get_iocp(base)) {
const struct win32_extension_fns *ext =
event_get_win32_extension_fns();
@@ -192,7 +193,8 @@ evconnlistener_new(struct event_base *base,
event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,
listener_read_cb, lev);
- evconnlistener_enable(&lev->base);
+ if (!(flags & LEV_OPT_DISABLED))
+ evconnlistener_enable(&lev->base);
return &lev->base;
}
@@ -382,7 +384,7 @@ listener_read_cb(evutil_socket_t fd, short what, void *p)
LOCK(lev);
while (1) {
struct sockaddr_storage ss;
-#ifdef WIN32
+#ifdef _WIN32
int socklen = sizeof(ss);
#else
socklen_t socklen = sizeof(ss);
@@ -436,7 +438,7 @@ listener_read_cb(evutil_socket_t fd, short what, void *p)
}
}
-#ifdef WIN32
+#ifdef _WIN32
struct accepting_socket {
CRITICAL_SECTION lock;
struct event_overlapped overlapped;
diff --git a/log-internal.h b/log-internal.h
index 211b3e1d..4e0fede0 100644
--- a/log-internal.h
+++ b/log-internal.h
@@ -39,6 +39,23 @@
#define _EVENT_ERR_ABORT ((int)0xdeaddead)
+#define USE_GLOBAL_FOR_DEBUG_LOGGING
+
+#if !defined(_EVENT_DISABLE_DEBUG_MODE) || defined(USE_DEBUG)
+#define EVENT_DEBUG_LOGGING_ENABLED
+#endif
+
+#ifdef EVENT_DEBUG_LOGGING_ENABLED
+#ifdef USE_GLOBAL_FOR_DEBUG_LOGGING
+extern ev_uint32_t _event_debug_logging_mask;
+#define _event_debug_get_logging_mask() (_event_debug_logging_mask)
+#else
+ev_uint32_t _event_debug_get_logging_mask(void);
+#endif
+#else
+#define _event_debug_get_logging_mask() (0)
+#endif
+
void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(3,4) EV_NORETURN;
@@ -48,10 +65,14 @@ void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2);
void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2);
-#ifdef USE_DEBUG
-#define event_debug(x) _event_debugx x
+#ifdef EVENT_DEBUG_LOGGING_ENABLED
+#define event_debug(x) do { \
+ if (_event_debug_get_logging_mask()) { \
+ _event_debugx x; \
+ } \
+ } while (0)
#else
-#define event_debug(x) do {;} while (0)
+#define event_debug(x) ((void)0)
#endif
#undef EV_CHECK_FMT
diff --git a/log.c b/log.c
index 046e81f9..79f23b46 100644
--- a/log.c
+++ b/log.c
@@ -38,8 +38,9 @@
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -63,6 +64,25 @@ static void event_exit(int errcode) EV_NORETURN;
static event_fatal_cb fatal_fn = NULL;
+#ifdef EVENT_DEBUG_LOGGING_ENABLED
+#ifdef USE_DEBUG
+#define DEFAULT_MASK EVENT_DBG_ALL
+#else
+#define DEFAULT_MASK 0
+#endif
+
+#ifdef USE_GLOBAL_FOR_DEBUG_LOGGING
+ev_uint32_t _event_debug_logging_mask = DEFAULT_MASK;
+#else
+static ev_uint32_t _event_debug_logging_mask = DEFAULT_MASK;
+ev_uint32_t
+_event_debug_get_logging_mask(void)
+{
+ return _event_debug_logging_mask;
+}
+#endif
+#endif /* EVENT_DEBUG_LOGGING_ENABLED */
+
void
event_set_fatal_callback(event_fatal_cb cb)
{
diff --git a/m4/ac_backport_259_ssizet.m4 b/m4/ac_backport_259_ssizet.m4
new file mode 100644
index 00000000..75fde386
--- /dev/null
+++ b/m4/ac_backport_259_ssizet.m4
@@ -0,0 +1,3 @@
+AN_IDENTIFIER([ssize_t], [AC_TYPE_SSIZE_T])
+AC_DEFUN([AC_TYPE_SSIZE_T], [AC_CHECK_TYPE(ssize_t, int)])
+
diff --git a/m4/libevent_openssl.m4 b/m4/libevent_openssl.m4
new file mode 100644
index 00000000..7b273255
--- /dev/null
+++ b/m4/libevent_openssl.m4
@@ -0,0 +1,47 @@
+dnl ######################################################################
+dnl OpenSSL support
+AC_DEFUN([LIBEVENT_OPENSSL], [
+AC_REQUIRE([NTP_PKG_CONFIG])dnl
+
+case "$enable_openssl" in
+ yes)
+ have_openssl=no
+ case "$PKG_CONFIG" in
+ '')
+ ;;
+ *)
+ OPENSSL_LIBS=`$PKG_CONFIG --libs openssl 2>/dev/null`
+ case "$OPENSSL_LIBS" in
+ '') ;;
+ *) OPENSSL_LIBS="$OPENSSL_LIBS $EV_LIB_GDI $EV_LIB_WS32"
+ have_openssl=yes
+ ;;
+ esac
+ OPENSSL_INCS=`$PKG_CONFIG --cflags openssl 2>/dev/null`
+ ;;
+ esac
+ case "$have_openssl" in
+ yes) ;;
+ *)
+ save_LIBS="$LIBS"
+ LIBS=""
+ OPENSSL_LIBS=""
+ AC_SEARCH_LIBS([SSL_new], [ssl],
+ [have_openssl=yes
+ OPENSSL_LIBS="$LIBS -lcrypto $EV_LIB_GDI $EV_LIB_WS32"],
+ [have_openssl=no],
+ [-lcrypto $EV_LIB_GDI $EV_LIB_WS32])
+ LIBS="$save_LIBS"
+ ;;
+ esac
+ AC_SUBST(OPENSSL_INCS)
+ AC_SUBST(OPENSSL_LIBS)
+ case "$have_openssl" in
+ yes) AC_DEFINE(HAVE_OPENSSL, 1, [Define if the system has openssl]) ;;
+ esac
+ ;;
+esac
+
+# check if we have and should use openssl
+AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"])
+])
diff --git a/m4/ntp_pkg_config.m4 b/m4/ntp_pkg_config.m4
new file mode 100644
index 00000000..1bce8a6e
--- /dev/null
+++ b/m4/ntp_pkg_config.m4
@@ -0,0 +1,27 @@
+dnl NTP_PKG_CONFIG -*- Autoconf -*-
+dnl
+dnl Look for pkg-config, which must be at least
+dnl $ntp_pkgconfig_min_version.
+dnl
+AC_DEFUN([NTP_PKG_CONFIG], [
+
+dnl lower the minimum version if you find an earlier one works
+ntp_pkgconfig_min_version='0.15.0'
+AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+AS_UNSET([ac_cv_path_PKG_CONFIG])
+AS_UNSET([ac_cv_path_ac_pt_PKG_CONFIG])
+
+case "$PKG_CONFIG" in
+ /*)
+ AC_MSG_CHECKING([if pkg-config is at least version $ntp_pkgconfig_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $ntp_pkgconfig_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+ ;;
+esac
+
+]) dnl NTP_PKG_CONFIG
+
diff --git a/make_epoll_table.py b/make_epoll_table.py
new file mode 100755
index 00000000..e77191c8
--- /dev/null
+++ b/make_epoll_table.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+def get(old,wc,rc):
+ if ('xxx' in (rc, wc)):
+ return "0",-1
+
+ if ('add' in (rc, wc)):
+ events = []
+ if rc == 'add' or (rc != 'del' and 'r' in old):
+ events.append("EPOLLIN")
+ if wc == 'add' or (wc != 'del' and 'w' in old):
+ events.append("EPOLLOUT")
+
+ if old == "0":
+ op = "EPOLL_CTL_ADD"
+ else:
+ op = "EPOLL_CTL_MOD"
+ return "|".join(events), op
+
+ if ('del' in (rc, wc)):
+ op = "EPOLL_CTL_DEL"
+ if rc == 'del':
+ if wc == 'del':
+ events = "EPOLLIN|EPOLLOUT"
+ elif 'w' in old:
+ events = "EPOLLOUT"
+ op = "EPOLL_CTL_MOD"
+ else:
+ events = "EPOLLIN"
+ else:
+ assert wc == 'del'
+ if 'r' in old:
+ events = "EPOLLIN"
+ op = "EPOLL_CTL_MOD"
+ else:
+ events = "EPOLLOUT"
+ return events, op
+
+ return 0, 0
+
+
+def fmt(op, ev, old, wc, rc):
+ entry = "{ %s, %s },"%(op, ev)
+ assert len(entry)<=36
+ sp = " "*(36-len(entry))
+ print "\t%s%s/* old=%2s, write:%3s, read:%3s */" % (
+ entry, sp, old, wc, rc)
+
+
+for old in ('0','r','w','rw'):
+ for wc in ('0', 'add', 'del', 'xxx'):
+ for rc in ('0', 'add', 'del', 'xxx'):
+
+ op,ev = get(old,wc,rc)
+
+ fmt(op, ev, old, wc, rc)
+
diff --git a/minheap-internal.h b/minheap-internal.h
index 6d8e80a2..2f2a8dc6 100644
--- a/minheap-internal.h
+++ b/minheap-internal.h
@@ -29,6 +29,7 @@
#define _MIN_HEAP_H_
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include "event2/event.h"
#include "event2/event_struct.h"
#include "event2/util.h"
@@ -45,21 +46,20 @@ static inline void min_heap_ctor(min_heap_t* s);
static inline void min_heap_dtor(min_heap_t* s);
static inline void min_heap_elem_init(struct event* e);
static inline int min_heap_elt_is_top(const struct event *e);
-static inline int min_heap_elem_greater(struct event *a, struct event *b);
static inline int min_heap_empty(min_heap_t* s);
static inline unsigned min_heap_size(min_heap_t* s);
static inline struct event* min_heap_top(min_heap_t* s);
static inline int min_heap_reserve(min_heap_t* s, unsigned n);
static inline int min_heap_push(min_heap_t* s, struct event* e);
static inline struct event* min_heap_pop(min_heap_t* s);
+static inline int min_heap_adjust(min_heap_t *s, struct event* e);
static inline int min_heap_erase(min_heap_t* s, struct event* e);
static inline void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e);
+static inline void min_heap_shift_up_unconditional_(min_heap_t* s, unsigned hole_index, struct event* e);
static inline void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e);
-int min_heap_elem_greater(struct event *a, struct event *b)
-{
- return evutil_timercmp(&a->ev_timeout, &b->ev_timeout, >);
-}
+#define min_heap_elem_greater(a, b) \
+ (evutil_timercmp(&(a)->ev_timeout, &(b)->ev_timeout, >))
void min_heap_ctor(min_heap_t* s) { s->p = 0; s->n = 0; s->a = 0; }
void min_heap_dtor(min_heap_t* s) { if (s->p) mm_free(s->p); }
@@ -105,7 +105,7 @@ int min_heap_erase(min_heap_t* s, struct event* e)
to be less than the parent, it can't need to shift both up and
down. */
if (e->ev_timeout_pos.min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last))
- min_heap_shift_up_(s, e->ev_timeout_pos.min_heap_idx, last);
+ min_heap_shift_up_unconditional_(s, e->ev_timeout_pos.min_heap_idx, last);
else
min_heap_shift_down_(s, e->ev_timeout_pos.min_heap_idx, last);
e->ev_timeout_pos.min_heap_idx = -1;
@@ -114,6 +114,23 @@ int min_heap_erase(min_heap_t* s, struct event* e)
return -1;
}
+int min_heap_adjust(min_heap_t *s, struct event *e)
+{
+ if (-1 == e->ev_timeout_pos.min_heap_idx) {
+ return min_heap_push(s, e);
+ } else {
+ unsigned parent = (e->ev_timeout_pos.min_heap_idx - 1) / 2;
+ /* The position of e has changed; we shift it up or down
+ * as needed. We can't need to do both. */
+ if (e->ev_timeout_pos.min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], e))
+ min_heap_shift_up_unconditional_(s, e->ev_timeout_pos.min_heap_idx, e);
+ else
+ min_heap_shift_down_(s, e->ev_timeout_pos.min_heap_idx, e);
+ return 0;
+ }
+ return -1;
+}
+
int min_heap_reserve(min_heap_t* s, unsigned n)
{
if (s->a < n)
@@ -130,6 +147,18 @@ int min_heap_reserve(min_heap_t* s, unsigned n)
return 0;
}
+void min_heap_shift_up_unconditional_(min_heap_t* s, unsigned hole_index, struct event* e)
+{
+ unsigned parent = (hole_index - 1) / 2;
+ do
+ {
+ (s->p[hole_index] = s->p[parent])->ev_timeout_pos.min_heap_idx = hole_index;
+ hole_index = parent;
+ parent = (hole_index - 1) / 2;
+ } while (hole_index && min_heap_elem_greater(s->p[parent], e));
+ (s->p[hole_index] = e)->ev_timeout_pos.min_heap_idx = hole_index;
+}
+
void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e)
{
unsigned parent = (hole_index - 1) / 2;
diff --git a/mm-internal.h b/mm-internal.h
index b375155d..96d9e9ba 100644
--- a/mm-internal.h
+++ b/mm-internal.h
@@ -36,9 +36,35 @@ extern "C" {
/* Internal use only: Memory allocation functions. We give them nice short
* mm_names for our own use, but make sure that the symbols have longer names
* so they don't conflict with other libraries (like, say, libmm). */
+
+/** Allocate uninitialized memory.
+ *
+ * @return On success, return a pointer to sz newly allocated bytes.
+ * On failure, set errno to ENOMEM and return NULL.
+ * If the argument sz is 0, simply return NULL.
+ */
void *event_mm_malloc_(size_t sz);
+
+/** Allocate memory initialized to zero.
+ *
+ * @return On success, return a pointer to (count * size) newly allocated
+ * bytes, initialized to zero.
+ * On failure, or if the product would result in an integer overflow,
+ * set errno to ENOMEM and return NULL.
+ * If either arguments are 0, simply return NULL.
+ */
void *event_mm_calloc_(size_t count, size_t size);
-char *event_mm_strdup_(const char *s);
+
+/** Duplicate a string.
+ *
+ * @return On success, return a pointer to a newly allocated duplicate
+ * of a string.
+ * Set errno to ENOMEM and return NULL if a memory allocation error
+ * occurs (or would occur) in the process.
+ * If the argument str is NULL, set errno to EINVAL and return NULL.
+ */
+char *event_mm_strdup_(const char *str);
+
void *event_mm_realloc_(void *p, size_t sz);
void event_mm_free_(void *p);
#define mm_malloc(sz) event_mm_malloc_(sz)
diff --git a/poll.c b/poll.c
index 04d311bb..f48fe59e 100644
--- a/poll.c
+++ b/poll.c
@@ -27,6 +27,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
diff --git a/sample/Makefile.am b/sample/Makefile.am
index 234feb40..be7492b3 100644
--- a/sample/Makefile.am
+++ b/sample/Makefile.am
@@ -19,6 +19,7 @@ hello_world_SOURCES = hello-world.c
http_server_SOURCES = http-server.c
if OPENSSL
+AM_CPPFLAGS += $(OPENSSL_INCS)
noinst_PROGRAMS += le-proxy
le_proxy_SOURCES = le-proxy.c
le_proxy_LDADD = $(LDADD) ../libevent_openssl.la -lssl -lcrypto
diff --git a/sample/dns-example.c b/sample/dns-example.c
index f97a0c6b..6ba1359a 100644
--- a/sample/dns-example.c
+++ b/sample/dns-example.c
@@ -12,7 +12,7 @@
#include <sys/types.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
@@ -196,7 +196,7 @@ main(int c, char **v) {
}
if (idx < c) {
int res;
-#ifdef WIN32
+#ifdef _WIN32
res = evdns_base_config_windows_nameservers(evdns_base);
#else
res = evdns_base_resolv_conf_parse(evdns_base, DNS_OPTION_NAMESERVERS,
diff --git a/sample/event-test.c b/sample/event-test.c
index e4d7a423..0bddcf4e 100644
--- a/sample/event-test.c
+++ b/sample/event-test.c
@@ -12,7 +12,7 @@
#include <sys/types.h>
#include <sys/stat.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
@@ -34,7 +34,7 @@ fifo_read(int fd, short event, void *arg)
char buf[255];
int len;
struct event *ev = arg;
-#ifdef WIN32
+#ifdef _WIN32
DWORD dwBytesRead;
#endif
@@ -43,7 +43,7 @@ fifo_read(int fd, short event, void *arg)
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
fd, event, arg);
-#ifdef WIN32
+#ifdef _WIN32
len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL);
/* Check for end of file. */
@@ -74,7 +74,7 @@ int
main(int argc, char **argv)
{
struct event evfifo;
-#ifdef WIN32
+#ifdef _WIN32
HANDLE socket;
/* Open a file. */
socket = CreateFileA("test.txt", /* open File */
@@ -125,7 +125,7 @@ main(int argc, char **argv)
event_init();
/* Initalize one event */
-#ifdef WIN32
+#ifdef _WIN32
event_set(&evfifo, (int)socket, EV_READ, fifo_read, &evfifo);
#else
event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
@@ -135,7 +135,7 @@ main(int argc, char **argv)
event_add(&evfifo, NULL);
event_dispatch();
-#ifdef WIN32
+#ifdef _WIN32
CloseHandle(socket);
#endif
return (0);
diff --git a/sample/hello-world.c b/sample/hello-world.c
index d40af529..d3cf058a 100644
--- a/sample/hello-world.c
+++ b/sample/hello-world.c
@@ -11,7 +11,7 @@
#include <errno.h>
#include <stdio.h>
#include <signal.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
@@ -43,7 +43,7 @@ main(int argc, char **argv)
struct event *signal_event;
struct sockaddr_in sin;
-#ifdef WIN32
+#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
diff --git a/sample/http-server.c b/sample/http-server.c
index fb455792..2d04fd30 100644
--- a/sample/http-server.c
+++ b/sample/http-server.c
@@ -6,6 +6,9 @@
*/
+/* Compatibility for possible missing IPv6 declarations */
+#include "../util-internal.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -13,7 +16,7 @@
#include <sys/types.h>
#include <sys/stat.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
@@ -44,10 +47,7 @@
# endif
#endif
-/* Compatibility for possible missing IPv6 declarations */
-#include "../util-internal.h"
-
-#ifdef WIN32
+#ifdef _WIN32
#define stat _stat
#define fstat _fstat
#define open _open
@@ -206,7 +206,7 @@ send_document_cb(struct evhttp_request *req, void *arg)
if (S_ISDIR(st.st_mode)) {
/* If it's a directory, read the comments and make a little
* index page */
-#ifdef WIN32
+#ifdef _WIN32
HANDLE d;
WIN32_FIND_DATAA ent;
char *pattern;
@@ -220,7 +220,7 @@ send_document_cb(struct evhttp_request *req, void *arg)
if (!strlen(path) || path[strlen(path)-1] != '/')
trailing_slash = "/";
-#ifdef WIN32
+#ifdef _WIN32
dirlen = strlen(whole_path);
pattern = malloc(dirlen+3);
memcpy(pattern, whole_path, dirlen);
@@ -247,7 +247,7 @@ send_document_cb(struct evhttp_request *req, void *arg)
uri_root, path, /* XXX html-escape this? */
trailing_slash,
decoded_path /* XXX html-escape this */);
-#ifdef WIN32
+#ifdef _WIN32
do {
const char *name = ent.cFileName;
#else
@@ -257,13 +257,13 @@ send_document_cb(struct evhttp_request *req, void *arg)
evbuffer_add_printf(evb,
" <li><a href=\"%s\">%s</a>\n",
name, name);/* XXX escape this */
-#ifdef WIN32
+#ifdef _WIN32
} while (FindNextFileA(d, &ent));
#else
}
#endif
evbuffer_add_printf(evb, "</ul></body></html>\n");
-#ifdef WIN32
+#ifdef _WIN32
CloseHandle(d);
#else
closedir(d);
@@ -321,7 +321,7 @@ main(int argc, char **argv)
struct evhttp_bound_socket *handle;
unsigned short port = 0;
-#ifdef WIN32
+#ifdef _WIN32
WSADATA WSAData;
WSAStartup(0x101, &WSAData);
#else
diff --git a/sample/le-proxy.c b/sample/le-proxy.c
index 446bfb56..4bab6a61 100644
--- a/sample/le-proxy.c
+++ b/sample/le-proxy.c
@@ -11,7 +11,7 @@
#include <string.h>
#include <errno.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
diff --git a/sample/signal-test.c b/sample/signal-test.c
index 53c025f9..3772e2b7 100644
--- a/sample/signal-test.c
+++ b/sample/signal-test.c
@@ -9,7 +9,7 @@
#include <event2/event-config.h>
#include <sys/stat.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
@@ -50,7 +50,7 @@ main(int argc, char **argv)
{
struct event signal_int;
struct event_base* base;
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
diff --git a/sample/time-test.c b/sample/time-test.c
index 61b4a6c1..ddbc029a 100644
--- a/sample/time-test.c
+++ b/sample/time-test.c
@@ -13,7 +13,7 @@
#include <event2/event-config.h>
#include <sys/stat.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/queue.h>
#include <unistd.h>
#endif
@@ -31,7 +31,7 @@
#include <event2/event_struct.h>
#include <event2/util.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
@@ -71,7 +71,7 @@ main(int argc, char **argv)
struct event_base *base;
int flags;
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
diff --git a/select.c b/select.c
index afba6d34..5d284c79 100644
--- a/select.c
+++ b/select.c
@@ -27,6 +27,13 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#ifdef __APPLE__
+/* Apple wants us to define this if we might ever pass more than
+ * FD_SETSIZE bits to select(). */
+#define _DARWIN_UNLIMITED_SELECT
+#endif
#include <sys/types.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
diff --git a/signal.c b/signal.c
index d40da0e3..6c19b4af 100644
--- a/signal.c
+++ b/signal.c
@@ -27,8 +27,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "event2/event-config.h"
+#include "evconfig-private.h"
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <windows.h>
@@ -81,7 +82,7 @@
on Linux do a reasonable thing using signalfd.
*/
-#ifndef WIN32
+#ifndef _WIN32
/* Windows wants us to call our signal handlers as __cdecl. Nobody else
* expects you to do anything crazy like this. */
#define __cdecl
@@ -175,7 +176,7 @@ evsig_init(struct event_base *base)
*/
if (evutil_socketpair(
AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
-#ifdef WIN32
+#ifdef _WIN32
/* Make this nonfatal on win32, where sometimes people
have localhost firewalled. */
event_sock_warn(-1, "%s: socketpair", __func__);
@@ -187,6 +188,9 @@ evsig_init(struct event_base *base)
evutil_make_socket_closeonexec(base->sig.ev_signal_pair[0]);
evutil_make_socket_closeonexec(base->sig.ev_signal_pair[1]);
+ if (base->sig.sh_old) {
+ mm_free(base->sig.sh_old);
+ }
base->sig.sh_old = NULL;
base->sig.sh_old_max = 0;
@@ -329,6 +333,12 @@ _evsig_restore_handler(struct event_base *base, int evsignal)
ev_sighandler_t *sh;
#endif
+ if (evsignal >= sig->sh_old_max) {
+ /* Can't actually restore. */
+ /* XXXX.*/
+ return 0;
+ }
+
/* restore previous handler */
sh = sig->sh_old[evsignal];
sig->sh_old[evsignal] = NULL;
@@ -368,7 +378,7 @@ static void __cdecl
evsig_handler(int sig)
{
int save_errno = errno;
-#ifdef WIN32
+#ifdef _WIN32
int socket_errno = EVUTIL_SOCKET_ERROR();
#endif
ev_uint8_t msg;
@@ -388,7 +398,7 @@ evsig_handler(int sig)
msg = sig;
send(evsig_base_fd, (char*)&msg, 1, 0);
errno = save_errno;
-#ifdef WIN32
+#ifdef _WIN32
EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif
}
diff --git a/strlcpy-internal.h b/strlcpy-internal.h
index 1968c003..4152b7bd 100644
--- a/strlcpy-internal.h
+++ b/strlcpy-internal.h
@@ -6,6 +6,7 @@ extern "C" {
#endif
#include "event2/event-config.h"
+#include "evconfig-private.h"
#ifndef _EVENT_HAVE_STRLCPY
#include <string.h>
diff --git a/strlcpy.c b/strlcpy.c
index 11053c33..3656e9f0 100644
--- a/strlcpy.c
+++ b/strlcpy.c
@@ -31,9 +31,10 @@
static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
#endif /* LIBC_SCCS and not lint */
-#include <sys/types.h>
-
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
+#include <sys/types.h>
#ifndef _EVENT_HAVE_STRLCPY
#include "strlcpy-internal.h"
diff --git a/test/Makefile.nmake b/test/Makefile.nmake
index a899941f..6f37db9b 100644
--- a/test/Makefile.nmake
+++ b/test/Makefile.nmake
@@ -1,5 +1,5 @@
-CFLAGS=/I.. /I../WIN32-Code /I../include /I../compat /DWIN32 /DHAVE_CONFIG_H /DTINYTEST_LOCAL
+CFLAGS=/I.. /I../WIN32-Code /I../include /I../compat /DHAVE_CONFIG_H /DTINYTEST_LOCAL
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo
diff --git a/test/bench.c b/test/bench.c
index 66b7d719..77ca8b53 100644
--- a/test/bench.c
+++ b/test/bench.c
@@ -40,7 +40,7 @@
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
@@ -125,14 +125,14 @@ run_once(void)
int
main(int argc, char **argv)
{
-#ifndef WIN32
+#ifndef _WIN32
struct rlimit rl;
#endif
int i, c;
struct timeval *tv;
int *cp;
-#ifdef WIN32
+#ifdef _WIN32
WSADATA WSAData;
WSAStartup(0x101, &WSAData);
#endif
@@ -156,7 +156,7 @@ main(int argc, char **argv)
}
}
-#ifndef WIN32
+#ifndef _WIN32
rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
perror("setrlimit");
diff --git a/test/bench_cascade.c b/test/bench_cascade.c
index 08b7f945..dc42f287 100644
--- a/test/bench_cascade.c
+++ b/test/bench_cascade.c
@@ -32,7 +32,7 @@
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
@@ -134,7 +134,7 @@ run_once(int num_pipes)
int
main(int argc, char **argv)
{
-#ifndef WIN32
+#ifndef _WIN32
struct rlimit rl;
#endif
int i, c;
@@ -152,7 +152,7 @@ main(int argc, char **argv)
}
}
-#ifndef WIN32
+#ifndef _WIN32
rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
perror("setrlimit");
diff --git a/test/bench_http.c b/test/bench_http.c
index f6fd81b2..6a1628fb 100644
--- a/test/bench_http.c
+++ b/test/bench_http.c
@@ -27,7 +27,7 @@
#include <sys/types.h>
#include <sys/stat.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
@@ -93,7 +93,7 @@ main(int argc, char **argv)
unsigned short port = 8080;
char *endptr = NULL;
-#ifdef WIN32
+#ifdef _WIN32
WSADATA WSAData;
WSAStartup(0x101, &WSAData);
#else
@@ -135,7 +135,7 @@ main(int argc, char **argv)
exit(1);
}
break;
-#ifdef WIN32
+#ifdef _WIN32
case 'i':
use_iocp = 1;
evthread_use_windows_threads();
diff --git a/test/bench_httpclient.c b/test/bench_httpclient.c
index cf667534..727abc67 100644
--- a/test/bench_httpclient.c
+++ b/test/bench_httpclient.c
@@ -25,8 +25,11 @@
*
*/
+/* for EVUTIL_ERR_CONNECT_RETRIABLE macro */
+#include "util-internal.h"
+
#include <sys/types.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
@@ -44,9 +47,6 @@
#include "event2/buffer.h"
#include "event2/util.h"
-/* for EVUTIL_ERR_CONNECT_RETRIABLE macro */
-#include "util-internal.h"
-
const char *resource = NULL;
struct event_base *base = NULL;
@@ -201,7 +201,7 @@ main(int argc, char **argv)
throughput = total_n_handled /
(total.tv_sec+ ((double)total.tv_usec)/1000000.0);
-#ifdef WIN32
+#ifdef _WIN32
#define I64_FMT "%I64d"
#define I64_TYP __int64
#else
diff --git a/test/regress.c b/test/regress.c
index 2d48583d..9c779672 100644
--- a/test/regress.c
+++ b/test/regress.c
@@ -24,8 +24,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
@@ -38,7 +39,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@@ -63,12 +64,11 @@
#include "event2/util.h"
#include "event-internal.h"
#include "evthread-internal.h"
-#include "util-internal.h"
#include "log-internal.h"
#include "regress.h"
-#ifndef WIN32
+#ifndef _WIN32
#include "regress.gen.h"
#endif
@@ -87,13 +87,12 @@ static struct timeval tcalled;
#define TEST1 "this is a test"
-#define SECONDS 1
#ifndef SHUT_WR
#define SHUT_WR 1
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define write(fd,buf,len) send((fd),(buf),(int)(len),0)
#define read(fd,buf,len) recv((fd),(buf),(int)(len),0)
#endif
@@ -233,21 +232,7 @@ multiple_read_cb(evutil_socket_t fd, short event, void *arg)
static void
timeout_cb(evutil_socket_t fd, short event, void *arg)
{
- struct timeval tv;
- int diff;
-
evutil_gettimeofday(&tcalled, NULL);
- if (evutil_timercmp(&tcalled, &tset, >))
- evutil_timersub(&tcalled, &tset, &tv);
- else
- evutil_timersub(&tset, &tcalled, &tv);
-
- diff = tv.tv_sec*1000 + tv.tv_usec/1000 - SECONDS * 1000;
- if (diff < 0)
- diff = -diff;
-
- if (diff < 100)
- test_ok = 1;
}
struct both {
@@ -414,7 +399,7 @@ test_simpleclose(void *ptr)
short got_read_on_close = 0, got_write_on_close = 0;
char buf[1024];
memset(buf, 99, sizeof(buf));
-#ifdef WIN32
+#ifdef _WIN32
#define LOCAL_SOCKETPAIR_AF AF_INET
#else
#define LOCAL_SOCKETPAIR_AF AF_UNIX
@@ -583,14 +568,18 @@ test_simpletimeout(void)
setup_test("Simple timeout: ");
- tv.tv_usec = 0;
- tv.tv_sec = SECONDS;
+ tv.tv_usec = 200*1000;
+ tv.tv_sec = 0;
+ evutil_timerclear(&tcalled);
evtimer_set(&ev, timeout_cb, NULL);
evtimer_add(&ev, &tv);
evutil_gettimeofday(&tset, NULL);
event_dispatch();
+ test_timeval_diff_eq(&tset, &tcalled, 200);
+ test_ok = 1;
+end:
cleanup_test();
}
@@ -707,7 +696,7 @@ common_timeout_cb(evutil_socket_t fd, short event, void *arg)
struct common_timeout_info *ti = arg;
++ti->count;
evutil_gettimeofday(&ti->called_at, NULL);
- if (ti->count >= 6)
+ if (ti->count >= 4)
event_del(&ti->ev);
}
@@ -720,7 +709,7 @@ test_common_timeout(void *ptr)
int i;
struct common_timeout_info info[100];
- struct timeval now;
+ struct timeval start;
struct timeval tmp_100_ms = { 0, 100*1000 };
struct timeval tmp_200_ms = { 0, 200*1000 };
@@ -751,23 +740,17 @@ test_common_timeout(void *ptr)
}
event_base_assert_ok(base);
+ evutil_gettimeofday(&start, NULL);
event_base_dispatch(base);
- evutil_gettimeofday(&now, NULL);
event_base_assert_ok(base);
for (i=0; i<10; ++i) {
- struct timeval tmp;
- int ms_diff;
- tt_int_op(info[i].count, ==, 6);
- evutil_timersub(&now, &info[i].called_at, &tmp);
- ms_diff = tmp.tv_usec/1000 + tmp.tv_sec*1000;
+ tt_int_op(info[i].count, ==, 4);
if (i % 2) {
- tt_int_op(ms_diff, >, 500);
- tt_int_op(ms_diff, <, 700);
+ test_timeval_diff_eq(&start, &info[i].called_at, 400);
} else {
- tt_int_op(ms_diff, >, -100);
- tt_int_op(ms_diff, <, 100);
+ test_timeval_diff_eq(&start, &info[i].called_at, 800);
}
}
@@ -786,7 +769,7 @@ end:
data->base = NULL;
}
-#ifndef WIN32
+#ifndef _WIN32
static void signal_cb(evutil_socket_t fd, short event, void *arg);
#define current_base event_global_current_base_
@@ -857,7 +840,10 @@ test_fork(void)
}
/* wait for the child to read the data */
- sleep(1);
+ {
+ const struct timeval tv = { 0, 100000 };
+ evutil_usleep(&tv);
+ }
if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) {
tt_fail_perror("write");
@@ -924,7 +910,8 @@ test_simplesignal(void)
evsignal_add(&ev, NULL);
memset(&itv, 0, sizeof(itv));
- itv.it_value.tv_sec = 1;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 100000;
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
goto skip_simplesignal;
@@ -951,7 +938,8 @@ test_multiplesignal(void)
evsignal_add(&ev_two, NULL);
memset(&itv, 0, sizeof(itv));
- itv.it_value.tv_sec = 1;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 100000;
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
goto skip_simplesignal;
@@ -1340,23 +1328,22 @@ test_loopexit(void)
evtimer_set(&ev, timeout_cb, NULL);
evtimer_add(&ev, &tv);
- tv.tv_usec = 0;
- tv.tv_sec = 1;
+ tv.tv_usec = 300*1000;
+ tv.tv_sec = 0;
event_loopexit(&tv);
evutil_gettimeofday(&tv_start, NULL);
event_dispatch();
evutil_gettimeofday(&tv_end, NULL);
- evutil_timersub(&tv_end, &tv_start, &tv_end);
evtimer_del(&ev);
tt_assert(event_base_got_exit(global_base));
tt_assert(!event_base_got_break(global_base));
- if (tv.tv_sec < 2)
- test_ok = 1;
+ test_timeval_diff_eq(&tv_start, &tv_end, 300);
+ test_ok = 1;
end:
cleanup_test();
}
@@ -1364,28 +1351,32 @@ end:
static void
test_loopexit_multiple(void)
{
- struct timeval tv;
+ struct timeval tv, tv_start, tv_end;
struct event_base *base;
setup_test("Loop Multiple exit: ");
base = event_base_new();
- tv.tv_usec = 0;
- tv.tv_sec = 1;
+ tv.tv_usec = 200*1000;
+ tv.tv_sec = 0;
event_base_loopexit(base, &tv);
tv.tv_usec = 0;
- tv.tv_sec = 2;
+ tv.tv_sec = 3;
event_base_loopexit(base, &tv);
+ evutil_gettimeofday(&tv_start, NULL);
event_base_dispatch(base);
+ evutil_gettimeofday(&tv_end, NULL);
tt_assert(event_base_got_exit(base));
tt_assert(!event_base_got_break(base));
event_base_free(base);
+ test_timeval_diff_eq(&tv_start, &tv_end, 200);
+
test_ok = 1;
end:
@@ -1659,7 +1650,7 @@ test_want_only_once(void)
/* Setup the loop termination */
evutil_timerclear(&tv);
- tv.tv_sec = 1;
+ tv.tv_usec = 300*1000;
event_loopexit(&tv);
event_set(&ev, pair[1], EV_READ, read_once_cb, &ev);
@@ -2032,6 +2023,15 @@ end:
}
static void
+immediate_called_twice_cb(evutil_socket_t fd, short event, void *arg)
+{
+ tt_int_op(event, ==, EV_TIMEOUT);
+ called += 1000;
+end:
+ ;
+}
+
+static void
test_event_once(void *ptr)
{
struct basic_test_data *data = ptr;
@@ -2049,6 +2049,14 @@ test_event_once(void *ptr)
tt_int_op(r, ==, 0);
r = event_base_once(data->base, -1, 0, NULL, NULL, NULL);
tt_int_op(r, <, 0);
+ r = event_base_once(data->base, -1, EV_TIMEOUT,
+ immediate_called_twice_cb, NULL, NULL);
+ tt_int_op(r, ==, 0);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ r = event_base_once(data->base, -1, EV_TIMEOUT,
+ immediate_called_twice_cb, NULL, &tv);
+ tt_int_op(r, ==, 0);
if (write(data->pair[1], TEST1, strlen(TEST1)+1) < 0) {
tt_fail_perror("write");
@@ -2058,7 +2066,7 @@ test_event_once(void *ptr)
event_base_dispatch(data->base);
- tt_int_op(called, ==, 101);
+ tt_int_op(called, ==, 2101);
end:
;
}
@@ -2068,7 +2076,7 @@ test_event_pending(void *ptr)
{
struct basic_test_data *data = ptr;
struct event *r=NULL, *w=NULL, *t=NULL;
- struct timeval tv, now, tv2, diff;
+ struct timeval tv, now, tv2;
tv.tv_sec = 0;
tv.tv_usec = 500 * 1000;
@@ -2091,10 +2099,8 @@ test_event_pending(void *ptr)
tt_assert( event_pending(t, EV_TIMEOUT, &tv2));
tt_assert(evutil_timercmp(&tv2, &now, >));
- evutil_timeradd(&now, &tv, &tv);
- evutil_timersub(&tv2, &tv, &diff);
- tt_int_op(diff.tv_sec, ==, 0);
- tt_int_op(labs(diff.tv_usec), <, 1000);
+
+ test_timeval_diff_eq(&now, &tv2, 500);
end:
if (r) {
@@ -2111,7 +2117,7 @@ end:
}
}
-#ifndef WIN32
+#ifndef _WIN32
/* You can't do this test on windows, since dup2 doesn't work on sockets */
static void
@@ -2268,7 +2274,6 @@ test_many_events(void *arg)
int called[MANY];
int i;
int loopflags = EVLOOP_NONBLOCK, evflags=0;
- const int is_evport = !strcmp(event_base_get_method(base),"evport");
if (one_at_a_time) {
loopflags |= EVLOOP_ONCE;
evflags = EV_PERSIST;
@@ -2277,10 +2282,6 @@ test_many_events(void *arg)
memset(sock, 0xff, sizeof(sock));
memset(ev, 0, sizeof(ev));
memset(called, 0, sizeof(called));
- if (is_evport && one_at_a_time) {
- TT_DECLARE("NOTE", ("evport can't pass this in 2.0; skipping\n"));
- tt_skip();
- }
for (i = 0; i < MANY; ++i) {
/* We need an event that will hit the backend, and that will
@@ -2365,7 +2366,7 @@ struct testcase_t main_testcases[] = {
{ "event_once", test_event_once, TT_ISOLATED, &basic_setup, NULL },
{ "event_pending", test_event_pending, TT_ISOLATED, &basic_setup,
NULL },
-#ifndef WIN32
+#ifndef _WIN32
{ "dup_fd", test_dup_fd, TT_ISOLATED, &basic_setup, NULL },
#endif
{ "mm_functions", test_mm_functions, TT_FORK, NULL, NULL },
@@ -2374,7 +2375,7 @@ struct testcase_t main_testcases[] = {
{ "struct_event_size", test_struct_event_size, 0, NULL, NULL },
-#ifndef WIN32
+#ifndef _WIN32
LEGACY(fork, TT_ISOLATED),
#endif
END_OF_TESTCASES
@@ -2390,7 +2391,7 @@ struct testcase_t evtag_testcases[] = {
};
struct testcase_t signal_testcases[] = {
-#ifndef WIN32
+#ifndef _WIN32
LEGACY(simplesignal, TT_ISOLATED),
LEGACY(multiplesignal, TT_ISOLATED),
LEGACY(immediatesignal, TT_ISOLATED),
diff --git a/test/regress.h b/test/regress.h
index 32adccdf..dd6e3fad 100644
--- a/test/regress.h
+++ b/test/regress.h
@@ -62,7 +62,7 @@ extern int called;
extern struct event_base *global_base;
extern int in_legacy_test_wrapper;
-int regress_make_tmpfile(const void *data, size_t datalen);
+int regress_make_tmpfile(const void *data, size_t datalen, char **filename_out);
struct basic_test_data {
struct event_base *base;
diff --git a/test/regress_buffer.c b/test/regress_buffer.c
index 6a2b6fcc..b22f083a 100644
--- a/test/regress_buffer.c
+++ b/test/regress_buffer.c
@@ -24,8 +24,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
@@ -38,7 +39,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@@ -620,41 +621,182 @@ test_evbuffer_reference(void *ptr)
evbuffer_free(src);
}
-int _evbuffer_testing_use_sendfile(void);
-int _evbuffer_testing_use_mmap(void);
-int _evbuffer_testing_use_linear_file_access(void);
+static struct event_base *addfile_test_event_base = NULL;
+static int addfile_test_done_writing = 0;
+static int addfile_test_total_written = 0;
+static int addfile_test_total_read = 0;
+
+static void
+addfile_test_writecb(evutil_socket_t fd, short what, void *arg)
+{
+ struct evbuffer *b = arg;
+ int r;
+ evbuffer_validate(b);
+ while (evbuffer_get_length(b)) {
+ r = evbuffer_write(b, fd);
+ if (r > 0) {
+ addfile_test_total_written += r;
+ TT_BLATHER(("Wrote %d/%d bytes", r, addfile_test_total_written));
+ } else {
+ int e = evutil_socket_geterror(fd);
+ if (EVUTIL_ERR_RW_RETRIABLE(e))
+ return;
+ tt_fail_perror("write");
+ event_base_loopexit(addfile_test_event_base,NULL);
+ }
+ evbuffer_validate(b);
+ }
+ addfile_test_done_writing = 1;
+ return;
+end:
+ event_base_loopexit(addfile_test_event_base,NULL);
+}
+
+static void
+addfile_test_readcb(evutil_socket_t fd, short what, void *arg)
+{
+ struct evbuffer *b = arg;
+ int e, r = 0;
+ do {
+ int r = evbuffer_read(b, fd, 1024);
+ if (r > 0) {
+ addfile_test_total_read += r;
+ TT_BLATHER(("Read %d/%d bytes", r, addfile_test_total_read));
+ }
+ } while (r > 0);
+ if (r < 0) {
+ e = evutil_socket_geterror(fd);
+ if (! EVUTIL_ERR_RW_RETRIABLE(e)) {
+ tt_fail_perror("read");
+ event_base_loopexit(addfile_test_event_base,NULL);
+ }
+ }
+ if (addfile_test_done_writing &&
+ addfile_test_total_read >= addfile_test_total_written) {
+ event_base_loopexit(addfile_test_event_base,NULL);
+ }
+}
static void
test_evbuffer_add_file(void *ptr)
{
- const char *impl = ptr;
- struct evbuffer *src = evbuffer_new();
- const char *data = "this is what we add as file system data.";
- size_t datalen;
+ struct basic_test_data *testdata = ptr;
+ const char *impl = testdata->setup_data;
+ struct evbuffer *src = evbuffer_new(), *dest = evbuffer_new();
+ char *tmpfilename = NULL;
+ char *data = NULL;
+ const char *expect_data;
+ size_t datalen, expect_len;
const char *compare;
int fd = -1;
+ int want_ismapping = -1, want_cansendfile = -1;
+ unsigned flags = 0;
+ int use_segment = 1, use_bigfile = 0, map_from_offset = 0,
+ view_from_offset = 0;
+ struct evbuffer_file_segment *seg = NULL;
+ ev_off_t starting_offset = 0, mapping_len = -1;
+ ev_off_t segment_offset = 0, segment_len = -1;
+ struct event *rev=NULL, *wev=NULL;
+ struct event_base *base = testdata->base;
evutil_socket_t pair[2] = {-1, -1};
- int r=0, n_written=0;
-
- /* Add a test for a big file. XXXX */
+ /* This test is highly parameterized based on substrings of its
+ * argument. The strings are: */
tt_assert(impl);
- if (!strcmp(impl, "sendfile")) {
- if (!_evbuffer_testing_use_sendfile())
- tt_skip();
- TT_BLATHER(("Using sendfile-based implementaion"));
- } else if (!strcmp(impl, "mmap")) {
- if (!_evbuffer_testing_use_mmap())
- tt_skip();
- TT_BLATHER(("Using mmap-based implementaion"));
- } else if (!strcmp(impl, "linear")) {
- if (!_evbuffer_testing_use_linear_file_access())
- tt_skip();
- TT_BLATHER(("Using read-based implementaion"));
+ if (strstr(impl, "nosegment")) {
+ /* If nosegment is set, use the older evbuffer_add_file
+ * interface */
+ use_segment = 0;
+ }
+ if (strstr(impl, "bigfile")) {
+ /* If bigfile is set, use a 512K file. Else use a smaller
+ * one. */
+ use_bigfile = 1;
+ }
+ if (strstr(impl, "map_offset")) {
+ /* If map_offset is set, we build the file segment starting
+ * from a point other than byte 0 and ending somewhere other
+ * than the last byte. Otherwise we map the whole thing */
+ map_from_offset = 1;
+ }
+ if (strstr(impl, "offset_in_segment")) {
+ /* If offset_in_segment is set, we add a subsection of the
+ * file semgment starting from a point other than byte 0 of
+ * the segment. */
+ view_from_offset = 1;
+ }
+ if (strstr(impl, "sendfile")) {
+ /* If sendfile is set, we try to use a sendfile/splice style
+ * backend. */
+ flags = EVBUF_FS_DISABLE_MMAP;
+ want_cansendfile = 1;
+ want_ismapping = 0;
+ } else if (strstr(impl, "mmap")) {
+ /* If sendfile is set, we try to use a mmap/CreateFileMapping
+ * style backend. */
+ flags = EVBUF_FS_DISABLE_SENDFILE;
+ want_ismapping = 1;
+ want_cansendfile = 0;
+ } else if (strstr(impl, "linear")) {
+ /* If linear is set, we try to use a read-the-whole-thing
+ * backend. */
+ flags = EVBUF_FS_DISABLE_SENDFILE|EVBUF_FS_DISABLE_MMAP;
+ want_ismapping = 0;
+ want_cansendfile = 0;
+ } else if (strstr(impl, "default")) {
+ /* The caller doesn't care which backend we use. */
+ ;
} else {
+ /* The caller must choose a backend. */
TT_DIE(("Didn't recognize the implementation"));
}
+ if (use_bigfile) {
+ unsigned int i;
+ datalen = 1024*512;
+ data = malloc(1024*512);
+ tt_assert(data);
+ for (i = 0; i < datalen; ++i)
+ data[i] = _evutil_weakrand();
+ } else {
+ data = strdup("here is a relatively small string.");
+ tt_assert(data);
+ datalen = strlen(data);
+ }
+
+ fd = regress_make_tmpfile(data, datalen, &tmpfilename);
+
+ if (map_from_offset) {
+ starting_offset = datalen/4 + 1;
+ mapping_len = datalen / 2 - 1;
+ expect_data = data + starting_offset;
+ expect_len = mapping_len;
+ } else {
+ expect_data = data;
+ expect_len = datalen;
+ }
+ if (view_from_offset) {
+ tt_assert(use_segment); /* Can't do this with add_file*/
+ segment_offset = expect_len / 3;
+ segment_len = expect_len / 2;
+ expect_data = expect_data + segment_offset;
+ expect_len = segment_len;
+ }
+
+ if (use_segment) {
+ seg = evbuffer_file_segment_new(fd, starting_offset,
+ mapping_len, flags);
+ tt_assert(seg);
+ if (want_ismapping >= 0) {
+ if (seg->is_mapping != (unsigned)want_ismapping)
+ tt_skip();
+ }
+ if (want_cansendfile >= 0) {
+ if (seg->can_sendfile != (unsigned)want_cansendfile)
+ tt_skip();
+ }
+ }
+
/* Say that it drains to a fd so that we can use sendfile. */
evbuffer_set_flags(src, EVBUFFER_FLAG_DRAINS_TO_FD);
@@ -667,39 +809,62 @@ test_evbuffer_add_file(void *ptr)
if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
tt_abort_msg("socketpair failed");
#endif
-
- datalen = strlen(data);
- fd = regress_make_tmpfile(data, datalen);
+ evutil_make_socket_nonblocking(pair[0]);
+ evutil_make_socket_nonblocking(pair[1]);
tt_assert(fd != -1);
- tt_assert(evbuffer_add_file(src, fd, 0, datalen) != -1);
+ if (use_segment) {
+ tt_assert(evbuffer_add_file_segment(src, seg,
+ segment_offset, segment_len)!=-1);
+ } else {
+ tt_assert(evbuffer_add_file(src, fd, starting_offset,
+ mapping_len) != -1);
+ }
evbuffer_validate(src);
- while (evbuffer_get_length(src) &&
- (r = evbuffer_write(src, pair[0])) > 0) {
- evbuffer_validate(src);
- n_written += r;
- }
- tt_int_op(r, !=, -1);
- tt_int_op(n_written, ==, datalen);
+ addfile_test_event_base = base;
+ wev = event_new(base, pair[0], EV_WRITE|EV_PERSIST,
+ addfile_test_writecb, src);
+ rev = event_new(base, pair[1], EV_READ|EV_PERSIST,
+ addfile_test_readcb, dest);
+
+ event_add(wev, NULL);
+ event_add(rev, NULL);
+ event_base_dispatch(base);
evbuffer_validate(src);
- tt_int_op(evbuffer_read(src, pair[1], (int)strlen(data)), ==, datalen);
- evbuffer_validate(src);
- compare = (char *)evbuffer_pullup(src, datalen);
+ evbuffer_validate(dest);
+
+ tt_assert(addfile_test_done_writing);
+ tt_int_op(addfile_test_total_written, ==, expect_len);
+ tt_int_op(addfile_test_total_read, ==, expect_len);
+
+ compare = (char *)evbuffer_pullup(dest, expect_len);
tt_assert(compare != NULL);
- if (memcmp(compare, data, datalen))
+ if (memcmp(compare, expect_data, expect_len)) {
tt_abort_msg("Data from add_file differs.");
+ }
- evbuffer_validate(src);
+ evbuffer_validate(dest);
end:
+ if (data)
+ free(data);
+ if (seg)
+ evbuffer_file_segment_free(seg);
+ if (src)
+ evbuffer_free(src);
+ if (dest)
+ evbuffer_free(dest);
if (pair[0] >= 0)
evutil_closesocket(pair[0]);
if (pair[1] >= 0)
evutil_closesocket(pair[1]);
- evbuffer_free(src);
+ if (tmpfilename) {
+ unlink(tmpfilename);
+ free(tmpfilename);
+ }
}
#ifndef _EVENT_DISABLE_MM_REPLACEMENT
@@ -855,6 +1020,32 @@ test_evbuffer_readln(void *ptr)
free(cp);
evbuffer_validate(evb);
+ /* Test NUL */
+ tt_int_op(evbuffer_get_length(evb), ==, 0);
+ {
+ char x[] =
+ "NUL\n\0\0"
+ "The all-zeros character which may serve\0"
+ "to accomplish time fill\0and media fill";
+ /* Add all but the final NUL of x. */
+ evbuffer_add(evb, x, sizeof(x)-1);
+ }
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("NUL\n");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("The all-zeros character which may serve");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_line_eq("to accomplish time fill");
+ free(cp);
+ cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL);
+ tt_ptr_op(cp, ==, NULL);
+ evbuffer_drain(evb, -1);
+
/* Test CRLF_STRICT - across boundaries*/
s = " and a bad crlf\nand a good one\r";
evbuffer_add(evb_tmp, s, strlen(s));
@@ -974,6 +1165,12 @@ test_evbuffer_search_eol(void *ptr)
tt_int_op(ptr2.pos, ==, 11);
tt_int_op(eol_len, ==, 1);
+ tt_assert(evbuffer_ptr_set(buf, &ptr1, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0);
+ eol_len = -1;
+ ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF);
+ tt_int_op(ptr2.pos, ==, -1);
+ tt_int_op(eol_len, ==, 0);
+
end:
evbuffer_free(buf);
}
@@ -1074,6 +1271,15 @@ test_evbuffer_ptr_set(void *ptr)
struct evbuffer_ptr pos;
struct evbuffer_iovec v[1];
+ tt_int_op(evbuffer_get_length(buf), ==, 0);
+
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ tt_assert(pos.pos == 0);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_ADD) == -1);
+ tt_assert(pos.pos == -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_SET) == -1);
+ tt_assert(pos.pos == -1);
+
/* create some chains */
evbuffer_reserve_space(buf, 5000, v, 1);
v[0].iov_len = 5000;
@@ -1106,6 +1312,8 @@ test_evbuffer_ptr_set(void *ptr)
tt_assert(pos.pos == 10000);
tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == 0);
tt_assert(pos.pos == 11000);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == 0);
+ tt_assert(pos.pos == 12000);
tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == -1);
tt_assert(pos.pos == -1);
@@ -1121,6 +1329,18 @@ test_evbuffer_search(void *ptr)
struct evbuffer *tmp = evbuffer_new();
struct evbuffer_ptr pos, end;
+ pos = evbuffer_search(buf, "x", 1, NULL);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search(buf, "x", 1, &pos);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "x", 1, &pos, &pos);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "x", 1, &pos, NULL);
+ tt_int_op(pos.pos, ==, -1);
+
/* set up our chains */
evbuffer_add_printf(tmp, "hello"); /* 5 chars */
evbuffer_add_buffer(buf, tmp);
@@ -1164,6 +1384,20 @@ test_evbuffer_search(void *ptr)
pos = evbuffer_search_range(buf, "ack", 3, NULL, &end);
tt_int_op(pos.pos, ==, -1);
+ /* Set "end" after the last byte in the buffer. */
+ tt_assert(evbuffer_ptr_set(buf, &end, 17, EVBUFFER_PTR_SET) == 0);
+
+ pos = evbuffer_search_range(buf, "attack", 6, NULL, &end);
+ tt_int_op(pos.pos, ==, 11);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 11, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "attack", 6, &pos, &end);
+ tt_int_op(pos.pos, ==, 11);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "attack", 6, &pos, &end);
+ tt_int_op(pos.pos, ==, -1);
+ tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0);
+ pos = evbuffer_search_range(buf, "attack", 6, &pos, NULL);
+ tt_int_op(pos.pos, ==, -1);
end:
if (buf)
@@ -1362,6 +1596,111 @@ end:
evbuffer_free(buf2);
}
+static void
+test_evbuffer_multicast(void *ptr)
+{
+ const char chunk1[] = "If you have found the answer to such a problem";
+ const char chunk2[] = "you ought to write it up for publication";
+ /* -- Knuth's "Notes on the Exercises" from TAOCP */
+ char tmp[16];
+ size_t len1 = strlen(chunk1), len2=strlen(chunk2);
+
+ struct evbuffer *buf1 = NULL, *buf2 = NULL;
+
+ buf1 = evbuffer_new();
+ tt_assert(buf1);
+
+ evbuffer_add(buf1, chunk1, len1);
+ evbuffer_add(buf1, ", ", 2);
+ evbuffer_add(buf1, chunk2, len2);
+ tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2);
+
+ buf2 = evbuffer_new();
+ tt_assert(buf2);
+
+ tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0);
+ /* nested references are not allowed */
+ tt_int_op(evbuffer_add_buffer_reference(buf2, buf2), ==, -1);
+ tt_int_op(evbuffer_add_buffer_reference(buf1, buf2), ==, -1);
+
+ /* both buffers contain the same amount of data */
+ tt_int_op(evbuffer_get_length(buf1), ==, evbuffer_get_length(buf1));
+
+ /* Make sure we can drain a little from the first buffer. */
+ tt_int_op(evbuffer_remove(buf1, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "If you", 6), ==, 0);
+ tt_int_op(evbuffer_remove(buf1, tmp, 5), ==, 5);
+ tt_int_op(memcmp(tmp, " have", 5), ==, 0);
+
+ /* Make sure that prepending does not meddle with immutable data */
+ tt_int_op(evbuffer_prepend(buf1, "I have ", 7), ==, 0);
+ tt_int_op(memcmp(chunk1, "If you", 6), ==, 0);
+ evbuffer_validate(buf1);
+
+ /* Make sure we can drain a little from the second buffer. */
+ tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "If you", 6), ==, 0);
+ tt_int_op(evbuffer_remove(buf2, tmp, 5), ==, 5);
+ tt_int_op(memcmp(tmp, " have", 5), ==, 0);
+
+ /* Make sure that prepending does not meddle with immutable data */
+ tt_int_op(evbuffer_prepend(buf2, "I have ", 7), ==, 0);
+ tt_int_op(memcmp(chunk1, "If you", 6), ==, 0);
+ evbuffer_validate(buf2);
+
+ /* Make sure the data can be read from the second buffer when the first is freed */
+ evbuffer_free(buf1);
+ buf1 = NULL;
+
+ tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, "I have", 6), ==, 0);
+
+ tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6);
+ tt_int_op(memcmp(tmp, " foun", 6), ==, 0);
+
+end:
+ if (buf1)
+ evbuffer_free(buf1);
+ if (buf2)
+ evbuffer_free(buf2);
+}
+
+static void
+test_evbuffer_multicast_drain(void *ptr)
+{
+ const char chunk1[] = "If you have found the answer to such a problem";
+ const char chunk2[] = "you ought to write it up for publication";
+ /* -- Knuth's "Notes on the Exercises" from TAOCP */
+ size_t len1 = strlen(chunk1), len2=strlen(chunk2);
+
+ struct evbuffer *buf1 = NULL, *buf2 = NULL;
+
+ buf1 = evbuffer_new();
+ tt_assert(buf1);
+
+ evbuffer_add(buf1, chunk1, len1);
+ evbuffer_add(buf1, ", ", 2);
+ evbuffer_add(buf1, chunk2, len2);
+ tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2);
+
+ buf2 = evbuffer_new();
+ tt_assert(buf2);
+
+ tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0);
+ tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2);
+ tt_int_op(evbuffer_drain(buf1, evbuffer_get_length(buf1)), ==, 0);
+ tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2);
+ tt_int_op(evbuffer_drain(buf2, evbuffer_get_length(buf2)), ==, 0);
+ evbuffer_validate(buf1);
+ evbuffer_validate(buf2);
+
+end:
+ if (buf1)
+ evbuffer_free(buf1);
+ if (buf2)
+ evbuffer_free(buf2);
+}
+
/* Some cases that we didn't get in test_evbuffer() above, for more coverage. */
static void
test_evbuffer_prepend(void *ptr)
@@ -1518,6 +1857,13 @@ test_evbuffer_peek(void *info)
tt_iov_eq(&v[0], "Contents of chunk [2]\n");
tt_iov_eq(&v[1], "Contents of chunk [3]\n"); /*more than we asked for*/
+ /* peek at the end of the buffer */
+ memset(v, 0, sizeof(v));
+ tt_assert(evbuffer_ptr_set(buf, &ptr, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0);
+ i = evbuffer_peek(buf, 44, &ptr, v, 20);
+ tt_int_op(i, ==, 0);
+ tt_assert(v[0].iov_base == NULL);
+
end:
if (buf)
evbuffer_free(buf);
@@ -1616,6 +1962,131 @@ end:
evbuffer_free(tmp_buf);
}
+static void
+test_evbuffer_add_iovec(void * ptr)
+{
+ struct evbuffer * buf = NULL;
+ struct evbuffer_iovec vec[4];
+ const char * data[] = {
+ "Guilt resembles a sword with two edges.",
+ "On the one hand, it cuts for Justice, imposing practical morality upon those who fear it.",
+ "Conscience does not always adhere to rational judgment.",
+ "Guilt is always a self-imposed burden, but it is not always rightly imposed."
+ /* -- R.A. Salvatore, _Sojurn_ */
+ };
+ size_t expected_length = 0;
+ size_t returned_length = 0;
+ int i;
+
+ buf = evbuffer_new();
+
+ for (i = 0; i < 4; i++) {
+ vec[i].iov_len = strlen(data[i]);
+ vec[i].iov_base = (char*) data[i];
+ expected_length += vec[i].iov_len;
+ }
+
+ returned_length = evbuffer_add_iovec(buf, vec, 4);
+
+ tt_int_op(returned_length, ==, evbuffer_get_length(buf));
+ tt_int_op(evbuffer_get_length(buf), ==, expected_length);
+
+ for (i = 0; i < 4; i++) {
+ char charbuf[1024];
+
+ memset(charbuf, 0, 1024);
+ evbuffer_remove(buf, charbuf, strlen(data[i]));
+ tt_assert(strcmp(charbuf, data[i]) == 0);
+ }
+
+ tt_assert(evbuffer_get_length(buf) == 0);
+end:
+ if (buf) {
+ evbuffer_free(buf);
+ }
+}
+
+static void
+test_evbuffer_copyout(void *dummy)
+{
+ const char string[] =
+ "Still they skirmish to and fro, men my messmates on the snow "
+ "When we headed off the aurochs turn for turn; "
+ "When the rich Allobrogenses never kept amanuenses, "
+ "And our only plots were piled in lakes at Berne.";
+ /* -- Kipling, "In The Neolithic Age" */
+ char tmp[256];
+ struct evbuffer_ptr ptr;
+ struct evbuffer *buf;
+
+ (void)dummy;
+
+ buf = evbuffer_new();
+ tt_assert(buf);
+
+ tt_int_op(strlen(string), ==, 206);
+
+ /* Ensure separate chains */
+ evbuffer_add_reference(buf, string, 80, no_cleanup, NULL);
+ evbuffer_add_reference(buf, string+80, 80, no_cleanup, NULL);
+ evbuffer_add(buf, string+160, strlen(string)-160);
+
+ tt_int_op(206, ==, evbuffer_get_length(buf));
+
+ /* First, let's test plain old copyout. */
+
+ /* Copy a little from the beginning. */
+ tt_int_op(10, ==, evbuffer_copyout(buf, tmp, 10));
+ tt_int_op(0, ==, memcmp(tmp, "Still they", 10));
+
+ /* Now copy more than a little from the beginning */
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(100, ==, evbuffer_copyout(buf, tmp, 100));
+ tt_int_op(0, ==, memcmp(tmp, string, 100));
+
+ /* Copy too much; ensure truncation. */
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(206, ==, evbuffer_copyout(buf, tmp, 230));
+ tt_int_op(0, ==, memcmp(tmp, string, 206));
+
+ /* That was supposed to be nondestructive, btw */
+ tt_int_op(206, ==, evbuffer_get_length(buf));
+
+ /* Now it's time to test copyout_from! First, let's start in the
+ * first chain. */
+ evbuffer_ptr_set(buf, &ptr, 15, EVBUFFER_PTR_SET);
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(10, ==, evbuffer_copyout_from(buf, &ptr, tmp, 10));
+ tt_int_op(0, ==, memcmp(tmp, "mish to an", 10));
+
+ /* Right up to the end of the first chain */
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(65, ==, evbuffer_copyout_from(buf, &ptr, tmp, 65));
+ tt_int_op(0, ==, memcmp(tmp, string+15, 65));
+
+ /* Span into the second chain */
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(90, ==, evbuffer_copyout_from(buf, &ptr, tmp, 90));
+ tt_int_op(0, ==, memcmp(tmp, string+15, 90));
+
+ /* Span into the third chain */
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(160, ==, evbuffer_copyout_from(buf, &ptr, tmp, 160));
+ tt_int_op(0, ==, memcmp(tmp, string+15, 160));
+
+ /* Overrun */
+ memset(tmp, 0, sizeof(tmp));
+ tt_int_op(206-15, ==, evbuffer_copyout_from(buf, &ptr, tmp, 999));
+ tt_int_op(0, ==, memcmp(tmp, string+15, 206-15));
+
+ /* That was supposed to be nondestructive, too */
+ tt_int_op(206, ==, evbuffer_get_length(buf));
+
+end:
+ if (buf)
+ evbuffer_free(buf);
+}
+
static void *
setup_passthrough(const struct testcase_t *testcase)
{
@@ -1650,17 +2121,38 @@ struct testcase_t evbuffer_testcases[] = {
{ "search", test_evbuffer_search, 0, NULL, NULL },
{ "callbacks", test_evbuffer_callbacks, 0, NULL, NULL },
{ "add_reference", test_evbuffer_add_reference, 0, NULL, NULL },
+ { "multicast", test_evbuffer_multicast, 0, NULL, NULL },
+ { "multicast_drain", test_evbuffer_multicast_drain, 0, NULL, NULL },
{ "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL },
{ "peek", test_evbuffer_peek, 0, NULL, NULL },
{ "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" },
{ "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" },
- /* TODO: need a temp file implementation for Windows */
- { "add_file_sendfile", test_evbuffer_add_file, TT_FORK, &nil_setup,
- (void*)"sendfile" },
- { "add_file_mmap", test_evbuffer_add_file, TT_FORK, &nil_setup,
- (void*)"mmap" },
- { "add_file_linear", test_evbuffer_add_file, TT_FORK, &nil_setup,
- (void*)"linear" },
+ { "add_iovec", test_evbuffer_add_iovec, 0, NULL, NULL},
+ { "copyout", test_evbuffer_copyout, 0, NULL, NULL},
+
+#define ADDFILE_TEST(name, parameters) \
+ { name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \
+ &basic_setup, (void*)(parameters) }
+
+#define ADDFILE_TEST_GROUP(name, parameters) \
+ ADDFILE_TEST(name "_sendfile", "sendfile " parameters), \
+ ADDFILE_TEST(name "_mmap", "mmap " parameters), \
+ ADDFILE_TEST(name "_linear", "linear " parameters)
+
+ ADDFILE_TEST_GROUP("add_file", ""),
+ ADDFILE_TEST("add_file_nosegment", "default nosegment"),
+
+ ADDFILE_TEST_GROUP("add_big_file", "bigfile"),
+ ADDFILE_TEST("add_big_file_nosegment", "default nosegment bigfile"),
+
+ ADDFILE_TEST_GROUP("add_file_offset", "bigfile map_offset"),
+ ADDFILE_TEST("add_file_offset_nosegment",
+ "default nosegment bigfile map_offset"),
+
+ ADDFILE_TEST_GROUP("add_file_offset2", "bigfile offset_in_segment"),
+
+ ADDFILE_TEST_GROUP("add_file_offset3",
+ "bigfile offset_in_segment map_offset"),
END_OF_TESTCASES
};
diff --git a/test/regress_bufferevent.c b/test/regress_bufferevent.c
index 2a7097ce..dae83f90 100644
--- a/test/regress_bufferevent.c
+++ b/test/regress_bufferevent.c
@@ -24,11 +24,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
/* The old tests here need assertions to work. */
#undef NDEBUG
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
@@ -41,7 +42,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@@ -74,7 +75,7 @@
#include "event2/util.h"
#include "bufferevent-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "iocp-internal.h"
#endif
@@ -486,7 +487,7 @@ test_bufferevent_connect(void *arg)
be_flags |= BEV_OPT_THREADSAFE;
}
bufferevent_connect_test_flags = be_flags;
-#ifdef WIN32
+#ifdef _WIN32
if (!strcmp((char*)data->setup_data, "unset_connectex")) {
struct win32_extension_fns *ext =
(struct win32_extension_fns *)
@@ -746,8 +747,8 @@ test_bufferevent_timeouts(void *arg)
bufferevent_set_timeouts(bev2, &tv_r, &tv_w);
bufferevent_enable(bev2, EV_WRITE);
- tv_r.tv_sec = 1;
- tv_r.tv_usec = 0;
+ tv_r.tv_sec = 0;
+ tv_r.tv_usec = 350000;
event_base_loopexit(data->base, &tv_r);
event_base_dispatch(data->base);
diff --git a/test/regress_dns.c b/test/regress_dns.c
index a566469b..69f61ec1 100644
--- a/test/regress_dns.c
+++ b/test/regress_dns.c
@@ -24,8 +24,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "../util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
@@ -39,7 +40,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
@@ -71,8 +72,6 @@
#include "regress.h"
#include "regress_testutils.h"
-#include "../util-internal.h"
-
static int dns_ok = 0;
static int dns_got_cancel = 0;
static int dns_err = 0;
@@ -684,9 +683,9 @@ dns_retry_test(void *arg)
dns = evdns_base_new(base, 0);
tt_assert(!evdns_base_nameserver_ip_add(dns, buf));
- tt_assert(! evdns_base_set_option(dns, "timeout", "0.3"));
+ tt_assert(! evdns_base_set_option(dns, "timeout", "0.2"));
tt_assert(! evdns_base_set_option(dns, "max-timeouts:", "10"));
- tt_assert(! evdns_base_set_option(dns, "initial-probe-timeout", "0.5"));
+ tt_assert(! evdns_base_set_option(dns, "initial-probe-timeout", "0.1"));
evdns_base_resolve_ipv4(dns, "host.example.com", 0,
generic_dns_callback, &r1);
@@ -705,8 +704,8 @@ dns_retry_test(void *arg)
/* Now try again, but this time have the server get treated as
* failed, so we can send it a test probe. */
drop_count = 4;
- tt_assert(! evdns_base_set_option(dns, "max-timeouts:", "3"));
- tt_assert(! evdns_base_set_option(dns, "attempts:", "4"));
+ tt_assert(! evdns_base_set_option(dns, "max-timeouts:", "2"));
+ tt_assert(! evdns_base_set_option(dns, "attempts:", "3"));
memset(&r1, 0, sizeof(r1));
evdns_base_resolve_ipv4(dns, "host.example.com", 0,
@@ -883,6 +882,8 @@ be_getaddrinfo_server_cb(struct evdns_server_request *req, void *data)
struct in6_addr ans6;
memset(&ans6, 0, sizeof(ans6));
+ TT_BLATHER(("Got question about %s, type=%d", qname, qtype));
+
if (qtype == EVDNS_TYPE_A &&
qclass == EVDNS_CLASS_INET &&
!evutil_ascii_strcasecmp(qname, "nobodaddy.example.com")) {
@@ -983,10 +984,13 @@ be_getaddrinfo_server_cb(struct evdns_server_request *req, void *data)
TT_GRIPE(("Got weird request for %s",qname));
}
}
- if (added_any)
+ if (added_any) {
+ TT_BLATHER(("answering"));
evdns_server_request_respond(req, 0);
- else
+ } else {
+ TT_BLATHER(("saying nexist."));
evdns_server_request_respond(req, 3);
+ }
}
/* Implements a listener for connect_hostname test. */
@@ -1215,6 +1219,9 @@ test_getaddrinfo_async(void *arg)
/* for localhost */
evdns_base_load_hosts(dns_base, NULL);
+ tt_assert(! evdns_base_set_option(dns_base, "timeout", "0.3"));
+ tt_assert(! evdns_base_set_option(dns_base, "getaddrinfo-allow-skew", "0.2"));
+
memset(a_out, 0, sizeof(a_out));
n_gai_results_pending = 10000; /* don't think about exiting yet. */
diff --git a/test/regress_et.c b/test/regress_et.c
index dee7b936..ca5d9b57 100644
--- a/test/regress_et.c
+++ b/test/regress_et.c
@@ -26,7 +26,7 @@
#include "../util-internal.h"
#include "event2/event-config.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#endif
#include <sys/types.h>
@@ -38,7 +38,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#endif
@@ -71,7 +71,7 @@ read_cb(evutil_socket_t fd, short event, void *arg)
#define SHUT_WR 1
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define LOCAL_SOCKETPAIR_AF AF_INET
#else
#define LOCAL_SOCKETPAIR_AF AF_UNIX
diff --git a/test/regress_http.c b/test/regress_http.c
index aea318f4..da34f099 100644
--- a/test/regress_http.c
+++ b/test/regress_http.c
@@ -24,8 +24,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
@@ -39,7 +40,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <signal.h>
#include <unistd.h>
@@ -59,7 +60,6 @@
#include "event2/bufferevent.h"
#include "event2/util.h"
#include "log-internal.h"
-#include "util-internal.h"
#include "http-internal.h"
#include "regress.h"
#include "regress_testutils.h"
@@ -154,7 +154,7 @@ http_connect(const char *address, u_short port)
evutil_make_socket_nonblocking(fd);
if (connect(fd, sa, slen) == -1) {
-#ifdef WIN32
+#ifdef _WIN32
int tmp_err = WSAGetLastError();
if (tmp_err != WSAEINPROGRESS && tmp_err != WSAEINVAL &&
tmp_err != WSAEWOULDBLOCK)
@@ -487,7 +487,7 @@ http_badreq_errorcb(struct bufferevent *bev, short what, void *arg)
}
#ifndef SHUT_WR
-#ifdef WIN32
+#ifdef _WIN32
#define SHUT_WR SD_SEND
#else
#define SHUT_WR 1
@@ -628,7 +628,7 @@ http_large_delay_cb(struct evhttp_request *req, void *arg)
{
struct timeval tv;
evutil_timerclear(&tv);
- tv.tv_sec = 3;
+ tv.tv_usec = 500000;
event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv);
evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF);
@@ -1769,7 +1769,7 @@ close_detect_done(struct evhttp_request *req, void *arg)
end:
evutil_timerclear(&tv);
- tv.tv_sec = 3;
+ tv.tv_usec = 150000;
event_base_loopexit(arg, &tv);
}
@@ -1803,9 +1803,10 @@ close_detect_cb(struct evhttp_request *req, void *arg)
}
evutil_timerclear(&tv);
- tv.tv_sec = 3; /* longer than the http time out */
+ tv.tv_sec = 0; /* longer than the http time out */
+ tv.tv_usec = 600000; /* longer than the http time out */
- /* launch a new request on the persistent connection in 3 seconds */
+ /* launch a new request on the persistent connection in .3 seconds */
event_base_once(base, -1, EV_TIMEOUT, close_detect_launch, evcon, &tv);
end:
;
@@ -1818,15 +1819,19 @@ _http_close_detection(struct basic_test_data *data, int with_delay)
ev_uint16_t port = 0;
struct evhttp_connection *evcon = NULL;
struct evhttp_request *req = NULL;
+ const struct timeval sec_tenth = { 0, 100000 };
test_ok = 0;
http = http_setup(&port, data->base);
- /* 2 second timeout */
- evhttp_set_timeout(http, 1);
+ /* .1 second timeout */
+ evhttp_set_timeout_tv(http, &sec_tenth);
evcon = evhttp_connection_base_new(data->base, NULL,
"127.0.0.1", port);
+ evhttp_connection_set_timeout_tv(evcon, &sec_tenth);
+
+
tt_assert(evcon);
delayed_client = evcon;
@@ -3143,7 +3148,12 @@ http_connection_retry_test(void *arg)
*/
test_ok = 0;
- evhttp_connection_set_timeout(evcon, 1);
+ {
+ const struct timeval tv_timeout = { 0, 300000 };
+ const struct timeval tv_retry = { 0, 200000 };
+ evhttp_connection_set_timeout_tv(evcon, &tv_timeout);
+ evhttp_connection_set_initial_retry_tv(evcon, &tv_retry);
+ }
evhttp_connection_set_retries(evcon, 1);
req = evhttp_request_new(http_connection_retry_done, data->base);
@@ -3160,9 +3170,9 @@ http_connection_retry_test(void *arg)
evutil_gettimeofday(&tv_start, NULL);
event_base_dispatch(data->base);
evutil_gettimeofday(&tv_end, NULL);
- evutil_timersub(&tv_end, &tv_start, &tv_end);
- tt_int_op(tv_end.tv_sec, >, 1);
- tt_int_op(tv_end.tv_sec, <, 6);
+
+ /* fails fast, .2 sec to wait to retry, fails fast again. */
+ test_timeval_diff_eq(&tv_start, &tv_end, 200);
tt_assert(test_ok == 1);
@@ -3186,22 +3196,19 @@ http_connection_retry_test(void *arg)
tt_abort_msg("Couldn't make request");
}
- /* start up a web server one second after the connection tried
+ /* start up a web server .2 seconds after the connection tried
* to send a request
*/
evutil_timerclear(&tv);
- tv.tv_sec = 1;
+ tv.tv_usec = 200000;
http_make_web_server_base = data->base;
event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &port, &tv);
evutil_gettimeofday(&tv_start, NULL);
event_base_dispatch(data->base);
evutil_gettimeofday(&tv_end, NULL);
-
- evutil_timersub(&tv_end, &tv_start, &tv_end);
-
- tt_int_op(tv_end.tv_sec, >, 1);
- tt_int_op(tv_end.tv_sec, <, 6);
+ /* We'll wait twice as long as we did last time. */
+ test_timeval_diff_eq(&tv_start, &tv_end, 400);
tt_int_op(test_ok, ==, 1);
diff --git a/test/regress_listener.c b/test/regress_listener.c
index f6c22cb0..d45c7fed 100644
--- a/test/regress_listener.c
+++ b/test/regress_listener.c
@@ -23,15 +23,16 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
#include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
@@ -49,7 +50,6 @@
#include "regress.h"
#include "tinytest.h"
#include "tinytest_macros.h"
-#include "util-internal.h"
static void
acceptcb(struct evconnlistener *listener, evutil_socket_t fd,
@@ -119,7 +119,7 @@ regress_pick_a_port(void *arg)
evutil_socket_connect(&fd2, (struct sockaddr*)&ss1, slen1);
evutil_socket_connect(&fd3, (struct sockaddr*)&ss2, slen2);
-#ifdef WIN32
+#ifdef _WIN32
Sleep(100); /* XXXX this is a stupid stopgap. */
#endif
event_base_dispatch(base);
diff --git a/test/regress_main.c b/test/regress_main.c
index 42a0366a..23a8cd11 100644
--- a/test/regress_main.c
+++ b/test/regress_main.c
@@ -24,8 +24,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
@@ -51,7 +52,7 @@
#include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@@ -85,7 +86,6 @@ timeval_msec_diff(const struct timeval *start, const struct timeval *end)
ms *= 1000;
ms += ((end->tv_usec - start->tv_usec)+500) / 1000;
return ms;
-
}
/* ============================================================ */
@@ -107,13 +107,15 @@ static void dnslogcb(int w, const char *m)
TT_BLATHER(("%s", m));
}
-/* creates a temporary file with the data in it */
+/* creates a temporary file with the data in it. If *filename_out gets set,
+ * the caller should try to unlink it. */
int
-regress_make_tmpfile(const void *data, size_t datalen)
+regress_make_tmpfile(const void *data, size_t datalen, char **filename_out)
{
-#ifndef WIN32
+#ifndef _WIN32
char tmpfilename[32];
int fd;
+ *filename_out = NULL;
strcpy(tmpfilename, "/tmp/eventtmp.XXXXXX");
fd = mkstemp(tmpfilename);
if (fd == -1)
@@ -148,6 +150,7 @@ regress_make_tmpfile(const void *data, size_t datalen)
if (tries == 0)
return (-1);
written = 0;
+ *filename_out = strdup(tmpfilename);
WriteFile(h, data, (DWORD)datalen, &written, NULL);
/* Closing the fd returned by this function will indeed close h. */
return _open_osfhandle((intptr_t)h,_O_RDONLY);
@@ -166,7 +169,7 @@ basic_test_setup(const struct testcase_t *testcase)
evutil_socket_t spair[2] = { -1, -1 };
struct basic_test_data *data = NULL;
-#ifndef WIN32
+#ifndef _WIN32
if (testcase->flags & TT_ENABLE_IOCP_FLAG)
return (void*)TT_SKIP;
#endif
@@ -328,7 +331,7 @@ const struct testcase_setup_t legacy_setup = {
/* ============================================================ */
-#if (!defined(_EVENT_HAVE_PTHREADS) && !defined(WIN32)) || defined(_EVENT_DISABLE_THREAD_SUPPORT)
+#if (!defined(_EVENT_HAVE_PTHREADS) && !defined(_WIN32)) || defined(_EVENT_DISABLE_THREAD_SUPPORT)
struct testcase_t thread_testcases[] = {
{ "basic", NULL, TT_SKIP, NULL, NULL },
END_OF_TESTCASES
@@ -349,7 +352,7 @@ struct testgroup_t testgroups[] = {
{ "rpc/", rpc_testcases },
{ "thread/", thread_testcases },
{ "listener/", listener_testcases },
-#ifdef WIN32
+#ifdef _WIN32
{ "iocp/", iocp_testcases },
{ "iocp/bufferevent/", bufferevent_iocp_testcases },
{ "iocp/listener/", listener_iocp_testcases },
@@ -363,7 +366,7 @@ struct testgroup_t testgroups[] = {
int
main(int argc, const char **argv)
{
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
@@ -373,12 +376,12 @@ main(int argc, const char **argv)
err = WSAStartup(wVersionRequested, &wsaData);
#endif
-#ifndef WIN32
+#ifndef _WIN32
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return 1;
#endif
-#ifdef WIN32
+#ifdef _WIN32
tinytest_skip(testgroups, "http/connection_retry");
#endif
diff --git a/test/regress_minheap.c b/test/regress_minheap.c
index 049d22a0..98f85bb8 100644
--- a/test/regress_minheap.c
+++ b/test/regress_minheap.c
@@ -23,13 +23,13 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "../minheap-internal.h"
#include <stdlib.h>
#include "event2/event_struct.h"
#include "tinytest.h"
#include "tinytest_macros.h"
-#include "../minheap-internal.h"
static void
set_random_timeout(struct event *ev)
diff --git a/test/regress_rpc.c b/test/regress_rpc.c
index 922fdc10..71ff1b50 100644
--- a/test/regress_rpc.c
+++ b/test/regress_rpc.c
@@ -28,7 +28,7 @@
/* The old tests here need assertions to work. */
#undef NDEBUG
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
@@ -41,7 +41,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <signal.h>
#include <unistd.h>
@@ -687,8 +687,8 @@ rpc_client_timeout(void)
pool = rpc_pool_with_connection(port);
- /* set the timeout to 5 seconds */
- evrpc_pool_set_timeout(pool, 5);
+ /* set the timeout to 1 second. */
+ evrpc_pool_set_timeout(pool, 1);
/* set up the basic message */
msg = msg_new();
diff --git a/test/regress_ssl.c b/test/regress_ssl.c
index a2b7f05b..c0c20279 100644
--- a/test/regress_ssl.c
+++ b/test/regress_ssl.c
@@ -24,12 +24,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
diff --git a/test/regress_testutils.c b/test/regress_testutils.c
index 9b3046e0..e74f212d 100644
--- a/test/regress_testutils.c
+++ b/test/regress_testutils.c
@@ -23,8 +23,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "../util-internal.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
@@ -38,7 +39,7 @@
#include <sys/time.h>
#endif
#include <sys/queue.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
@@ -68,8 +69,6 @@
#include "regress.h"
#include "regress_testutils.h"
-#include "../util-internal.h"
-
/* globals */
static struct evdns_server_port *dns_port;
evutil_socket_t dns_sock = -1;
diff --git a/test/regress_thread.c b/test/regress_thread.c
index 1acf6824..2cd5948f 100644
--- a/test/regress_thread.c
+++ b/test/regress_thread.c
@@ -23,6 +23,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "util-internal.h"
/* The old tests here need assertions to work. */
#undef NDEBUG
@@ -42,7 +43,7 @@
#ifdef _EVENT_HAVE_PTHREADS
#include <pthread.h>
-#elif defined(WIN32)
+#elif defined(_WIN32)
#include <process.h>
#endif
#include <assert.h>
@@ -53,10 +54,10 @@
#include "sys/queue.h"
-#include "event2/util.h"
#include "event2/event.h"
#include "event2/event_struct.h"
#include "event2/thread.h"
+#include "event2/util.h"
#include "evthread-internal.h"
#include "event-internal.h"
#include "defer-internal.h"
@@ -156,7 +157,7 @@ basic_thread(void *arg)
}
static int notification_fd_used = 0;
-#ifndef WIN32
+#ifndef _WIN32
static int got_sigchld = 0;
static void
sigchld_cb(evutil_socket_t fd, short event, void *arg)
@@ -199,7 +200,7 @@ thread_basic(void *arg)
tt_abort_msg("Couldn't make base notifiable!");
}
-#ifndef WIN32
+#ifndef _WIN32
if (data->setup_data && !strcmp(data->setup_data, "forking")) {
pid_t pid;
int status;
@@ -403,19 +404,22 @@ end:
#define CB_COUNT 128
#define QUEUE_THREAD_COUNT 8
-#ifdef WIN32
-#define SLEEP_MS(ms) Sleep(ms)
-#else
-#define SLEEP_MS(ms) usleep((ms) * 1000)
-#endif
+static void
+SLEEP_MS(int ms)
+{
+ struct timeval tv;
+ tv.tv_sec = ms/1000;
+ tv.tv_usec = (ms%1000)*1000;
+ evutil_usleep(&tv);
+}
struct deferred_test_data {
struct deferred_cb cbs[CB_COUNT];
struct deferred_cb_queue *queue;
};
-static time_t timer_start = 0;
-static time_t timer_end = 0;
+static struct timeval timer_start = {0,0};
+static struct timeval timer_end = {0,0};
static unsigned callback_count = 0;
static THREAD_T load_threads[QUEUE_THREAD_COUNT];
static struct deferred_test_data deferred_data[QUEUE_THREAD_COUNT];
@@ -445,7 +449,7 @@ load_deferred_queue(void *arg)
static void
timer_callback(evutil_socket_t fd, short what, void *arg)
{
- timer_end = time(NULL);
+ evutil_gettimeofday(&timer_end, NULL);
}
static void
@@ -463,9 +467,10 @@ static void
thread_deferred_cb_skew(void *arg)
{
struct basic_test_data *data = arg;
- struct timeval tv_timer = {4, 0};
+ struct timeval tv_timer = {1, 0};
struct deferred_cb_queue *queue;
- time_t elapsed;
+ struct timeval elapsed;
+ int elapsed_usec;
int i;
queue = event_base_get_deferred_cb_queue(data->base);
@@ -474,25 +479,97 @@ thread_deferred_cb_skew(void *arg)
for (i = 0; i < QUEUE_THREAD_COUNT; ++i)
deferred_data[i].queue = queue;
- timer_start = time(NULL);
+ evutil_gettimeofday(&timer_start, NULL);
event_base_once(data->base, -1, EV_TIMEOUT, timer_callback, NULL,
&tv_timer);
event_base_once(data->base, -1, EV_TIMEOUT, start_threads_callback,
NULL, NULL);
event_base_dispatch(data->base);
- elapsed = timer_end - timer_start;
+ evutil_timersub(&timer_end, &timer_start, &elapsed);
TT_BLATHER(("callback count, %u", callback_count));
- TT_BLATHER(("elapsed time, %u", (unsigned)elapsed));
+ elapsed_usec =
+ (unsigned)(elapsed.tv_sec*1000000 + elapsed.tv_usec);
+ TT_BLATHER(("elapsed time, %u usec", elapsed_usec));
+
/* XXX be more intelligent here. just make sure skew is
- * within 2 seconds for now. */
- tt_assert(elapsed >= 4 && elapsed <= 6);
+ * within .4 seconds for now. */
+ tt_assert(elapsed_usec >= 600000 && elapsed_usec <= 1400000);
end:
for (i = 0; i < QUEUE_THREAD_COUNT; ++i)
THREAD_JOIN(load_threads[i]);
}
+static struct event time_events[5];
+static struct timeval times[5];
+static struct event_base *exit_base = NULL;
+static void
+note_time_cb(evutil_socket_t fd, short what, void *arg)
+{
+ evutil_gettimeofday(arg, NULL);
+ if (arg == &times[4]) {
+ event_base_loopbreak(exit_base);
+ }
+}
+static THREAD_FN
+register_events_subthread(void *arg)
+{
+ struct timeval tv = {0,0};
+ SLEEP_MS(100);
+ event_active(&time_events[0], EV_TIMEOUT, 1);
+ SLEEP_MS(100);
+ event_active(&time_events[1], EV_TIMEOUT, 1);
+ SLEEP_MS(100);
+ tv.tv_usec = 100*1000;
+ event_add(&time_events[2], &tv);
+ tv.tv_usec = 150*1000;
+ event_add(&time_events[3], &tv);
+ SLEEP_MS(200);
+ event_active(&time_events[4], EV_TIMEOUT, 1);
+
+ THREAD_RETURN();
+}
+
+static void
+thread_no_events(void *arg)
+{
+ THREAD_T thread;
+ struct basic_test_data *data = arg;
+ struct timeval starttime, endtime;
+ int i;
+ exit_base = data->base;
+
+ memset(times,0,sizeof(times));
+ for (i=0;i<5;++i) {
+ event_assign(&time_events[i], data->base,
+ -1, 0, note_time_cb, &times[i]);
+ }
+
+ evutil_gettimeofday(&starttime, NULL);
+ THREAD_START(thread, register_events_subthread, data->base);
+ event_base_loop(data->base, EVLOOP_NO_EXIT_ON_EMPTY);
+ evutil_gettimeofday(&endtime, NULL);
+ tt_assert(event_base_got_break(data->base));
+ THREAD_JOIN(thread);
+ for (i=0; i<5; ++i) {
+ struct timeval diff;
+ double sec;
+ evutil_timersub(&times[i], &starttime, &diff);
+ sec = diff.tv_sec + diff.tv_usec/1.0e6;
+ TT_BLATHER(("event %d at %.4f seconds", i, sec));
+ }
+ test_timeval_diff_eq(&starttime, &times[0], 100);
+ test_timeval_diff_eq(&starttime, &times[1], 200);
+ test_timeval_diff_eq(&starttime, &times[2], 400);
+ test_timeval_diff_eq(&starttime, &times[3], 450);
+ test_timeval_diff_eq(&starttime, &times[4], 500);
+ test_timeval_diff_eq(&starttime, &endtime, 500);
+
+end:
+ ;
+}
+
#define TEST(name) \
{ #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \
&basic_setup, NULL }
@@ -500,12 +577,13 @@ end:
struct testcase_t thread_testcases[] = {
{ "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
&basic_setup, NULL },
-#ifndef WIN32
+#ifndef _WIN32
{ "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
&basic_setup, (char*)"forking" },
#endif
TEST(conditions_simple),
TEST(deferred_cb_skew),
+ TEST(no_events),
END_OF_TESTCASES
};
diff --git a/test/regress_util.c b/test/regress_util.c
index 88d80577..92c2e442 100644
--- a/test/regress_util.c
+++ b/test/regress_util.c
@@ -23,7 +23,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifdef WIN32
+#include "../util-internal.h"
+
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
@@ -33,7 +35,7 @@
#include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -53,9 +55,9 @@
#include "event2/event.h"
#include "event2/util.h"
#include "../ipv6-internal.h"
-#include "../util-internal.h"
#include "../log-internal.h"
#include "../strlcpy-internal.h"
+#include "../mm-internal.h"
#include "regress.h"
@@ -459,7 +461,7 @@ fatalfn(int exitcode)
exit(exitcode);
}
-#ifndef WIN32
+#ifndef _WIN32
#define CAN_CHECK_ERR
static void
check_error_logging(void (*fn)(void), int wantexitcode,
@@ -499,7 +501,7 @@ static void
sock_err_fn(void)
{
evutil_socket_t fd = socket(AF_INET, SOCK_STREAM, 0);
-#ifdef WIN32
+#ifdef _WIN32
EVUTIL_SET_SOCKET_ERROR(WSAEWOULDBLOCK);
#else
errno = EAGAIN;
@@ -571,7 +573,7 @@ test_evutil_log(void *ptr)
/* Try with a socket errno. */
fd = socket(AF_INET, SOCK_STREAM, 0);
-#ifdef WIN32
+#ifdef _WIN32
evutil_snprintf(buf, sizeof(buf),
"Unhappy socket: %s",
evutil_socket_error_to_string(WSAEWOULDBLOCK));
@@ -1055,7 +1057,7 @@ end:
evutil_freeaddrinfo(ai);
}
-#ifdef WIN32
+#ifdef _WIN32
static void
test_evutil_loadsyslib(void *arg)
{
@@ -1071,6 +1073,136 @@ end:
}
#endif
+/** Test mm_malloc(). */
+static void
+test_event_malloc(void *arg)
+{
+ void *p = NULL;
+ (void)arg;
+
+ /* mm_malloc(0) should simply return NULL. */
+ errno = 0;
+ p = mm_malloc(0);
+ tt_assert(p == NULL);
+ tt_int_op(errno, ==, 0);
+
+ /* Trivial case. */
+ errno = 0;
+ p = mm_malloc(8);
+ tt_assert(p != NULL);
+ tt_int_op(errno, ==, 0);
+ mm_free(p);
+
+ end:
+ errno = 0;
+ return;
+}
+
+static void
+test_event_calloc(void *arg)
+{
+ void *p = NULL;
+ (void)arg;
+
+ /* mm_calloc() should simply return NULL
+ * if either argument is zero. */
+ errno = 0;
+ p = mm_calloc(0, 0);
+ tt_assert(p == NULL);
+ tt_int_op(errno, ==, 0);
+ errno = 0;
+ p = mm_calloc(0, 1);
+ tt_assert(p == NULL);
+ tt_int_op(errno, ==, 0);
+ errno = 0;
+ p = mm_calloc(1, 0);
+ tt_assert(p == NULL);
+ tt_int_op(errno, ==, 0);
+
+ /* Trivial case. */
+ errno = 0;
+ p = mm_calloc(8, 8);
+ tt_assert(p != NULL);
+ tt_int_op(errno, ==, 0);
+ mm_free(p);
+
+ /* mm_calloc() should set errno = ENOMEM and return NULL
+ * in case of potential overflow. */
+ errno = 0;
+ p = mm_calloc(EV_SIZE_MAX/2, EV_SIZE_MAX/2 + 8);
+ tt_assert(p == NULL);
+ tt_int_op(errno, ==, ENOMEM);
+
+ end:
+ errno = 0;
+ return;
+}
+
+static void
+test_event_strdup(void *arg)
+{
+ void *p = NULL;
+ (void)arg;
+
+ /* mm_strdup(NULL) should set errno = EINVAL and return NULL. */
+ errno = 0;
+ p = mm_strdup(NULL);
+ tt_assert(p == NULL);
+ tt_int_op(errno, ==, EINVAL);
+
+ /* Trivial cases. */
+
+ errno = 0;
+ p = mm_strdup("");
+ tt_assert(p != NULL);
+ tt_int_op(errno, ==, 0);
+ tt_str_op(p, ==, "");
+ mm_free(p);
+
+ errno = 0;
+ p = mm_strdup("foo");
+ tt_assert(p != NULL);
+ tt_int_op(errno, ==, 0);
+ tt_str_op(p, ==, "foo");
+ mm_free(p);
+
+ /* XXX
+ * mm_strdup(str) where str is a string of length EV_SIZE_MAX
+ * should set errno = ENOMEM and return NULL. */
+
+ end:
+ errno = 0;
+ return;
+}
+
+static void
+test_evutil_usleep(void *arg)
+{
+ struct timeval tv1, tv2, tv3, diff1, diff2;
+ const struct timeval quarter_sec = {0, 250*1000};
+ const struct timeval tenth_sec = {0, 100*1000};
+ long usec1, usec2;
+
+ evutil_gettimeofday(&tv1, NULL);
+ evutil_usleep(&quarter_sec);
+ evutil_gettimeofday(&tv2, NULL);
+ evutil_usleep(&tenth_sec);
+ evutil_gettimeofday(&tv3, NULL);
+
+ evutil_timersub(&tv2, &tv1, &diff1);
+ evutil_timersub(&tv3, &tv2, &diff2);
+ usec1 = diff1.tv_sec * 1000000 + diff1.tv_usec;
+ usec2 = diff2.tv_sec * 1000000 + diff2.tv_usec;
+
+ tt_int_op(usec1, >, 200000);
+ tt_int_op(usec1, <, 300000);
+ tt_int_op(usec2, >, 80000);
+ tt_int_op(usec2, <, 120000);
+
+end:
+ ;
+}
+
struct testcase_t util_testcases[] = {
{ "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL },
{ "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL },
@@ -1086,9 +1218,13 @@ struct testcase_t util_testcases[] = {
{ "integers", test_evutil_integers, 0, NULL, NULL },
{ "rand", test_evutil_rand, TT_FORK, NULL, NULL },
{ "getaddrinfo", test_evutil_getaddrinfo, TT_FORK, NULL, NULL },
-#ifdef WIN32
+#ifdef _WIN32
{ "loadsyslib", test_evutil_loadsyslib, TT_FORK, NULL, NULL },
#endif
+ { "mm_malloc", test_event_malloc, 0, NULL, NULL },
+ { "mm_calloc", test_event_calloc, 0, NULL, NULL },
+ { "mm_strdup", test_event_strdup, 0, NULL, NULL },
+ { "usleep", test_evutil_usleep, 0, NULL, NULL },
END_OF_TESTCASES,
};
diff --git a/test/regress_zlib.c b/test/regress_zlib.c
index 887a6853..f6fa8517 100644
--- a/test/regress_zlib.c
+++ b/test/regress_zlib.c
@@ -27,7 +27,7 @@
/* The old tests here need assertions to work. */
#undef NDEBUG
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#endif
@@ -35,7 +35,7 @@
#include "event2/event-config.h"
#include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
diff --git a/test/test-changelist.c b/test/test-changelist.c
index fae099c7..658057de 100644
--- a/test/test-changelist.c
+++ b/test/test-changelist.c
@@ -26,7 +26,7 @@
#include "event2/event-config.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#else
@@ -52,7 +52,7 @@
#include <time.h>
struct cpu_usage_timer {
-#ifdef WIN32
+#ifdef _WIN32
HANDLE thread;
FILETIME usertimeBegin;
FILETIME kerneltimeBegin;
@@ -64,7 +64,7 @@ struct cpu_usage_timer {
static void
start_cpu_usage_timer(struct cpu_usage_timer *timer)
{
-#ifdef WIN32
+#ifdef _WIN32
int r;
FILETIME createtime, exittime;
timer->thread = GetCurrentThread();
@@ -77,7 +77,7 @@ start_cpu_usage_timer(struct cpu_usage_timer *timer)
evutil_gettimeofday(&timer->timeBegin, NULL);
}
-#ifdef WIN32
+#ifdef _WIN32
static ev_int64_t
filetime_to_100nsec(const FILETIME *ft)
{
@@ -104,7 +104,7 @@ static void
get_cpu_usage(struct cpu_usage_timer *timer, double *secElapsedOut,
double *secUsedOut, double *usageOut)
{
-#ifdef WIN32
+#ifdef _WIN32
double usertime_seconds, kerneltime_seconds;
FILETIME createtime, exittime, usertimeEnd, kerneltimeEnd;
int r;
@@ -114,7 +114,7 @@ get_cpu_usage(struct cpu_usage_timer *timer, double *secElapsedOut,
struct timeval timeEnd, timeDiff;
double secondsPassed, secondsUsed;
-#ifdef WIN32
+#ifdef _WIN32
r = GetThreadTimes(timer->thread, &createtime, &exittime,
&usertimeEnd, &kerneltimeEnd);
if (r==0) printf("GetThreadTimes failed.");
@@ -171,7 +171,7 @@ main(int argc, char **argv)
double usage, secPassed, secUsed;
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
@@ -191,8 +191,8 @@ main(int argc, char **argv)
/* and watch for writability on one end of the pipe */
ev = event_new(base,pair[1],EV_WRITE | EV_PERSIST, write_cb, &ev);
- tv.tv_sec = 5;
- tv.tv_usec = 0;
+ tv.tv_sec = 1;
+ tv.tv_usec = 500*1000;
evtimer_add(timeout, &tv);
diff --git a/test/test-eof.c b/test/test-eof.c
index 3f470ce2..e3cb4603 100644
--- a/test/test-eof.c
+++ b/test/test-eof.c
@@ -26,7 +26,7 @@
*/
#include "event2/event-config.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#else
#include <unistd.h>
@@ -92,7 +92,7 @@ main(int argc, char **argv)
const char *test = "test string";
evutil_socket_t pair[2];
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
diff --git a/test/test-init.c b/test/test-init.c
index 2dfa865c..1a26b4e1 100644
--- a/test/test-init.c
+++ b/test/test-init.c
@@ -38,7 +38,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <unistd.h>
#endif
#include <errno.h>
@@ -48,7 +48,7 @@
int
main(int argc, char **argv)
{
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
diff --git a/test/test-ratelim.c b/test/test-ratelim.c
index eb0b1654..1aabc458 100644
--- a/test/test-ratelim.c
+++ b/test/test-ratelim.c
@@ -23,6 +23,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "../util-internal.h"
#include <stdio.h>
#include <stdlib.h>
@@ -30,7 +31,7 @@
#include <assert.h>
#include <math.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
@@ -49,7 +50,6 @@
#include "event2/listener.h"
#include "event2/thread.h"
-#include "../util-internal.h"
static int cfg_verbose = 0;
static int cfg_help = 0;
@@ -89,7 +89,7 @@ loud_writecb(struct bufferevent *bev, void *ctx)
struct client_state *cs = ctx;
struct evbuffer *output = bufferevent_get_output(bev);
char buf[1024];
-#ifdef WIN32
+#ifdef _WIN32
int r = rand() % 256;
#else
int r = random() % 256;
@@ -421,7 +421,7 @@ main(int argc, char **argv)
int i,j;
double ratio;
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData;
int err;
@@ -429,7 +429,7 @@ main(int argc, char **argv)
err = WSAStartup(wVersionRequested, &wsaData);
#endif
-#ifndef WIN32
+#ifndef _WIN32
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return 1;
#endif
@@ -463,7 +463,7 @@ main(int argc, char **argv)
{
struct timeval tv;
evutil_gettimeofday(&tv, NULL);
-#ifdef WIN32
+#ifdef _WIN32
srand(tv.tv_usec);
#else
srandom(tv.tv_usec);
diff --git a/test/test-time.c b/test/test-time.c
index 8f0bd628..8b763c16 100644
--- a/test/test-time.c
+++ b/test/test-time.c
@@ -32,7 +32,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
@@ -51,7 +51,7 @@ struct event *ev[NEVENT];
static int
rand_int(int n)
{
-#ifdef WIN32
+#ifdef _WIN32
return (int)(rand() % n);
#else
return (int)(random() % n);
@@ -84,7 +84,7 @@ main(int argc, char **argv)
{
struct timeval tv;
int i;
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
diff --git a/test/test-weof.c b/test/test-weof.c
index dfdd3f04..06d67d16 100644
--- a/test/test-weof.c
+++ b/test/test-weof.c
@@ -26,7 +26,7 @@
*/
#include "event2/event-config.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#else
#include <unistd.h>
@@ -85,7 +85,7 @@ main(int argc, char **argv)
{
struct event ev;
-#ifdef WIN32
+#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
int err;
@@ -95,7 +95,7 @@ main(int argc, char **argv)
err = WSAStartup(wVersionRequested, &wsaData);
#endif
-#ifndef WIN32
+#ifndef _WIN32
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return (1);
#endif
diff --git a/test/test.sh b/test/test.sh
index 08a4cddb..58ab5531 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -114,49 +114,27 @@ run_tests () {
fi
}
+do_test() {
+ setup
+ announce "$1 $2"
+ unset EVENT_NO$1
+ if test "$2" = "(changelist)" ; then
+ EVENT_EPOLL_USE_CHANGELIST=yes; export EVENT_EPOLL_USE_CHANGELIST
+ fi
+ run_tests
+}
+
announce "Running tests:"
# Need to do this by hand?
-setup
-unset EVENT_NOEVPORT
-announce "EVPORT"
-run_tests
-
-setup
-unset EVENT_NOKQUEUE
-announce "KQUEUE"
-run_tests
-
-setup
-unset EVENT_NOEPOLL
-announce "EPOLL"
-run_tests
-
-setup
-unset EVENT_NOEPOLL
-EVENT_EPOLL_USE_CHANGELIST=yes; export EVENT_EPOLL_USE_CHANGELIST
-announce "EPOLL (changelist)"
-run_tests
-
-setup
-unset EVENT_NODEVPOLL
-announce "DEVPOLL"
-run_tests
-
-setup
-unset EVENT_NOPOLL
-announce "POLL"
-run_tests
-
-setup
-unset EVENT_NOSELECT
-announce "SELECT"
-run_tests
-
-setup
-unset EVENT_NOWIN32
-announce "WIN32"
-run_tests
+do_test EVPORT
+do_test KQUEUE
+do_test EPOLL
+do_test EPOLL "(changelist)"
+do_test DEVPOLL
+do_test POLL
+do_test SELECT
+do_test WIN32
if test "$FAILED" = "yes"; then
exit 1
diff --git a/test/tinytest.c b/test/tinytest.c
index 91cd8cb1..27fa94c2 100644
--- a/test/tinytest.c
+++ b/test/tinytest.c
@@ -22,17 +22,16 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef TINYTEST_LOCAL
+#include "tinytest_local.h"
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
-#ifdef TINYTEST_LOCAL
-#include "tinytest_local.h"
-#endif
-
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
@@ -65,7 +64,7 @@ const char *cur_test_prefix = NULL; /**< prefix of the current test group */
/** Name of the current test, if we haven't logged is yet. Used for --quiet */
const char *cur_test_name = NULL;
-#ifdef WIN32
+#ifdef _WIN32
/* Copy of argv[0] for win32. */
static char commandname[MAX_PATH+1];
#endif
@@ -104,7 +103,7 @@ static enum outcome
_testcase_run_forked(const struct testgroup_t *group,
const struct testcase_t *testcase)
{
-#ifdef WIN32
+#ifdef _WIN32
/* Fork? On Win32? How primitive! We'll do what the smart kids do:
we'll invoke our own exe (whose name we recall from the command
line) with a command line that tells it to run just the test we
@@ -290,7 +289,7 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups)
{
int i, j, n=0;
-#ifdef WIN32
+#ifdef _WIN32
const char *sp = strrchr(v[0], '.');
const char *extension = "";
if (!sp || stricmp(sp, ".exe"))
diff --git a/test/tinytest_local.h b/test/tinytest_local.h
index 1a7f75ef..87ec2fa6 100644
--- a/test/tinytest_local.h
+++ b/test/tinytest_local.h
@@ -1,10 +1,10 @@
-#ifdef WIN32
+#include "util-internal.h"
+#ifdef _WIN32
#include <winsock2.h>
#endif
#include "event2/util.h"
-#include "util-internal.h"
#ifdef snprintf
#undef snprintf
diff --git a/util-internal.h b/util-internal.h
index d51bc000..52f61f2c 100644
--- a/util-internal.h
+++ b/util-internal.h
@@ -27,6 +27,8 @@
#define _EVENT_UTIL_INTERNAL_H
#include "event2/event-config.h"
+#include "evconfig-private.h"
+
#include <errno.h>
/* For EVUTIL_ASSERT */
@@ -71,7 +73,7 @@ extern "C" {
data," and we need to look at the error code. Second, Windows defines
a different set of error codes for sockets. */
-#ifndef WIN32
+#ifndef _WIN32
/* True iff e is an error that means a read/write operation can be retried. */
#define EVUTIL_ERR_RW_RETRIABLE(e) \
@@ -266,7 +268,9 @@ long evutil_tv_to_msec(const struct timeval *tv);
int evutil_hex_char_to_int(char c);
-#ifdef WIN32
+void evutil_usleep(const struct timeval *tv);
+
+#ifdef _WIN32
HANDLE evutil_load_windows_system_library(const TCHAR *library_name);
#endif
diff --git a/win32select.c b/win32select.c
index 46c32c34..675ce00a 100644
--- a/win32select.c
+++ b/win32select.c
@@ -25,6 +25,8 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "event2/event-config.h"
+#include "evconfig-private.h"
#include <winsock2.h>
#include <windows.h>
@@ -38,7 +40,6 @@
#include <errno.h>
#include "event2/util.h"
-#include "event2/event-config.h"
#include "util-internal.h"
#include "log-internal.h"
#include "event2/event.h"