diff options
107 files changed, 2906 insertions, 1191 deletions
@@ -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 a151d943..8f0907f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,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: @@ -32,7 +32,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:3:1 +VERSION_INFO = 1:0:0 # History: RELEASE VERSION_INFO # 2.0.1-alpha -- 2.0 1:0:0 @@ -49,7 +49,6 @@ VERSION_INFO = 6:3:1 # 2.0.12-stable-- 2.0 6:1:1 (No ABI change) # 2.0.13-stable-- 2.0 6:2:1 (No ABI change) # 2.0.14-stable-- 2.0 6:3:1 (No ABI change) -# 2.0.15-stable-- 2.0 7:0:2 (ABI changed, backward-compatible) # # For Libevent 2.1: # 2.1.1-alpha -- 2.1 1:0:0 @@ -155,7 +154,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-' >> $@ @@ -170,6 +169,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" >> $@ @@ -211,6 +211,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 \ @@ -220,7 +221,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 55b82e4d..93b590a9 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 0x02000f01 +#define _EVENT_NUMERIC_VERSION 0x02010001 /* Name of package */ #define _EVENT_PACKAGE "libevent" @@ -334,7 +334,7 @@ #define _EVENT_TIME_WITH_SYS_TIME 1 /* Version number of package */ -#define _EVENT_VERSION "2.0.15-stable-dev" +#define _EVENT_VERSION "2.1.0-alpha-dev" /* Define to appropriate substitue if compiler doesnt have __func__ */ #define _EVENT___func__ __FUNCTION__ diff --git a/arc4random.c b/arc4random.c index 4833169a..254c12b6 100644 --- a/arc4random.c +++ b/arc4random.c @@ -49,7 +49,8 @@ #endif #ifndef ARC4RANDOM_NO_INCLUDES -#ifdef WIN32 +#include "evconfig-private.h" +#ifdef _WIN32 #include <wincrypt.h> #include <process.h> #else @@ -78,7 +79,7 @@ struct arc4_stream { unsigned char s[256]; }; -#ifdef WIN32 +#ifdef _WIN32 #define getpid _getpid #define pid_t int #endif @@ -119,7 +120,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) { @@ -139,7 +140,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) @@ -289,7 +290,7 @@ arc4_seed_proc_sys_kernel_random_uuid(void) } #endif -#ifndef WIN32 +#ifndef _WIN32 #define TRY_SEED_URANDOM static int arc4_seed_urandom(void) @@ -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); +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); -#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 struct evbuffer_chain * evbuffer_chain_new(size_t size) @@ -187,40 +188,29 @@ evbuffer_chain_free(struct evbuffer_chain *chain) 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); - } + + 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 } mm_free(chain); @@ -976,10 +966,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; @@ -1341,7 +1329,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; @@ -1372,13 +1360,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 @@ -1390,6 +1381,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) { @@ -1416,24 +1415,27 @@ 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; @@ -1447,9 +1449,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; @@ -1918,7 +1919,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 @@ -2006,7 +2007,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; @@ -2072,7 +2073,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; @@ -2103,7 +2104,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); @@ -2148,56 +2149,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, @@ -2232,7 +2183,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)) @@ -2249,37 +2200,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); @@ -2288,7 +2241,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; @@ -2326,7 +2279,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); @@ -2374,12 +2327,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); @@ -2406,14 +2383,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; } /** @@ -2518,8 +2499,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; @@ -2534,6 +2514,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) { @@ -2684,162 +2668,314 @@ 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; -#endif -#if defined(USE_SENDFILE) - int sendfile_okay = 1; + 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 - int ok = 1; + if (length == -1) { + struct stat st; + if (fstat(fd, &st) < 0) + goto err; + length = st.st_size; + } + seg->length = length; #if defined(USE_SENDFILE) - if (use_sendfile) { - EVBUFFER_LOCK(outbuf); - sendfile_okay = outbuf->flags & EVBUFFER_FLAG_DRAINS_TO_FD; - EVBUFFER_UNLOCK(outbuf); + if (!(flags & EVBUF_FS_DISABLE_SENDFILE)) { + seg->can_sendfile = 1; + goto done; } +#endif - 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); - } + if (evbuffer_file_segment_materialize(seg)<0) + goto err; - chain->flags |= EVBUFFER_SENDFILE | EVBUFFER_IMMUTABLE; - chain->buffer = NULL; /* no reading possible */ - chain->buffer_len = length + offset; - chain->off = length; - chain->misalign = offset; +done: + if (!(flags & EVBUF_FS_DISABLE_LOCKING)) { + EVTHREAD_ALLOC_LOCK(seg->lock, 0); + } + return seg; +err: + mm_free(seg); + return NULL; +} + +/* 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) + return NULL; + 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) @@ -2975,50 +3111,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 82c44ac0..764f6b38 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" diff --git a/bufferevent-internal.h b/bufferevent-internal.h index 1b7674d3..0ad997a6 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" @@ -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 13219f0b..8cd32dcc 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 af5e57f5..b8f73496 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 d81dc759..7b54147f 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 86a8619b..5dadbe88 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 diff --git a/bufferevent_pair.c b/bufferevent_pair.c index 35d6b5f0..271e305d 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 98790ee1..152e4ff7 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) @@ -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 6d27d7f8..5ac2afc3 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 8657d864..a997de6f 100644 --- a/configure.in +++ b/configure.in @@ -1,37 +1,48 @@ dnl configure.in for libevent dnl 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.15-stable-dev) -AM_CONFIG_HEADER(config.h) -AC_DEFINE(NUMERIC_VERSION, 0x02000f01, [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 @@ -53,7 +64,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]) @@ -75,6 +95,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 @@ -94,12 +117,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 ], @@ -149,22 +172,9 @@ 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 @@ -534,6 +544,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 @@ -550,7 +561,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 @@ -573,7 +584,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 @@ -646,12 +657,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) @@ -678,7 +694,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 79a2baff..a413327b 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; @@ -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> @@ -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 466b1297..f6471406 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 78a293a4..6c3838e1 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> @@ -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 @@ -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 @@ -194,21 +195,54 @@ 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; +}; + #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 @@ -34,8 +34,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 @@ -59,7 +61,7 @@ #include <sys/stat.h> #include <stdio.h> #include <stdarg.h> -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #ifndef _WIN32_IE @@ -87,7 +89,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> @@ -3475,7 +3477,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 @@ -3557,7 +3559,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) { @@ -3837,7 +3839,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 a361cf3b..d8de5761 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; }; @@ -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 */ @@ -639,7 +685,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 @@ -650,7 +696,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); @@ -667,7 +713,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) @@ -700,7 +746,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 @@ -813,22 +859,18 @@ event_reinit(struct event_base *base) 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); + event_queue_remove_inserted(base, &base->sig.ev_signal); if (base->sig.ev_signal.ev_flags & EVLIST_ACTIVE) - event_queue_remove(base, &base->sig.ev_signal, - EVLIST_ACTIVE); + event_queue_remove_active(base, &base->sig.ev_signal); 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); + event_queue_remove_inserted(base, &base->th_notify); if (base->th_notify.ev_flags & EVLIST_ACTIVE) - event_queue_remove(base, &base->th_notify, - EVLIST_ACTIVE); + event_queue_remove_active(base, &base->th_notify); base->sig.ev_signal_added = 0; EVUTIL_CLOSESOCKET(base->th_notify_fd[0]); if (base->th_notify_fd[1] != -1) @@ -836,6 +878,7 @@ event_reinit(struct event_base *base) 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) @@ -911,6 +954,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); } @@ -981,6 +1027,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); @@ -989,14 +1052,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); @@ -1008,7 +1074,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; @@ -1016,7 +1082,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. */ @@ -1283,7 +1364,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; @@ -1292,7 +1374,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)) @@ -1336,6 +1418,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; } @@ -1347,12 +1438,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); @@ -1364,10 +1459,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; } @@ -1383,11 +1484,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) @@ -1398,7 +1516,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; } @@ -1559,7 +1678,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; @@ -1938,7 +2058,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); @@ -2032,7 +2152,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; @@ -2057,17 +2177,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. */ @@ -2083,7 +2192,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); @@ -2102,10 +2211,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); @@ -2197,14 +2307,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 @@ -2275,7 +2385,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); @@ -2362,7 +2472,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); @@ -2447,49 +2557,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); } } @@ -2525,44 +2677,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); } } @@ -2599,6 +2770,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 @@ -2608,31 +2782,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 * @@ -2687,7 +2881,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 @@ -2710,8 +2904,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 @@ -2737,7 +2933,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 @@ -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 de6b70b1..36cb087e 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 e7c9e831..d646ae3d 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 @@ -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> @@ -688,14 +689,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 +705,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; @@ -51,6 +51,7 @@ */ #include "event2/event-config.h" +#include "evconfig-private.h" #include <sys/time.h> #include <sys/queue.h> @@ -72,20 +73,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 @@ -93,7 +82,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) @@ -104,10 +98,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 *); @@ -115,6 +114,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", @@ -125,7 +125,7 @@ const struct eventop evportops = { evport_dealloc, 1, /* need reinit */ 0, /* features */ - 0, /* fdinfo length */ + sizeof(struct fd_info), /* fdinfo length */ }; /* @@ -136,7 +136,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); @@ -146,24 +145,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 @@ -175,9 +197,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); } /* @@ -193,7 +213,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 @@ -202,33 +221,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. */ @@ -239,7 +231,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); } @@ -260,12 +252,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; @@ -288,22 +280,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); @@ -323,13 +318,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 @@ -351,11 +348,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); @@ -372,27 +374,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); @@ -406,29 +391,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) && @@ -448,7 +416,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; @@ -464,7 +435,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); } @@ -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 60f02e00..2782bbe5 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. @@ -25,6 +25,7 @@ */ #include "event2/event-config.h" +#include "evconfig-private.h" #ifndef _EVENT_DISABLE_THREAD_SUPPORT @@ -137,7 +138,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 @@ -161,6 +165,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; @@ -173,18 +178,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); @@ -217,12 +225,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; } @@ -247,6 +258,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 ffbd95ec..a198bc19 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 1d383a32..ca25950d 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 @@ -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 @@ -79,7 +78,7 @@ #include "strlcpy-internal.h" #include "ipv6-internal.h" -#ifdef WIN32 +#ifdef _WIN32 #define open _open #define read _read #define close _close @@ -131,7 +130,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 +156,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 +174,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 +268,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 +295,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 +310,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 +327,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 +341,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 +357,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 +395,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) { @@ -700,7 +714,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 +1214,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 +1311,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 +1447,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 +1457,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 +2091,7 @@ evutil_getenv(const char *varname) long _evutil_weakrand(void) { -#ifdef WIN32 +#ifdef _WIN32 return rand(); #else return random(); @@ -2136,7 +2150,7 @@ evutil_hex_char_to_int(char c) return -1; } -#ifdef WIN32 +#ifdef _WIN32 HANDLE evutil_load_windows_system_library(const TCHAR *library_name) { diff --git a/evutil_rand.c b/evutil_rand.c index a93aab0c..399e161e 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 98e64efb..7fab6413 100644 --- a/http-internal.h +++ b/http-internal.h @@ -83,7 +83,7 @@ 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 */ @@ -152,7 +152,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 +165,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; }; @@ -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" @@ -496,12 +496,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); @@ -1374,6 +1374,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 +1395,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 +1433,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 +1522,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 +1536,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 +2154,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 +2172,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 +2180,25 @@ 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); 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,6 +2212,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) +{ + 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) @@ -2131,14 +2238,29 @@ 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 @@ -2190,8 +2312,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); @@ -3113,6 +3239,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) @@ -3183,7 +3319,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, @@ -3354,7 +3490,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 @@ -3438,6 +3591,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 */ @@ -3662,6 +3823,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) { @@ -3674,8 +3836,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) @@ -3743,8 +3908,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 d28cd2dc..a81e3e55 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; @@ -452,10 +463,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 +477,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 +695,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 diff --git a/include/event2/bufferevent.h b/include/event2/bufferevent.h index 0f1ea278..32a8fd77 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/dns.h b/include/event2/dns.h index b938707d..a84f12fe 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 1dcca7a4..ed9de423 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 6d05f341..01018fa8 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/http.h b/include/event2/http.h index cb21d466..5c9f0e55 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 @@ -468,10 +529,15 @@ 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 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 9b4b7676..9064fedd 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 06a5e85c..d1841da4 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 bfe91fae..94318707 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 882e7afb..bf2cfcc0 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 f8462556..c5b41a28 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 c5d1d522..955392e7 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 @@ -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 @@ -433,9 +426,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); @@ -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); @@ -430,7 +432,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 52590c26..a14cc3b9 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 @@ -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 e64f8555..e73b0552 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 b9168a1f..4e2abdb8 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) @@ -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 6a148148..61e72d60 100644 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -13,6 +13,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 c5f377d3..d8134e83 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 @@ -188,7 +188,7 @@ main(int c, char **v) { evdns_add_server_port_with_base(event_base, sock, 0, evdns_server_callback, NULL); } if (idx < c) { -#ifdef WIN32 +#ifdef _WIN32 evdns_base_config_windows_nameservers(evdns_base); #else 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 cd6b27af..5196fff1 100644 --- a/sample/http-server.c +++ b/sample/http-server.c @@ -13,7 +13,7 @@ #include <sys/types.h> #include <sys/stat.h> -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> @@ -47,7 +47,7 @@ /* 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; @@ -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 @@ -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__); @@ -368,7 +369,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 +389,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> @@ -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 647f9065..66ea1acd 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 36c226e6..ab5ced7d 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 8e60a219..efcd3e6b 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 1b73e309..ce5c16a3 100644 --- a/test/bench_httpclient.c +++ b/test/bench_httpclient.c @@ -26,7 +26,7 @@ */ #include <sys/types.h> -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #else #include <sys/socket.h> @@ -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 683303db..985c0938 100644 --- a/test/regress.c +++ b/test/regress.c @@ -25,7 +25,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #endif @@ -38,7 +38,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> @@ -68,7 +68,7 @@ #include "regress.h" -#ifndef WIN32 +#ifndef _WIN32 #include "regress.gen.h" #endif @@ -93,7 +93,7 @@ static struct timeval tcalled; #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 @@ -414,7 +414,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 @@ -782,7 +782,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_ @@ -2098,7 +2098,7 @@ end: } } -#ifndef WIN32 +#ifndef _WIN32 /* You can't do this test on windows, since dup2 doesn't work on sockets */ static void @@ -2255,7 +2255,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; @@ -2264,10 +2263,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 @@ -2352,7 +2347,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 }, @@ -2361,7 +2356,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 @@ -2377,7 +2372,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 23b3d313..d09e9426 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 e68b24fe..275bd3d0 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -25,7 +25,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #endif @@ -38,7 +38,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> @@ -58,6 +58,7 @@ #include "evbuffer-internal.h" #include "log-internal.h" +#include "util-internal.h" #include "regress.h" @@ -583,41 +584,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); @@ -630,39 +772,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 @@ -937,6 +1102,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); } @@ -1037,6 +1208,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; @@ -1069,6 +1249,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); @@ -1084,6 +1266,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); @@ -1127,6 +1321,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) @@ -1477,6 +1685,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); @@ -1612,13 +1827,30 @@ struct testcase_t evbuffer_testcases[] = { { "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" }, + +#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 e71288e8..26273f6c 100644 --- a/test/regress_bufferevent.c +++ b/test/regress_bufferevent.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 <sys/wait.h> #include <signal.h> @@ -74,7 +74,7 @@ #include "event2/util.h" #include "bufferevent-internal.h" -#ifdef WIN32 +#ifdef _WIN32 #include "iocp-internal.h" #endif @@ -486,7 +486,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 *) diff --git a/test/regress_dns.c b/test/regress_dns.c index 512d58b5..e4cfd520 100644 --- a/test/regress_dns.c +++ b/test/regress_dns.c @@ -25,7 +25,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #include <ws2tcpip.h> @@ -39,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> diff --git a/test/regress_et.c b/test/regress_et.c index 7b2d94a8..e15a6d91 100644 --- a/test/regress_et.c +++ b/test/regress_et.c @@ -26,7 +26,7 @@ #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 @@ -75,7 +75,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 e9336275..81632b93 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -25,7 +25,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> @@ -39,7 +39,7 @@ #include <sys/time.h> #endif #include <sys/queue.h> -#ifndef WIN32 +#ifndef _WIN32 #include <sys/socket.h> #include <signal.h> #include <unistd.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 diff --git a/test/regress_listener.c b/test/regress_listener.c index ab4fad27..f32efa27 100644 --- a/test/regress_listener.c +++ b/test/regress_listener.c @@ -24,14 +24,14 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#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 @@ -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 d129d7c2..ce623112 100644 --- a/test/regress_main.c +++ b/test/regress_main.c @@ -25,7 +25,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #include <io.h> @@ -51,7 +51,7 @@ #include <sys/types.h> -#ifndef WIN32 +#ifndef _WIN32 #include <sys/socket.h> #include <sys/wait.h> #include <signal.h> @@ -106,13 +106,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) @@ -147,6 +149,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); @@ -165,7 +168,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 @@ -325,7 +328,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 @@ -346,7 +349,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 }, @@ -360,7 +363,7 @@ struct testgroup_t testgroups[] = { int main(int argc, const char **argv) { -#ifdef WIN32 +#ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; int err; @@ -370,12 +373,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_rpc.c b/test/regress_rpc.c index 1b7bc2e1..c698ed35 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> diff --git a/test/regress_ssl.c b/test/regress_ssl.c index 7f714106..186958d3 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 efc1ff5f..45e9eb80 100644 --- a/test/regress_testutils.c +++ b/test/regress_testutils.c @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #include <ws2tcpip.h> @@ -38,7 +38,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> diff --git a/test/regress_thread.c b/test/regress_thread.c index 8a3e6a9e..da43b2e7 100644 --- a/test/regress_thread.c +++ b/test/regress_thread.c @@ -42,7 +42,7 @@ #ifdef _EVENT_HAVE_PTHREADS #include <pthread.h> -#elif defined(WIN32) +#elif defined(_WIN32) #include <process.h> #endif #include <assert.h> @@ -156,7 +156,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 +199,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,7 +403,7 @@ end: #define CB_COUNT 128 #define QUEUE_THREAD_COUNT 8 -#ifdef WIN32 +#ifdef _WIN32 #define SLEEP_MS(ms) Sleep(ms) #else #define SLEEP_MS(ms) usleep((ms) * 1000) @@ -493,6 +493,75 @@ end: 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 == ×[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, ×[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(×[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, ×[0], 100); + test_timeval_diff_eq(&starttime, ×[1], 200); + test_timeval_diff_eq(&starttime, ×[2], 400); + test_timeval_diff_eq(&starttime, ×[3], 450); + test_timeval_diff_eq(&starttime, ×[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 +569,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 f679a8a6..585b63d5 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -23,7 +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. */ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #include <ws2tcpip.h> @@ -33,7 +33,7 @@ #include <sys/types.h> -#ifndef WIN32 +#ifndef _WIN32 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -56,6 +56,7 @@ #include "../util-internal.h" #include "../log-internal.h" #include "../strlcpy-internal.h" +#include "../mm-internal.h" #include "regress.h" @@ -459,7 +460,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 +500,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 +572,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 +1056,7 @@ end: evutil_freeaddrinfo(ai); } -#ifdef WIN32 +#ifdef _WIN32 static void test_evutil_loadsyslib(void *arg) { @@ -1071,6 +1072,108 @@ 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; +} + struct testcase_t util_testcases[] = { { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL }, { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL }, @@ -1086,9 +1189,12 @@ 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 }, END_OF_TESTCASES, }; diff --git a/test/regress_zlib.c b/test/regress_zlib.c index 7dde48f2..f3911e49 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 f368f1cb..1b2af0e8 100644 --- a/test/test-changelist.c +++ b/test/test-changelist.c @@ -4,7 +4,7 @@ #include "event2/event-config.h" -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <windows.h> #else @@ -30,7 +30,7 @@ #include <time.h> struct cpu_usage_timer { -#ifdef WIN32 +#ifdef _WIN32 HANDLE thread; FILETIME usertimeBegin; FILETIME kerneltimeBegin; @@ -42,7 +42,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(); @@ -55,7 +55,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) { @@ -82,7 +82,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; @@ -92,7 +92,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."); @@ -149,7 +149,7 @@ main(int argc, char **argv) double usage, secPassed, secUsed; -#ifdef WIN32 +#ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; int err; diff --git a/test/test-eof.c b/test/test-eof.c index 417476e8..b3cd7dfd 100644 --- a/test/test-eof.c +++ b/test/test-eof.c @@ -4,7 +4,7 @@ */ #include "event2/event-config.h" -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #else #include <unistd.h> @@ -70,7 +70,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 967a0131..7e1bf8c7 100644 --- a/test/test-init.c +++ b/test/test-init.c @@ -16,7 +16,7 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#ifndef WIN32 +#ifndef _WIN32 #include <unistd.h> #endif #include <errno.h> @@ -26,7 +26,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 a07678af..c5ba30e4 100644 --- a/test/test-ratelim.c +++ b/test/test-ratelim.c @@ -30,7 +30,7 @@ #include <assert.h> #include <math.h> -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #else @@ -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 5628696e..8e431487 100644 --- a/test/test-time.c +++ b/test/test-time.c @@ -10,7 +10,7 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#ifndef WIN32 +#ifndef _WIN32 #include <unistd.h> #include <sys/time.h> #endif @@ -29,7 +29,7 @@ struct event *ev[NEVENT]; static int rand_int(int n) { -#ifdef WIN32 +#ifdef _WIN32 return (int)(rand() % n); #else return (int)(random() % n); @@ -62,7 +62,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 b20f27e1..87a04a28 100644 --- a/test/test-weof.c +++ b/test/test-weof.c @@ -4,7 +4,7 @@ */ #include "event2/event-config.h" -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #else #include <unistd.h> @@ -63,7 +63,7 @@ main(int argc, char **argv) { struct event ev; -#ifdef WIN32 +#ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; int err; @@ -73,7 +73,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 16b12857..d3103efe 100644 --- a/test/tinytest.c +++ b/test/tinytest.c @@ -32,7 +32,7 @@ #include "tinytest_local.h" #endif -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #else #include <sys/types.h> @@ -65,7 +65,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 +104,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 +290,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..888b1159 100644 --- a/test/tinytest_local.h +++ b/test/tinytest_local.h @@ -1,5 +1,5 @@ -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #endif diff --git a/util-internal.h b/util-internal.h index 541d45fb..6278cecb 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,7 @@ long evutil_tv_to_msec(const struct timeval *tv); int evutil_hex_char_to_int(char c); -#ifdef WIN32 +#ifdef _WIN32 HANDLE evutil_load_windows_system_library(const TCHAR *library_name); #endif diff --git a/win32select.c b/win32select.c index 2e0910bd..d8ea7a00 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" |