mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-10-04 04:03:18 +03:00
adding logic to do 'fast' calls to read/write on a single connection without going back to the main event loop to help FE with his performance issue
This commit is contained in:
1 parent
cc803ec8c2
commit
84ec6a79ca
4 files changed
+115
-95
No files matched your search
@@ -1,3 +1,12 @@
|
||||
Mon May 2 06:08:26 CEST 2016
|
||||
Adding logic to help address FE performance issue as
|
||||
discussed on the mailinglist with subject
|
||||
"single-threaded daemon, multiple pending requests, responses batched".
|
||||
The new logic is only enabled when MHD_USE_EPOLL_TURBO is set.
|
||||
Note that some additional refactoring was also done to clean up
|
||||
the code and avoid code duplication, which may have actually fixed
|
||||
an unrelated issue with HTTPS and a POLL-style event loop. -CG
|
||||
|
||||
Sat Apr 30 10:22:37 CEST 2016
|
||||
Added clarifications to manual based on questions on list. -CG
|
||||
|
||||
|
||||
@@ -520,6 +520,25 @@ call has fundamentally lower complexity (O(1) for @code{epoll()}
|
||||
vs. O(n) for @code{select()}/@code{poll()} where n is the number of
|
||||
open connections).
|
||||
|
||||
@item MHD_USE_EPOLL_TURBO
|
||||
@cindex performance
|
||||
Enable optimizations to aggressively improve performance. Note that
|
||||
the option is a slight misnomer, as these days it also enables optimziations
|
||||
that are unrelated to @code{MHD_USE_EPOLL_LINUX_ONLY}. Hence it is OK to
|
||||
use this option with other event loops.
|
||||
|
||||
Currently, the optimizations this option enables are based on
|
||||
opportunistic reads and writes. Bascially, MHD will simply try to
|
||||
read or write or accept on a socket before checking that the socket is
|
||||
ready for IO using the event loop mechanism. As the sockets are
|
||||
non-blocking, this may fail (at a loss of performance), but generally
|
||||
MHD does this in situations where the operation is likely to succeed,
|
||||
in which case performance is improved. Setting the flag should generally
|
||||
be safe (even though the code is slightly more experimental). You may
|
||||
want to benchmark your application to see if this makes any difference
|
||||
for you.
|
||||
|
||||
|
||||
@item MHD_SUPPRESS_DATE_NO_CLOCK
|
||||
@cindex date
|
||||
@cindex clock
|
||||
|
||||
@@ -130,7 +130,7 @@ typedef intptr_t ssize_t;
|
||||
* Current version of the library.
|
||||
* 0x01093001 = 1.9.30-1.
|
||||
*/
|
||||
#define MHD_VERSION 0x00094900
|
||||
#define MHD_VERSION 0x00094901
|
||||
|
||||
/**
|
||||
* MHD-internal return code for "YES".
|
||||
|
||||
+86
-94
@@ -825,6 +825,62 @@ MHD_get_fdset2 (struct MHD_Daemon *daemon,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call the handlers for a connection in the
|
||||
* appropriate order based on the readiness as
|
||||
* detected by the event loop.
|
||||
*
|
||||
* @param con connection to handle
|
||||
* @param read_ready set if the socket is ready for reading
|
||||
* @param write_ready set if the socket is ready for writing
|
||||
* @param force_close set if a hard error was detected on the socket;
|
||||
* if this information is not available, simply pass #MHD_NO
|
||||
* @return #MHD_YES to continue normally,
|
||||
* #MHD_NO if a serious error was encountered and the
|
||||
* connection is to be closed.
|
||||
*/
|
||||
static int
|
||||
call_handlers (struct MHD_Connection *con,
|
||||
int read_ready,
|
||||
int write_ready,
|
||||
int force_close)
|
||||
{
|
||||
struct MHD_Daemon *daemon = con->daemon;
|
||||
int had_response_before_idle;
|
||||
int ret;
|
||||
|
||||
#if HTTPS_SUPPORT
|
||||
if (MHD_YES == con->tls_read_ready)
|
||||
read_ready = MHD_YES;
|
||||
#endif
|
||||
if (read_ready)
|
||||
con->read_handler (con);
|
||||
if (write_ready)
|
||||
con->write_handler (con);
|
||||
had_response_before_idle = (NULL != con->response);
|
||||
if (force_close)
|
||||
MHD_connection_close_ (con,
|
||||
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
||||
ret = con->idle_handler (con);
|
||||
/* If we're in TURBO mode, and got a response object,
|
||||
try opportunistically to just call write immediately. */
|
||||
if ( (! force_close) &&
|
||||
(MHD_YES == ret) &&
|
||||
(0 != (daemon->options & MHD_USE_EPOLL_TURBO)) &&
|
||||
(NULL != con->response) &&
|
||||
(MHD_NO == had_response_before_idle) )
|
||||
{
|
||||
/* first 'write' gets the header, then 'idle'
|
||||
readies the body, then 2nd 'write' may send
|
||||
the body. */
|
||||
con->write_handler (con);
|
||||
if (MHD_YES == (ret = con->idle_handler (con)))
|
||||
con->write_handler (con);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main function of the thread that handles an individual
|
||||
* connection when #MHD_USE_THREAD_PER_CONNECTION is set.
|
||||
@@ -962,17 +1018,12 @@ MHD_handle_connection (void *data)
|
||||
(FD_ISSET (spipe, &rs)) )
|
||||
MHD_pipe_drain_ (spipe);
|
||||
#endif
|
||||
/* call appropriate connection handler if necessary */
|
||||
if ( (FD_ISSET (con->socket_fd, &rs))
|
||||
#if HTTPS_SUPPORT
|
||||
|| (MHD_YES == con->tls_read_ready)
|
||||
#endif
|
||||
)
|
||||
con->read_handler (con);
|
||||
if (FD_ISSET (con->socket_fd, &ws))
|
||||
con->write_handler (con);
|
||||
if (MHD_NO == con->idle_handler (con))
|
||||
goto exit;
|
||||
if (MHD_NO ==
|
||||
call_handlers (con,
|
||||
FD_ISSET (con->socket_fd, &rs),
|
||||
FD_ISSET (con->socket_fd, &ws),
|
||||
MHD_NO))
|
||||
goto exit;
|
||||
}
|
||||
#ifdef HAVE_POLL
|
||||
else
|
||||
@@ -1034,19 +1085,12 @@ MHD_handle_connection (void *data)
|
||||
(0 != (p[1].revents & (POLLERR | POLLHUP))) )
|
||||
MHD_pipe_drain_ (spipe);
|
||||
#endif
|
||||
if ( (0 != (p[0].revents & POLLIN))
|
||||
#if HTTPS_SUPPORT
|
||||
|| (MHD_YES == con->tls_read_ready)
|
||||
#endif
|
||||
)
|
||||
con->read_handler (con);
|
||||
if (0 != (p[0].revents & POLLOUT))
|
||||
con->write_handler (con);
|
||||
if (0 != (p[0].revents & (POLLERR | POLLHUP)))
|
||||
MHD_connection_close_ (con,
|
||||
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
||||
if (MHD_NO == con->idle_handler (con))
|
||||
goto exit;
|
||||
if (MHD_NO ==
|
||||
call_handlers (con,
|
||||
0 != (p[0].revents & POLLIN),
|
||||
0 != (p[0].revents & POLLOUT),
|
||||
0 != (p[0].revents & (POLLERR | POLLHUP))))
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1823,7 +1867,7 @@ resume_suspended_connections (struct MHD_Daemon *daemon)
|
||||
MHD_PANIC ("Failed to acquire cleanup mutex\n");
|
||||
if (MHD_NO != daemon->resuming)
|
||||
next = daemon->suspended_connections_head;
|
||||
|
||||
|
||||
/* Clear the flag *only* if connections will be resumed otherwise
|
||||
it may accidentally clear flag that was set at the same time in
|
||||
other thread (just after 'if (MHD_NO != daemon->resuming)' in
|
||||
@@ -2310,33 +2354,10 @@ MHD_run_from_select (struct MHD_Daemon *daemon,
|
||||
ds = pos->socket_fd;
|
||||
if (MHD_INVALID_SOCKET == ds)
|
||||
continue;
|
||||
switch (pos->event_loop_info)
|
||||
{
|
||||
case MHD_EVENT_LOOP_INFO_READ:
|
||||
if ( (FD_ISSET (ds, read_fd_set))
|
||||
#if HTTPS_SUPPORT
|
||||
|| (MHD_YES == pos->tls_read_ready)
|
||||
#endif
|
||||
)
|
||||
pos->read_handler (pos);
|
||||
break;
|
||||
case MHD_EVENT_LOOP_INFO_WRITE:
|
||||
if ( (FD_ISSET (ds, read_fd_set)) &&
|
||||
(pos->read_buffer_size > pos->read_buffer_offset) )
|
||||
pos->read_handler (pos);
|
||||
if (FD_ISSET (ds, write_fd_set))
|
||||
pos->write_handler (pos);
|
||||
break;
|
||||
case MHD_EVENT_LOOP_INFO_BLOCK:
|
||||
if ( (FD_ISSET (ds, read_fd_set)) &&
|
||||
(pos->read_buffer_size > pos->read_buffer_offset) )
|
||||
pos->read_handler (pos);
|
||||
break;
|
||||
case MHD_EVENT_LOOP_INFO_CLEANUP:
|
||||
/* should never happen */
|
||||
break;
|
||||
}
|
||||
pos->idle_handler (pos);
|
||||
call_handlers (pos,
|
||||
FD_ISSET (ds, read_fd_set),
|
||||
FD_ISSET (ds, write_fd_set),
|
||||
MHD_NO);
|
||||
}
|
||||
}
|
||||
MHD_cleanup_connections (daemon);
|
||||
@@ -2621,43 +2642,15 @@ MHD_poll_all (struct MHD_Daemon *daemon,
|
||||
while (NULL != (pos = next))
|
||||
{
|
||||
next = pos->next;
|
||||
switch (pos->event_loop_info)
|
||||
{
|
||||
case MHD_EVENT_LOOP_INFO_READ:
|
||||
/* first, sanity checks */
|
||||
if (i >= num_connections)
|
||||
break; /* connection list changed somehow, retry later ... */
|
||||
if (p[poll_server+i].fd != pos->socket_fd)
|
||||
break; /* fd mismatch, something else happened, retry later ... */
|
||||
/* normal handling */
|
||||
if (0 != (p[poll_server+i].revents & POLLIN))
|
||||
pos->read_handler (pos);
|
||||
pos->idle_handler (pos);
|
||||
i++;
|
||||
break;
|
||||
case MHD_EVENT_LOOP_INFO_WRITE:
|
||||
/* first, sanity checks */
|
||||
if (i >= num_connections)
|
||||
break; /* connection list changed somehow, retry later ... */
|
||||
if (p[poll_server+i].fd != pos->socket_fd)
|
||||
break; /* fd mismatch, something else happened, retry later ... */
|
||||
/* normal handling */
|
||||
if (0 != (p[poll_server+i].revents & POLLIN))
|
||||
pos->read_handler (pos);
|
||||
if (0 != (p[poll_server+i].revents & POLLOUT))
|
||||
pos->write_handler (pos);
|
||||
pos->idle_handler (pos);
|
||||
i++;
|
||||
break;
|
||||
case MHD_EVENT_LOOP_INFO_BLOCK:
|
||||
if (0 != (p[poll_server+i].revents & POLLIN))
|
||||
pos->read_handler (pos);
|
||||
pos->idle_handler (pos);
|
||||
break;
|
||||
case MHD_EVENT_LOOP_INFO_CLEANUP:
|
||||
pos->idle_handler (pos);
|
||||
break;
|
||||
}
|
||||
/* first, sanity checks */
|
||||
if (i >= num_connections)
|
||||
continue; /* connection list changed somehow, retry later ... */
|
||||
if (p[poll_server+i].fd != pos->socket_fd)
|
||||
continue; /* fd mismatch, something else happened, retry later ... */
|
||||
call_handlers (pos,
|
||||
0 != (p[poll_server+i].revents & POLLIN),
|
||||
0 != (p[poll_server+i].revents & POLLOUT),
|
||||
MHD_NO);
|
||||
}
|
||||
/* handle 'listen' FD */
|
||||
if ( (-1 != poll_listen) &&
|
||||
@@ -2934,11 +2927,10 @@ MHD_epoll (struct MHD_Daemon *daemon,
|
||||
daemon->eready_tail,
|
||||
pos);
|
||||
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)
|
||||
pos->write_handler (pos);
|
||||
pos->idle_handler (pos);
|
||||
call_handlers (pos,
|
||||
MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info,
|
||||
MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info,
|
||||
MHD_NO);
|
||||
}
|
||||
|
||||
/* Finally, handle timed-out connections; we need to do this here
|
||||
|
||||
Reference in new issue
Block a user