From 79f2eea2629367ca402671cf7d033e52de0f9e8a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 25 Jun 2013 13:13:46 +0000 Subject: [PATCH] -documentation updates for epoll --- doc/libmicrohttpd.texi | 136 +++++++++++++++++++++++++++++++++++++-- doc/performance_data.png | Bin 0 -> 9169 bytes 2 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 doc/performance_data.png diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi index 882431a4..377302ef 100644 --- a/doc/libmicrohttpd.texi +++ b/doc/libmicrohttpd.texi @@ -142,6 +142,72 @@ Examples based on reports we've received from developers include: @c If you have other interesting examples, please let us know @end itemize +@section Thread modes and event loops +@cindex poll +@cindex epoll +@cindex select + +MHD supports four basic thread modes and up to three event loop +styes. + +The four basic thread modes are external (MHD creates no threads, +event loop is fully managed by the application), internal (MHD creates +one thread for all connections), thread pool (MHD creates a thread +pool which is used to process all connections) and +thread-per-connection (MHD creates one listen thread and then one +thread per accepted connection). + +These thread modes are then combined with the event loop styles. +MHD support select, poll and epoll. epoll is only available on +Linux, poll may not be available on some platforms. Note that +it is possible to combine MHD using epoll with an external +select-based event loop. + +The default (if no other option is passed) is ``external select''. +The highest performance can typically be obtained with a thread pool +using @code{epoll}. Apache Benchmark (ab) was used to compare the +performance of @code{select} and @code{epoll} when using a thread pool +and a large number of connections. @ref{fig:performance} shows the +resulting plot from the @code{benchmark.c} example, which measures the +latency between an incoming request and the completion of the +transmission of the response. In this setting, the @code{epoll} +thread pool with four threads was able to handle more than 45,000 +connections per second on loopback (with Apache Benchmark running +three processes on the same machine). +@cindex performance + + +@float Figure,fig:performance +@image{performance_data,400pt,300pt,Data,.png} +@caption{Performance measurements for select vs. epoll (with thread-pool).} +@end float + + +Not all combinations of thread modes and event loop styles are +supported. This is partially to keep the API simple, and partially +because some combinations simply make no sense as others are strictly +superior. Note that the choice of style depends fist of all on the +application logic, and then on the performance requirements. +Applications that perform a blocking operation while handling a +request within the callbacks from MHD must use a thread per +connection. This is typically rather costly. Applications that do +not support threads or that must run on embedded devices without +thread-support must use the external mode. Using @code{epoll} is only +supported on Linux, thus portable applications must at least have a +fallback option available. @ref{tbl:supported} lists the sane +combinations. + +@float Table,tbl:supported +@multitable {@b{thread-per-connection}} {@b{select}} {@b{poll}} {@b{epoll}} +@item @tab @b{select} @tab @b{poll} @tab @b{epoll} +@item @b{external} @tab yes @tab no @tab yes +@item @b{internal} @tab yes @tab yes @tab yes +@item @b{thread pool} @tab yes @tab yes @tab yes +@item @b{thread-per-connection} @tab yes @tab yes @tab no +@end multitable +@caption{Supported combinations of event styles and thread modes.} +@end float + @section Compiling GNU libmicrohttpd @cindex compilation @@ -188,6 +254,9 @@ do not include the post processor API (results in binary incompatibility) @item ``--disable-dauth'' do not include the authentication APIs (results in binary incompatibility) +@item ``--disable-epoll +do not include epoll support, even on Linux (minimally smaller binary size, good for testing portability to non-Linux systems) + @item ``--enable-coverage'' set flags for analysis of code-coverage with gcc/gcov (results in slow, large binaries) @@ -341,6 +410,16 @@ Run using the IPv6 protocol (otherwise, MHD will just support IPv4). If you specify @code{MHD_USE_IPV6} and the local platform does not support it, @code{MHD_start_daemon} will return NULL. +If you want MHD to support IPv4 and IPv6 using a single socket, pass +MHD_USE_DUAL_STACK, otherwise, if you only pass this option, MHD will +try to bind to IPv6-only (resulting in no IPv4 support). + +@item MHD_USE_DUAL_STACK +@cindex IPv6 +Use a single socket for IPv4 and IPv6. Note that this will mean +that IPv4 addresses are returned by MHD in the IPv6-mapped format +(the 'struct sockaddr_in6' format will be used for IPv4 and IPv6). + @item MHD_USE_PEDANTIC_CHECKS Be pedantic about the protocol (as opposed to as tolerant as possible). Specifically, at the moment, this flag causes MHD to reject HTTP @@ -355,10 +434,26 @@ production. @cindex poll @cindex select Use poll instead of select. This allows sockets with descriptors -@code{>= FD_SETSIZE}. This option only works in conjunction with -@code{MHD_USE_THREAD_PER_CONNECTION} (at this point). If you -specify @code{MHD_USE_POLL} and the local platform does not support -it, @code{MHD_start_daemon} will return NULL. +@code{>= FD_SETSIZE}. This option currently only works in conjunction +with @code{MHD_USE_THREAD_PER_CONNECTION} or +@code{MHD_USE_INTERNAL_SELECT} (at this point). If you specify +@code{MHD_USE_POLL} and the local platform does not support it, +@code{MHD_start_daemon} will return NULL. + +@item MHD_USE_EPOLL_LINUX_ONLY +@cindex FD_SETSIZE +@cindex epoll +@cindex select +Use epoll instead of poll or select. This allows sockets with +descriptors @code{>= FD_SETSIZE}. This option is only available on +Linux systems and only works in conjunction with +@code{MHD_USE_THREAD_PER_CONNECTION} (at this point). If you specify +@code{MHD_USE_EPOLL_LINUX_ONLY} and the local platform does not +support it, @code{MHD_start_daemon} will return NULL. Using epoll +instead of select or poll can in some situations result in significantly +higher performance as the system call has fundamentally lower complexity +(O(1) for epoll vs. O(n) for select/poll where n is the number of +open connections). @item MHD_SUPPRESS_DATE_NO_CLOCK @cindex date @@ -380,6 +475,18 @@ connect HTTP clients to the HTTP server. This option is incompatible with using a thread pool; if it is used, @code{MHD_OPTION_THREAD_POOL_SIZE} is ignored. +@item MHD_USE_PIPE_FOR_SHUTDOWN +@cindex quiesce +Force MHD to use a signal pipe to notify the event loop (of threads) +of our shutdown. This is required if an appliction uses +@code{MHD_USE_INTERNAL_SELECT} or @code{MHD_USE_THREAD_PER_CONNECTION} +and then performs @code{MHD_quiesce_daemon} (which eliminates our +ability to signal termination via the listen socket). In these modes, +@code{MHD_quiesce_daemon} will fail if this option was not set. Also, +use of this option is automatic (as in, you do not even have to +specify it), if @code{MHD_USE_NO_LISTEN_SOCKET} is specified. In +"external" select mode, this option is always simply ignored. + @end table @end deftp @@ -1087,6 +1194,7 @@ Return @code{NULL} on error, handle to daemon on success. @deftypefun int MHD_quiesce_daemon (struct MHD_Daemon *daemon) +@cindex quiesce Stop accepting connections from the listening socket. Allows clients to continue processing, but stops accepting new connections. Note that the caller is responsible for closing the returned socket; @@ -1102,6 +1210,7 @@ processed until they are finished. Return @code{-1} on error (daemon not listening), the handle to the listen socket otherwise. + @end deftypefun @@ -1130,6 +1239,7 @@ Return @code{MHD_YES} on success, @code{MHD_NO} if this daemon was not started with the right options for this call. @end deftypefun + @deftypefun int MHD_run_from_select (struct MHD_Daemon *daemon, const fd_set *read_fd_set, const fd_set *write_fd_set, const fd_set *except_fd_set) Run webserver operations given sets of ready socket handles. @cindex select @@ -1162,6 +1272,7 @@ errors. @end deftypefun + @deftypefun void MHD_add_connection (struct MHD_Daemon *daemon, int client_socket, const struct sockaddr *addr, socklen_t addrlen) Add another client connection to the set of connections managed by MHD. This API is usually not needed (since @@ -2055,12 +2166,14 @@ information about a daemon is desired. @item MHD_DAEMON_INFO_KEY_SIZE Request information about the key size for a particular cipher algorithm. The cipher algorithm should be passed as an extra argument -(of type 'enum MHD_GNUTLS_CipherAlgorithm'). +(of type 'enum MHD_GNUTLS_CipherAlgorithm'). No longer supported, +using this value will cause MHD_get_daemon_info to return NULL. @item MHD_DAEMON_INFO_MAC_KEY_SIZE Request information about the key size for a particular cipher algorithm. The cipher algorithm should be passed as an extra argument -(of type 'enum MHD_GNUTLS_HashAlgorithm'). +(of type 'enum MHD_GNUTLS_HashAlgorithm'). No longer supported, +using this value will cause MHD_get_daemon_info to return NULL. @item MHD_DAEMON_INFO_LISTEN_FD @cindex listen @@ -2070,6 +2183,17 @@ was specified and a client needs to learn what port is actually being used by MHD. No extra arguments should be passed. +@item MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY +@cindex epoll +Request the file-descriptor number that MHD is using for epoll. If +the build is not supporting epoll, NULL is returned; if we are using a +thread pool or this daemon was not started with +MHD_USE_EPOLL_LINUX_ONLY, (a pointer to) -1 is returned. If we are +using MHD_USE_SELECT_INTERNALLY or are in 'external' select mode, the +internal epoll FD is returned. This function must be used in external +select mode with epoll to obtain the FD to call epoll on. No extra +arguments should be passed. + @end table @end deftp diff --git a/doc/performance_data.png b/doc/performance_data.png new file mode 100644 index 0000000000000000000000000000000000000000..0e447c2409e71562de5253d642621a1a7c9e6fba GIT binary patch literal 9169 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 literal 0 HcmV?d00001