libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit dd2d724c197d6fb5c3d676ae786763190ef278c5
parent 3edd4bfa7f87f5970fc701b0772eeadc2f076df9
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sun, 26 Feb 2017 23:34:20 +0300

Reworked handling "already ready" situations:
* busy-waiting for write if TLS connection is in MHD_EVENT_LOOP_INFO_WRITE mode and data is pending in TLS buffers
* removed calculation of number of TLS read-ready connections
* simplified and unified processing if any connection is in MHD_EVENT_LOOP_INFO_BLOCK mode

Diffstat:
Msrc/microhttpd/daemon.c | 106++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/microhttpd/internal.h | 25+++++++++++--------------
2 files changed, 67 insertions(+), 64 deletions(-)

diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -428,11 +428,6 @@ recv_tls_adapter (struct MHD_Connection *connection, { ssize_t res; - if (connection->tls_read_ready) - { - connection->daemon->num_tls_read_ready--; - connection->tls_read_ready = false; - } res = gnutls_record_recv (connection->tls_session, other, (i > SSIZE_MAX) ? SSIZE_MAX : i); @@ -451,13 +446,13 @@ recv_tls_adapter (struct MHD_Connection *connection, disrupted); set errno to something caller will interpret correctly as a hard error */ MHD_socket_set_error_ (MHD_SCKT_ECONNRESET_); + connection->tls_read_ready = false; return res; } - if ((size_t)res == i) - { - connection->tls_read_ready = true; - connection->daemon->num_tls_read_ready++; - } + + /* Check whether TLS buffers still have some unread data. */ + connection->tls_read_ready = ( ((size_t)res == i) && + (0 != gnutls_record_check_pending (connection->tls_session)) ); return res; } @@ -951,6 +946,23 @@ call_handlers (struct MHD_Connection *con, ret = con->idle_handler (con); } } + + /* All connection's data and states are processed for this turn. + * If connection already has more data to be processed - use + * zero timeout for next select()/poll(). */ + /* Thread-per-connection do not need global zero timeout as + * connections are processed individually. */ + if ( (!con->daemon->data_already_pending) && + (0 == (con->daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ) + { + if (MHD_EVENT_LOOP_INFO_BLOCK == con->event_loop_info) + con->daemon->data_already_pending = true; +#ifdef HTTPS_SUPPORT + else if ( (con->tls_read_ready) && + (MHD_EVENT_LOOP_INFO_READ == con->event_loop_info) ) + con->daemon->data_already_pending = true; +#endif /* HTTPS_SUPPORT */ + } return ret; } @@ -1061,7 +1073,6 @@ process_urh (struct MHD_UpgradeResponseHandle *urh) if (0 < gnutls_record_check_pending (urh->connection->tls_session)) { urh->connection->tls_read_ready = true; - urh->connection->daemon->has_tls_recv_ready = true; } } else if (0 >= res) @@ -1238,6 +1249,13 @@ process_urh (struct MHD_UpgradeResponseHandle *urh) } } + /* Check whether data is present in TLS buffers + * and incoming forward buffer have some space. */ + if ( (urh->connection->tls_read_ready) && + (urh->in_buffer_used < urh->in_buffer_size) && + (0 == (urh->connection->daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ) + urh->connection->daemon->data_already_pending = true; + if ( (urh->connection->daemon->shutdown) && ( (0 != urh->out_buffer_size) || (0 != urh->out_buffer_used) ) ) @@ -1312,7 +1330,8 @@ thread_main_connection_upgrade (struct MHD_Connection *con) { struct timeval* tvp; struct timeval tv; - if (con->tls_read_ready) + if ( (con->tls_read_ready) && + (urh->in_buffer_used < urh->in_buffer_size)) { /* No need to wait if incoming data is already pending in TLS buffers. */ tv.tv_sec = 0; tv.tv_usec = 0; @@ -1378,7 +1397,8 @@ thread_main_connection_upgrade (struct MHD_Connection *con) if (0 != urh->in_buffer_used) p[1].events |= POLLOUT; - if (con->tls_read_ready) + if ( (con->tls_read_ready) && + (urh->in_buffer_used < urh->in_buffer_size)) timeout = 0; /* No need to wait if incoming data is already pending in TLS buffers. */ else timeout = UINT_MAX; @@ -1548,15 +1568,20 @@ thread_main_handle_connection (void *data) } tvp = NULL; + + if ( (MHD_EVENT_LOOP_INFO_BLOCK == con->event_loop_info) #ifdef HTTPS_SUPPORT - if (con->tls_read_ready) + || ( (con->tls_read_ready) && + (MHD_EVENT_LOOP_INFO_READ == con->event_loop_info) ) +#endif /* HTTPS_SUPPORT */ + ) { - /* do not block (more data may be inside of TLS buffers waiting for us) */ + /* do not block: more data may be inside of TLS buffers waiting or + * application must provide response data */ tv.tv_sec = 0; tv.tv_usec = 0; tvp = &tv; } -#endif /* HTTPS_SUPPORT */ if ( (NULL == tvp) && (timeout > 0) ) { @@ -1609,9 +1634,6 @@ thread_main_handle_connection (void *data) &maxsock, FD_SETSIZE)) err_state = true; - tv.tv_sec = 0; - tv.tv_usec = 0; - tvp = &tv; break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* how did we get here!? */ @@ -1691,9 +1713,6 @@ thread_main_handle_connection (void *data) break; case MHD_EVENT_LOOP_INFO_BLOCK: p[0].events |= MHD_POLL_EVENTS_ERR_DISC; - tv.tv_sec = 0; - tv.tv_usec = 0; - tvp = &tv; break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* how did we get here!? */ @@ -2905,15 +2924,13 @@ MHD_get_timeout (struct MHD_Daemon *daemon, return MHD_NO; } -#ifdef HTTPS_SUPPORT - if (0 != daemon->num_tls_read_ready || daemon->has_tls_recv_ready) + if (daemon->data_already_pending) { - /* if there is any TLS connection with data ready for - reading, we must not block in the event loop */ + /* Some data already waiting to be processed. */ *timeout = 0; return MHD_YES; } -#endif /* HTTPS_SUPPORT */ + #ifdef EPOLL_SUPPORT if ( (0 != (daemon->options & MHD_USE_EPOLL)) && (NULL != daemon->eready_head) ) @@ -3009,6 +3026,10 @@ MHD_run_from_select (struct MHD_Daemon *daemon, unsigned int mask = MHD_ALLOW_SUSPEND_RESUME | MHD_USE_EPOLL_INTERNAL_THREAD | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_POLL_INTERNAL_THREAD; + /* Reset. New value will be set when connections are processed. */ + /* Note: no-op for thread-per-connection as it is always false in that mode. */ + daemon->data_already_pending = false; + /* Clear ITC to avoid spinning select */ /* Do it before any other processing so new signals will trigger select again and will be processed */ @@ -3017,14 +3038,6 @@ MHD_run_from_select (struct MHD_Daemon *daemon, read_fd_set)) ) MHD_itc_clear_ (daemon->itc); -#ifdef HTTPS_SUPPORT - /* Reset TLS read-ready. - * New value will be set by read handlers. */ - if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && - (0 != (daemon->options & MHD_USE_TLS)) ) - daemon->has_tls_recv_ready = 0; -#endif /* HTTPS_SUPPORT */ - /* Resuming external connections when using an extern mainloop */ if (MHD_ALLOW_SUSPEND_RESUME == (daemon->options & mask)) resume_suspended_connections (daemon); @@ -3364,7 +3377,6 @@ MHD_poll_all (struct MHD_Daemon *daemon, break; case MHD_EVENT_LOOP_INFO_BLOCK: p[poll_server+i].events |= MHD_POLL_EVENTS_ERR_DISC; - timeout = 0; break; case MHD_EVENT_LOOP_INFO_CLEANUP: timeout = 0; /* clean up "pos" immediately */ @@ -3412,6 +3424,10 @@ MHD_poll_all (struct MHD_Daemon *daemon, free(p); return MHD_NO; } + + /* Reset. New value will be set when connections are processed. */ + daemon->data_already_pending = false; + /* handle ITC FD */ /* do it before any other processing so new signals will be processed in next loop */ @@ -3419,14 +3435,6 @@ MHD_poll_all (struct MHD_Daemon *daemon, (0 != (p[poll_itc_idx].revents & POLLIN)) ) MHD_itc_clear_ (daemon->itc); -#ifdef HTTPS_SUPPORT - /* Reset TLS read-ready. - * New value will be set by read handlers. */ - if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && - (0 != (daemon->options & MHD_USE_TLS)) ) - daemon->has_tls_recv_ready = 0; -#endif /* HTTPS_SUPPORT */ - /* handle shutdown */ if (daemon->shutdown) { @@ -3816,12 +3824,10 @@ MHD_epoll (struct MHD_Daemon *daemon, else timeout_ms = 0; -#ifdef HTTPS_SUPPORT - /* Reset TLS read-ready. - * New value will be set by read handlers. */ - if ( 0 != (daemon->options & MHD_USE_TLS) ) - daemon->has_tls_recv_ready = false; -#endif /* HTTPS_SUPPORT */ + /* Reset. New value will be set when connections are processed. */ + /* Note: Used mostly for uniformity here as same situation is + * signaled in epoll mode by non-empty eready DLL. */ + daemon->data_already_pending = false; /* drain 'epoll' event queue; need to iterate as we get at most MAX_EVENTS in one system call here; in practice this should diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -1452,6 +1452,17 @@ struct MHD_Daemon bool resuming; /** + * 'True' if some data is already waiting to be processed. + * If set to 'true' - zero timeout for select()/poll*() + * is used. + * Should be reset each time before processing connections + * and raised by any connection which require additional + * immediately processing (application does not provide + * data for response, data waiting in TLS buffers etc.) + */ + bool data_already_pending; + + /** * Number of active parallel connections. */ unsigned int connections; @@ -1559,20 +1570,6 @@ struct MHD_Daemon */ bool have_dhparams; - /** - * For how many connections do we have 'tls_read_ready' set to MHD_YES? - * Used to avoid O(n) traversal over all connections when determining - * event-loop timeout (as it needs to be zero if there is any connection - * which might have ready data within TLS). - */ - unsigned int num_tls_read_ready; - - /** - * Indicate that some TLS connection(s) have received data pending in - * TLS buffers. - */ - bool has_tls_recv_ready; - #endif /* HTTPS_SUPPORT */ #ifdef DAUTH_SUPPORT