From 1237387390f4be97f861a5cb93eec2445fe1a565 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 19 Jul 2013 15:51:45 +0000 Subject: adding experimental turbo mode --- ChangeLog | 3 +- src/examples/benchmark.c | 11 ++-- src/include/microhttpd.h | 8 ++- src/microhttpd/connection.c | 139 ++++++++++++++++++++++++++++------------- src/microhttpd/daemon.c | 148 +++++++++++++++++++++++++++++--------------- src/microhttpd/internal.h | 12 +++- 6 files changed, 221 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index 728a9f9a..58b754f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Fri Jul 19 09:57:27 CEST 2013 Fix issue where connections were not cleaned up when - 'MHD_run_from_select' was used. + 'MHD_run_from_select' was used. Adding experimental + TURBO mode. Releasing libmicrohttpd 0.9.28. -CG Sun Jul 14 19:57:56 CEST 2013 diff --git a/src/examples/benchmark.c b/src/examples/benchmark.c index 711d118a..48f294e0 100644 --- a/src/examples/benchmark.c +++ b/src/examples/benchmark.c @@ -122,10 +122,12 @@ main (int argc, char *const *argv) response = MHD_create_response_from_buffer (strlen (PAGE), (void *) PAGE, MHD_RESPMEM_PERSISTENT); - - d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY -#if ! EPOLL_SUPPORT - | MHD_USE_EPOLL_LINUX_ONLY + (void) MHD_add_response_header (response, + MHD_HTTP_HEADER_CONNECTION, + "close"); + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_SUPPRESS_DATE_NO_CLOCK +#if EPOLL_SUPPORT + | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO #endif , atoi (argv[1]), @@ -134,6 +136,7 @@ main (int argc, char *const *argv) MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS, MHD_OPTION_URI_LOG_CALLBACK, &uri_logger_cb, NULL, MHD_OPTION_NOTIFY_COMPLETED, &completed_callback, NULL, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 1000, MHD_OPTION_END); if (d == NULL) return 1; diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index f5397cc5..461c95cd 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h @@ -436,7 +436,13 @@ enum MHD_FLAG /** * Use a single socket for IPv4 and IPv6. */ - MHD_USE_DUAL_STACK = MHD_USE_IPv6 | 2048 + MHD_USE_DUAL_STACK = MHD_USE_IPv6 | 2048, + + /** + * Enable EPOLL turbo. Only useful with MHD_USE_EPOLL_LINUX_ONLY. + * Highly experimental, do not use in production yet. + */ + MHD_USE_EPOLL_TURBO = 4096 }; diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index a16bf62d..de67f800 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2007, 2008, 2009, 2010, 2011, 2012 Daniel Pittman and Christian Grothoff + (C) 2007-2013 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -266,8 +266,9 @@ MHD_connection_close (struct MHD_Connection *connection, struct MHD_Daemon *daemon; daemon = connection->daemon; - SHUTDOWN (connection->socket_fd, - (MHD_YES == connection->read_closed) ? SHUT_WR : SHUT_RDWR); + if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO)) + SHUTDOWN (connection->socket_fd, + (MHD_YES == connection->read_closed) ? SHUT_WR : SHUT_RDWR); connection->state = MHD_CONNECTION_CLOSED; connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; if ( (NULL != daemon->notify_completed) && @@ -1485,7 +1486,7 @@ do_read (struct MHD_Connection *connection) if (bytes_read < 0) { if ((EINTR == errno) || (EAGAIN == errno)) - return MHD_NO; + return MHD_NO; #if HAVE_MESSAGES #if HTTPS_SUPPORT if (0 != (connection->daemon->options & MHD_USE_SSL)) @@ -2032,6 +2033,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) int rend; char *line; + connection->in_idle = MHD_YES; while (1) { #if DEBUG_STATES @@ -2376,32 +2378,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) } continue; case MHD_CONNECTION_CLOSED: - if (connection->response != NULL) - { - MHD_destroy_response (connection->response); - connection->response = NULL; - } - if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && - (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) - MHD_PANIC ("Failed to acquire cleanup mutex\n"); - if (connection->connection_timeout == daemon->connection_timeout) - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - else - XDLL_remove (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - connection); - DLL_remove (daemon->connections_head, - daemon->connections_tail, - connection); - DLL_insert (daemon->cleanup_head, - daemon->cleanup_tail, - connection); - if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && - (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex)) ) - MHD_PANIC ("Failed to release cleanup mutex\n"); - return MHD_NO; + goto cleanup_connection; default: EXTRA_CHECK (0); break; @@ -2413,35 +2390,109 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) (timeout <= (MHD_monotonic_time() - connection->last_activity)) ) { MHD_connection_close (connection, MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); + connection->in_idle = MHD_NO; return MHD_YES; } MHD_connection_update_event_loop_info (connection); switch (connection->event_loop_info) { case MHD_EVENT_LOOP_INFO_READ: - if (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - connection); + if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) && + (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) ) + { + EDLL_insert (daemon->eready_head, + daemon->eready_tail, + connection); + connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; + } break; case MHD_EVENT_LOOP_INFO_WRITE: - if (0 != (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - connection); + if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) && + (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) ) + { + EDLL_insert (daemon->eready_head, + daemon->eready_tail, + connection); + connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; + } break; case MHD_EVENT_LOOP_INFO_BLOCK: /* we should look at this connection again in the next iteration of the event loop, as we're waiting on the application */ - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - connection); + if (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) + { + EDLL_insert (daemon->eready_head, + daemon->eready_tail, + connection); + connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; + } break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* This connection is finished, nothing left to do */ break; } + +#if EPOLL_SUPPORT + if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) && + (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) && + ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) || + ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) && + (MHD_EVENT_LOOP_INFO_READ == connection->event_loop_info) && + (MHD_NO == connection->read_closed) ) ) ) + { + /* add to epoll set */ + struct epoll_event event; + + event.events = EPOLLIN | EPOLLOUT | EPOLLET; + event.data.ptr = connection; + if (0 != epoll_ctl (daemon->epoll_fd, + EPOLL_CTL_ADD, + connection->socket_fd, + &event)) + { +#if HAVE_MESSAGES + if (0 != (daemon->options & MHD_USE_DEBUG)) + MHD_DLOG (daemon, + "Call to epoll_ctl failed: %s\n", + STRERROR (errno)); +#endif + connection->state = MHD_CONNECTION_CLOSED; + goto cleanup_connection; + } + connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; + } +#endif + connection->in_idle = MHD_NO; return MHD_YES; + + cleanup_connection: + if (NULL != connection->response) + { + MHD_destroy_response (connection->response); + connection->response = NULL; + } + if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && + (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) ) + MHD_PANIC ("Failed to acquire cleanup mutex\n"); + if (connection->connection_timeout == daemon->connection_timeout) + XDLL_remove (daemon->normal_timeout_head, + daemon->normal_timeout_tail, + connection); + else + XDLL_remove (daemon->manual_timeout_head, + daemon->manual_timeout_tail, + connection); + DLL_remove (daemon->connections_head, + daemon->connections_tail, + connection); + DLL_insert (daemon->cleanup_head, + daemon->cleanup_tail, + connection); + if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && + (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex)) ) + MHD_PANIC ("Failed to release cleanup mutex\n"); + connection->in_idle = MHD_NO; + return MHD_NO; } @@ -2589,11 +2640,13 @@ MHD_queue_response (struct MHD_Connection *connection, /* response was queued "early", refuse to read body / footers or further requests! */ - (void) SHUTDOWN (connection->socket_fd, SHUT_RD); + if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO)) + (void) SHUTDOWN (connection->socket_fd, SHUT_RD); connection->read_closed = MHD_YES; connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; } - (void) MHD_connection_handle_idle (connection); + if (MHD_NO == connection->in_idle) + (void) MHD_connection_handle_idle (connection); return MHD_YES; } diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index 1625078d..d111d9cd 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c @@ -809,7 +809,7 @@ recv_param_adapter (struct MHD_Connection *connection, } ret = RECV (connection->socket_fd, other, i, MSG_NOSIGNAL); #if EPOLL_SUPPORT - if (ret < i) + if (ret < (ssize_t) i) { /* partial read --- no longer read-ready */ connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; @@ -884,7 +884,7 @@ send_param_adapter (struct MHD_Connection *connection, #endif ret = SEND (connection->socket_fd, other, i, MSG_NOSIGNAL); #if EPOLL_SUPPORT - if (ret < i) + if (ret < (ssize_t) i) { /* partial write --- no longer write-ready */ connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; @@ -1121,37 +1121,43 @@ MHD_add_connection (struct MHD_Daemon *daemon, MHD_set_http_callbacks_ (connection); connection->recv_cls = &recv_param_adapter; connection->send_cls = &send_param_adapter; - /* non-blocking sockets are required on most systems and for GNUtls; - however, they somehow cause serious problems on CYGWIN (#1824) */ + + if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO)) + { + /* non-blocking sockets are required on most systems and for GNUtls; + however, they somehow cause serious problems on CYGWIN (#1824); + in turbo mode, we assume that non-blocking was already set + by 'accept4' or whoever calls 'MHD_add_connection' */ #ifdef CYGWIN - if (0 != (daemon->options & MHD_USE_SSL)) + if (0 != (daemon->options & MHD_USE_SSL)) #endif - { - /* make socket non-blocking */ + { + /* make socket non-blocking */ #ifndef MINGW - int flags = fcntl (connection->socket_fd, F_GETFL); - if ( (-1 == flags) || - (0 != fcntl (connection->socket_fd, F_SETFL, flags | O_NONBLOCK)) ) - { + int flags = fcntl (connection->socket_fd, F_GETFL); + if ( (-1 == flags) || + (0 != fcntl (connection->socket_fd, F_SETFL, flags | O_NONBLOCK)) ) + { #if HAVE_MESSAGES - MHD_DLOG (daemon, - "Failed to make socket %d non-blocking: %s\n", - connection->socket_fd, - STRERROR (errno)); + MHD_DLOG (daemon, + "Failed to make socket %d non-blocking: %s\n", + connection->socket_fd, + STRERROR (errno)); #endif - } + } #else - unsigned long flags = 1; - if (0 != ioctlsocket (connection->socket_fd, FIONBIO, &flags)) - { + unsigned long flags = 1; + if (0 != ioctlsocket (connection->socket_fd, FIONBIO, &flags)) + { #if HAVE_MESSAGES - MHD_DLOG (daemon, - "Failed to make socket non-blocking: %s\n", - STRERROR (errno)); + MHD_DLOG (daemon, + "Failed to make socket non-blocking: %s\n", + STRERROR (errno)); #endif - } + } #endif - } + } + } #if HTTPS_SUPPORT if (0 != (daemon->options & MHD_USE_SSL)) @@ -1228,25 +1234,35 @@ MHD_add_connection (struct MHD_Daemon *daemon, #if EPOLL_SUPPORT if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) { - struct epoll_event event; - - event.events = EPOLLIN | EPOLLOUT | EPOLLET; - event.data.ptr = connection; - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - client_socket, - &event)) + if (0 == (daemon->options & MHD_USE_EPOLL_TURBO)) { + struct epoll_event event; + + event.events = EPOLLIN | EPOLLOUT | EPOLLET; + event.data.ptr = connection; + if (0 != epoll_ctl (daemon->epoll_fd, + EPOLL_CTL_ADD, + client_socket, + &event)) + { #if HAVE_MESSAGES - if (0 != (daemon->options & MHD_USE_DEBUG)) - MHD_DLOG (daemon, - "Call to epoll_ctl failed: %s\n", - STRERROR (errno)); + if (0 != (daemon->options & MHD_USE_DEBUG)) + MHD_DLOG (daemon, + "Call to epoll_ctl failed: %s\n", + STRERROR (errno)); #endif - goto cleanup; + goto cleanup; + } + connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; + } + else + { + connection->epoll_state |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY + | MHD_EPOLL_STATE_IN_EREADY_EDLL; + EDLL_insert (daemon->eready_head, + daemon->eready_tail, + connection); } - daemon->listen_socket_in_epoll = MHD_YES; - } #endif daemon->max_connections--; @@ -1300,13 +1316,19 @@ MHD_accept_connection (struct MHD_Daemon *daemon) socklen_t addrlen; int s; int fd; + int nonblock; addrlen = sizeof (addrstorage); memset (addr, 0, sizeof (addrstorage)); if (-1 == (fd = daemon->socket_fd)) return MHD_NO; + nonblock = SOCK_NONBLOCK; +#ifdef CYGWIN + if (0 == (daemon->options & MHD_USE_SSL)) + nonblock = 0; +#endif #if HAVE_ACCEPT4 - s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC); + s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC | nonblock); #else s = ACCEPT (fd, addr, &addrlen); #endif @@ -1329,9 +1351,19 @@ MHD_accept_connection (struct MHD_Daemon *daemon) } #if !HAVE_ACCEPT4 { - /* make socket non-inheritable */ + /* make socket non-inheritable and non-blocking */ #ifdef WINDOWS DWORD dwFlags; + unsigned long flags = 1; + + if (0 != ioctlsocket (s, FIONBIO, &flags)) + { +#if HAVE_MESSAGES + MHD_DLOG (daemon, + "Failed to make socket non-blocking: %s\n", + STRERROR (errno)); +#endif + } if (!GetHandleInformation ((HANDLE) s, &dwFlags) || ((dwFlags != dwFlags & ~HANDLE_FLAG_INHERIT) && !SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0))) @@ -1342,14 +1374,19 @@ MHD_accept_connection (struct MHD_Daemon *daemon) "Failed to make socket non-inheritable: %s\n", STRERROR (errno)); #endif - } + } #else int flags; + nonblock = O_NONBLOCK; +#ifdef CYGWIN + if (0 == (daemon->options & MHD_USE_SSL)) + nonblock = 0; +#endif flags = fcntl (s, F_GETFD); if ( ( (-1 == flags) || ( (flags != (flags | FD_CLOEXEC)) && - (0 != fcntl (s, F_SETFD, flags | FD_CLOEXEC)) ) ) ) + (0 != fcntl (s, F_SETFD, flags | nonblock | FD_CLOEXEC)) ) ) ) { #if HAVE_MESSAGES MHD_DLOG (daemon, @@ -1358,7 +1395,10 @@ MHD_accept_connection (struct MHD_Daemon *daemon) #endif } #endif + /* make socket non-blocking */ + } + #endif #if HAVE_MESSAGES #if DEBUG_CONNECT @@ -1406,10 +1446,13 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) if (pos->tls_session != NULL) gnutls_deinit (pos->tls_session); #endif - MHD_ip_limit_del (daemon, (struct sockaddr*)pos->addr, pos->addr_len); + MHD_ip_limit_del (daemon, + (struct sockaddr *) pos->addr, + pos->addr_len); #if EPOLL_SUPPORT if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) && - (-1 != daemon->epoll_fd) ) + (-1 != daemon->epoll_fd) && + (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) ) { /* epoll documentation suggests that closing a FD automatically removes it from the epoll set; however, @@ -1422,6 +1465,7 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) pos->socket_fd, NULL)) MHD_PANIC ("Failed to remove FD from epoll set\n"); + pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET; } #endif if (NULL != pos->response) @@ -1949,10 +1993,10 @@ MHD_poll (struct MHD_Daemon *daemon, /** * Do 'epoll'-based processing (this function is allowed to - * block). + * block if 'may_block' is set to MHD_YES). * * @param daemon daemon to run poll loop for - * @param may_block YES if blocking, NO if non-blocking + * @param may_block MHD_YES if blocking, MHD_NO if non-blocking * @return MHD_NO on serious errors, MHD_YES on success */ static int @@ -1966,7 +2010,8 @@ MHD_epoll (struct MHD_Daemon *daemon, int timeout_ms; MHD_UNSIGNED_LONG_LONG timeout_ll; int num_events; - unsigned int i; + unsigned int i; + unsigned int series_length; if (-1 == daemon->epoll_fd) return MHD_NO; /* we're down! */ @@ -2082,8 +2127,11 @@ MHD_epoll (struct MHD_Daemon *daemon, { /* run 'accept' until it fails or we are not allowed to take on more connections */ + series_length = 0; while ( (MHD_YES == MHD_accept_connection (daemon)) && - (0 != daemon->max_connections) ) ; + (0 != daemon->max_connections) && + (series_length < 128) ) + series_length++; } } } @@ -2094,7 +2142,7 @@ MHD_epoll (struct MHD_Daemon *daemon, EDLL_remove (daemon->eready_head, daemon->eready_tail, pos); - pos->epoll_state -= MHD_EPOLL_STATE_IN_EREADY_EDLL; + pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL; if (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) pos->read_handler (pos); if (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h index 61424ca5..5c5605a7 100644 --- a/src/microhttpd/internal.h +++ b/src/microhttpd/internal.h @@ -111,7 +111,12 @@ enum MHD_EpollState /** * Is this connection currently in the 'eready' EDLL? */ - MHD_EPOLL_STATE_IN_EREADY_EDLL = 4 + MHD_EPOLL_STATE_IN_EREADY_EDLL = 4, + + /** + * Is this connection currently in the 'epoll' set? + */ + MHD_EPOLL_STATE_IN_EPOLL_SET = 8 }; @@ -719,6 +724,11 @@ struct MHD_Connection */ int thread_joined; + /** + * Are we currently inside the "idle" handler (to avoid recursively invoking it). + */ + int in_idle; + #if EPOLL_SUPPORT /** * What is the state of this socket in relation to epoll? -- cgit v1.2.3