aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-05-02 04:13:06 +0000
committerChristian Grothoff <christian@grothoff.org>2016-05-02 04:13:06 +0000
commit84ec6a79caafa209fef4ac8e1b87769863a79346 (patch)
tree525383b1cf0b7c9a227479d412fc16a773ebbb69
parentcc803ec8c257bea820e20927b38b5a5ae1fce7c6 (diff)
downloadlibmicrohttpd-84ec6a79caafa209fef4ac8e1b87769863a79346.tar.gz
libmicrohttpd-84ec6a79caafa209fef4ac8e1b87769863a79346.zip
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
-rw-r--r--ChangeLog9
-rw-r--r--doc/libmicrohttpd.texi19
-rw-r--r--src/include/microhttpd.h2
-rw-r--r--src/microhttpd/daemon.c180
4 files changed, 115 insertions, 95 deletions
diff --git a/ChangeLog b/ChangeLog
index 39404ccf..66829f94 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
1Mon May 2 06:08:26 CEST 2016
2 Adding logic to help address FE performance issue as
3 discussed on the mailinglist with subject
4 "single-threaded daemon, multiple pending requests, responses batched".
5 The new logic is only enabled when MHD_USE_EPOLL_TURBO is set.
6 Note that some additional refactoring was also done to clean up
7 the code and avoid code duplication, which may have actually fixed
8 an unrelated issue with HTTPS and a POLL-style event loop. -CG
9
1Sat Apr 30 10:22:37 CEST 2016 10Sat Apr 30 10:22:37 CEST 2016
2 Added clarifications to manual based on questions on list. -CG 11 Added clarifications to manual based on questions on list. -CG
3 12
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 497595cc..408f471d 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -520,6 +520,25 @@ call has fundamentally lower complexity (O(1) for @code{epoll()}
520vs. O(n) for @code{select()}/@code{poll()} where n is the number of 520vs. O(n) for @code{select()}/@code{poll()} where n is the number of
521open connections). 521open connections).
522 522
523@item MHD_USE_EPOLL_TURBO
524@cindex performance
525Enable optimizations to aggressively improve performance. Note that
526the option is a slight misnomer, as these days it also enables optimziations
527that are unrelated to @code{MHD_USE_EPOLL_LINUX_ONLY}. Hence it is OK to
528use this option with other event loops.
529
530Currently, the optimizations this option enables are based on
531opportunistic reads and writes. Bascially, MHD will simply try to
532read or write or accept on a socket before checking that the socket is
533ready for IO using the event loop mechanism. As the sockets are
534non-blocking, this may fail (at a loss of performance), but generally
535MHD does this in situations where the operation is likely to succeed,
536in which case performance is improved. Setting the flag should generally
537be safe (even though the code is slightly more experimental). You may
538want to benchmark your application to see if this makes any difference
539for you.
540
541
523@item MHD_SUPPRESS_DATE_NO_CLOCK 542@item MHD_SUPPRESS_DATE_NO_CLOCK
524@cindex date 543@cindex date
525@cindex clock 544@cindex clock
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 561ca6f5..0f1c3d5f 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -130,7 +130,7 @@ typedef intptr_t ssize_t;
130 * Current version of the library. 130 * Current version of the library.
131 * 0x01093001 = 1.9.30-1. 131 * 0x01093001 = 1.9.30-1.
132 */ 132 */
133#define MHD_VERSION 0x00094900 133#define MHD_VERSION 0x00094901
134 134
135/** 135/**
136 * MHD-internal return code for "YES". 136 * MHD-internal return code for "YES".
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 5476f956..2c0e0a82 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -826,6 +826,62 @@ MHD_get_fdset2 (struct MHD_Daemon *daemon,
826 826
827 827
828/** 828/**
829 * Call the handlers for a connection in the
830 * appropriate order based on the readiness as
831 * detected by the event loop.
832 *
833 * @param con connection to handle
834 * @param read_ready set if the socket is ready for reading
835 * @param write_ready set if the socket is ready for writing
836 * @param force_close set if a hard error was detected on the socket;
837 * if this information is not available, simply pass #MHD_NO
838 * @return #MHD_YES to continue normally,
839 * #MHD_NO if a serious error was encountered and the
840 * connection is to be closed.
841 */
842static int
843call_handlers (struct MHD_Connection *con,
844 int read_ready,
845 int write_ready,
846 int force_close)
847{
848 struct MHD_Daemon *daemon = con->daemon;
849 int had_response_before_idle;
850 int ret;
851
852#if HTTPS_SUPPORT
853 if (MHD_YES == con->tls_read_ready)
854 read_ready = MHD_YES;
855#endif
856 if (read_ready)
857 con->read_handler (con);
858 if (write_ready)
859 con->write_handler (con);
860 had_response_before_idle = (NULL != con->response);
861 if (force_close)
862 MHD_connection_close_ (con,
863 MHD_REQUEST_TERMINATED_WITH_ERROR);
864 ret = con->idle_handler (con);
865 /* If we're in TURBO mode, and got a response object,
866 try opportunistically to just call write immediately. */
867 if ( (! force_close) &&
868 (MHD_YES == ret) &&
869 (0 != (daemon->options & MHD_USE_EPOLL_TURBO)) &&
870 (NULL != con->response) &&
871 (MHD_NO == had_response_before_idle) )
872 {
873 /* first 'write' gets the header, then 'idle'
874 readies the body, then 2nd 'write' may send
875 the body. */
876 con->write_handler (con);
877 if (MHD_YES == (ret = con->idle_handler (con)))
878 con->write_handler (con);
879 }
880 return ret;
881}
882
883
884/**
829 * Main function of the thread that handles an individual 885 * Main function of the thread that handles an individual
830 * connection when #MHD_USE_THREAD_PER_CONNECTION is set. 886 * connection when #MHD_USE_THREAD_PER_CONNECTION is set.
831 * 887 *
@@ -962,17 +1018,12 @@ MHD_handle_connection (void *data)
962 (FD_ISSET (spipe, &rs)) ) 1018 (FD_ISSET (spipe, &rs)) )
963 MHD_pipe_drain_ (spipe); 1019 MHD_pipe_drain_ (spipe);
964#endif 1020#endif
965 /* call appropriate connection handler if necessary */ 1021 if (MHD_NO ==
966 if ( (FD_ISSET (con->socket_fd, &rs)) 1022 call_handlers (con,
967#if HTTPS_SUPPORT 1023 FD_ISSET (con->socket_fd, &rs),
968 || (MHD_YES == con->tls_read_ready) 1024 FD_ISSET (con->socket_fd, &ws),
969#endif 1025 MHD_NO))
970 ) 1026 goto exit;
971 con->read_handler (con);
972 if (FD_ISSET (con->socket_fd, &ws))
973 con->write_handler (con);
974 if (MHD_NO == con->idle_handler (con))
975 goto exit;
976 } 1027 }
977#ifdef HAVE_POLL 1028#ifdef HAVE_POLL
978 else 1029 else
@@ -1034,19 +1085,12 @@ MHD_handle_connection (void *data)
1034 (0 != (p[1].revents & (POLLERR | POLLHUP))) ) 1085 (0 != (p[1].revents & (POLLERR | POLLHUP))) )
1035 MHD_pipe_drain_ (spipe); 1086 MHD_pipe_drain_ (spipe);
1036#endif 1087#endif
1037 if ( (0 != (p[0].revents & POLLIN)) 1088 if (MHD_NO ==
1038#if HTTPS_SUPPORT 1089 call_handlers (con,
1039 || (MHD_YES == con->tls_read_ready) 1090 0 != (p[0].revents & POLLIN),
1040#endif 1091 0 != (p[0].revents & POLLOUT),
1041 ) 1092 0 != (p[0].revents & (POLLERR | POLLHUP))))
1042 con->read_handler (con); 1093 goto exit;
1043 if (0 != (p[0].revents & POLLOUT))
1044 con->write_handler (con);
1045 if (0 != (p[0].revents & (POLLERR | POLLHUP)))
1046 MHD_connection_close_ (con,
1047 MHD_REQUEST_TERMINATED_WITH_ERROR);
1048 if (MHD_NO == con->idle_handler (con))
1049 goto exit;
1050 } 1094 }
1051#endif 1095#endif
1052 } 1096 }
@@ -1823,7 +1867,7 @@ resume_suspended_connections (struct MHD_Daemon *daemon)
1823 MHD_PANIC ("Failed to acquire cleanup mutex\n"); 1867 MHD_PANIC ("Failed to acquire cleanup mutex\n");
1824 if (MHD_NO != daemon->resuming) 1868 if (MHD_NO != daemon->resuming)
1825 next = daemon->suspended_connections_head; 1869 next = daemon->suspended_connections_head;
1826 1870
1827 /* Clear the flag *only* if connections will be resumed otherwise 1871 /* Clear the flag *only* if connections will be resumed otherwise
1828 it may accidentally clear flag that was set at the same time in 1872 it may accidentally clear flag that was set at the same time in
1829 other thread (just after 'if (MHD_NO != daemon->resuming)' in 1873 other thread (just after 'if (MHD_NO != daemon->resuming)' in
@@ -2310,33 +2354,10 @@ MHD_run_from_select (struct MHD_Daemon *daemon,
2310 ds = pos->socket_fd; 2354 ds = pos->socket_fd;
2311 if (MHD_INVALID_SOCKET == ds) 2355 if (MHD_INVALID_SOCKET == ds)
2312 continue; 2356 continue;
2313 switch (pos->event_loop_info) 2357 call_handlers (pos,
2314 { 2358 FD_ISSET (ds, read_fd_set),
2315 case MHD_EVENT_LOOP_INFO_READ: 2359 FD_ISSET (ds, write_fd_set),
2316 if ( (FD_ISSET (ds, read_fd_set)) 2360 MHD_NO);
2317#if HTTPS_SUPPORT
2318 || (MHD_YES == pos->tls_read_ready)
2319#endif
2320 )
2321 pos->read_handler (pos);
2322 break;
2323 case MHD_EVENT_LOOP_INFO_WRITE:
2324 if ( (FD_ISSET (ds, read_fd_set)) &&
2325 (pos->read_buffer_size > pos->read_buffer_offset) )
2326 pos->read_handler (pos);
2327 if (FD_ISSET (ds, write_fd_set))
2328 pos->write_handler (pos);
2329 break;
2330 case MHD_EVENT_LOOP_INFO_BLOCK:
2331 if ( (FD_ISSET (ds, read_fd_set)) &&
2332 (pos->read_buffer_size > pos->read_buffer_offset) )
2333 pos->read_handler (pos);
2334 break;
2335 case MHD_EVENT_LOOP_INFO_CLEANUP:
2336 /* should never happen */
2337 break;
2338 }
2339 pos->idle_handler (pos);
2340 } 2361 }
2341 } 2362 }
2342 MHD_cleanup_connections (daemon); 2363 MHD_cleanup_connections (daemon);
@@ -2621,43 +2642,15 @@ MHD_poll_all (struct MHD_Daemon *daemon,
2621 while (NULL != (pos = next)) 2642 while (NULL != (pos = next))
2622 { 2643 {
2623 next = pos->next; 2644 next = pos->next;
2624 switch (pos->event_loop_info) 2645 /* first, sanity checks */
2625 { 2646 if (i >= num_connections)
2626 case MHD_EVENT_LOOP_INFO_READ: 2647 continue; /* connection list changed somehow, retry later ... */
2627 /* first, sanity checks */ 2648 if (p[poll_server+i].fd != pos->socket_fd)
2628 if (i >= num_connections) 2649 continue; /* fd mismatch, something else happened, retry later ... */
2629 break; /* connection list changed somehow, retry later ... */ 2650 call_handlers (pos,
2630 if (p[poll_server+i].fd != pos->socket_fd) 2651 0 != (p[poll_server+i].revents & POLLIN),
2631 break; /* fd mismatch, something else happened, retry later ... */ 2652 0 != (p[poll_server+i].revents & POLLOUT),
2632 /* normal handling */ 2653 MHD_NO);
2633 if (0 != (p[poll_server+i].revents & POLLIN))
2634 pos->read_handler (pos);
2635 pos->idle_handler (pos);
2636 i++;
2637 break;
2638 case MHD_EVENT_LOOP_INFO_WRITE:
2639 /* first, sanity checks */
2640 if (i >= num_connections)
2641 break; /* connection list changed somehow, retry later ... */
2642 if (p[poll_server+i].fd != pos->socket_fd)
2643 break; /* fd mismatch, something else happened, retry later ... */
2644 /* normal handling */
2645 if (0 != (p[poll_server+i].revents & POLLIN))
2646 pos->read_handler (pos);
2647 if (0 != (p[poll_server+i].revents & POLLOUT))
2648 pos->write_handler (pos);
2649 pos->idle_handler (pos);
2650 i++;
2651 break;
2652 case MHD_EVENT_LOOP_INFO_BLOCK:
2653 if (0 != (p[poll_server+i].revents & POLLIN))
2654 pos->read_handler (pos);
2655 pos->idle_handler (pos);
2656 break;
2657 case MHD_EVENT_LOOP_INFO_CLEANUP:
2658 pos->idle_handler (pos);
2659 break;
2660 }
2661 } 2654 }
2662 /* handle 'listen' FD */ 2655 /* handle 'listen' FD */
2663 if ( (-1 != poll_listen) && 2656 if ( (-1 != poll_listen) &&
@@ -2934,11 +2927,10 @@ MHD_epoll (struct MHD_Daemon *daemon,
2934 daemon->eready_tail, 2927 daemon->eready_tail,
2935 pos); 2928 pos);
2936 pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL; 2929 pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
2937 if (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) 2930 call_handlers (pos,
2938 pos->read_handler (pos); 2931 MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info,
2939 if (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) 2932 MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info,
2940 pos->write_handler (pos); 2933 MHD_NO);
2941 pos->idle_handler (pos);
2942 } 2934 }
2943 2935
2944 /* Finally, handle timed-out connections; we need to do this here 2936 /* Finally, handle timed-out connections; we need to do this here