| # SYNOPSIS |
| # |
| # MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER([ACTION-IF-TRIGGER], [ACTION-IF-NOT], |
| # [ACTION-IF-UNKNOWN]) |
| # |
| # DESCRIPTION |
| # |
| # Check whether shutdown of listen socket triggers waiting select(). |
| # If cross-compiling, result may be unknown (third action). |
| # Result is cached in $mhd_cv_host_shtdwn_trgr_select variable. |
| # |
| # LICENSE |
| # |
| # Copyright (c) 2017-2023 Karlson2k (Evgeny Grin) <k2k@narod.ru> |
| # |
| # Copying and distribution of this file, with or without modification, are |
| # permitted in any medium without royalty provided the copyright notice |
| # and this notice are preserved. This file is offered as-is, without any |
| # warranty. |
| |
| #serial 6 |
| |
| AC_DEFUN([MHD_CHECK_SOCKET_SHUTDOWN_TRIGGER],[dnl |
| AC_PREREQ([2.64])dnl |
| AC_REQUIRE([AC_CANONICAL_HOST])dnl |
| AC_REQUIRE([AC_PROG_CC])dnl |
| AC_REQUIRE([AX_PTHREAD])dnl |
| AC_REQUIRE([MHD_CHECK_FUNC_GETTIMEOFDAY])dnl |
| MHD_CHECK_FUNC([[usleep]], [[#include <unistd.h>]], [[usleep(100000);]]) |
| MHD_CHECK_FUNC([[nanosleep]], [[#include <time.h>]], [[struct timespec ts2, ts1 = {0, 0}; nanosleep(&ts1, &ts2);]]) |
| AC_CHECK_HEADERS([string.h sys/types.h sys/socket.h netinet/in.h time.h sys/select.h netinet/tcp.h],[],[], [AC_INCLUDES_DEFAULT]) |
| AC_CACHE_CHECK([[whether shutdown of listen socket triggers select()]], |
| [[mhd_cv_host_shtdwn_trgr_select]], [dnl |
| _MHD_OS_KNOWN_SOCKET_SHUTDOWN_TRIGGER([[mhd_cv_host_shtdwn_trgr_select]]) |
| AS_VAR_IF([mhd_cv_host_shtdwn_trgr_select], [["maybe"]], |
| [_MHD_RUN_CHECK_SOCKET_SHUTDOWN_TRIGGER([[mhd_cv_host_shtdwn_trgr_select]])]) |
| ] |
| ) |
| AS_IF([[test "x$mhd_cv_host_shtdwn_trgr_select" = "xyes"]], [$1], |
| [[test "x$mhd_cv_host_shtdwn_trgr_select" = "xno"]], [$2], [$3]) |
| ] |
| ) |
| |
| # |
| # _MHD_OS_KNOWN_SOCKET_SHUTDOWN_TRIGGER(VAR) |
| # |
| # Sets VAR to 'yes', 'no' or 'maybe'. |
| |
| AC_DEFUN([_MHD_OS_KNOWN_SOCKET_SHUTDOWN_TRIGGER],[dnl |
| [#] On Linux shutdown of listen socket always trigger select(). |
| [#] On Windows select() always ignore shutdown of listen socket. |
| [#] On other paltforms result may vary depending on platform version. |
| AS_CASE([[$host_os]], |
| [[linux | linux-* | *-linux | *-linux-*]], [$1='yes'], |
| [[mingw*]], [$1='no'], |
| [[cygwin* | msys*]], [$1='no'], |
| [[winnt* | interix*]], [$1='no'], |
| [[mks]], [$1='no'], |
| [[uwin]], [$1='no'], |
| [$1='maybe'] |
| ) |
| ] |
| ) |
| |
| # |
| # _MHD_RUN_CHECK_SOCKET_SHUTDOWN_TRIGGER(VAR) |
| # |
| # Sets VAR to 'yes', 'no' or 'guessing no'. |
| |
| AC_DEFUN([_MHD_RUN_CHECK_SOCKET_SHUTDOWN_TRIGGER],[dnl |
| AC_LANG_PUSH([C]) |
| MHD_CST_SAVE_CC="$CC" |
| MHD_CST_SAVE_CFLAGS="$CFLAGS" |
| MHD_CST_SAVE_LIBS="$LIBS" |
| CC="$PTHREAD_CC" |
| CFLAGS="$CFLAGS $PTHREAD_CFLAGS" |
| LIBS="$PTHREAD_LIBS $LIBS" |
| AC_RUN_IFELSE([AC_LANG_SOURCE([[ |
| #include <stdlib.h> |
| |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #ifdef HAVE_TIME_H |
| # include <time.h> |
| #endif |
| #ifdef HAVE_STRING_H |
| # include <string.h> |
| #endif |
| |
| #if !defined(_WIN32) || defined(__CYGWIN__) |
| # ifdef HAVE_SYS_TYPES_H |
| # include <sys/types.h> |
| # endif |
| # ifdef HAVE_SYS_SOCKET_H |
| # include <sys/socket.h> |
| # endif |
| # ifdef HAVE_NETINET_IN_H |
| # include <netinet/in.h> |
| # endif |
| # ifdef HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # endif |
| # ifdef HAVE_SYS_SELECT_H |
| # include <sys/select.h> |
| # endif |
| # ifdef HAVE_NETINET_TCP_H |
| # include <netinet/tcp.h> |
| # endif |
| typedef int MHD_socket; |
| # define MHD_INVALID_SOCKET (-1) |
| # define MHD_POSIX_SOCKETS 1 |
| #else |
| # include <winsock2.h> |
| # include <ws2tcpip.h> |
| # include <windows.h> |
| typedef SOCKET MHD_socket; |
| # define MHD_INVALID_SOCKET (INVALID_SOCKET) |
| # define MHD_WINSOCK_SOCKETS 1 |
| #endif |
| |
| #include <pthread.h> |
| |
| #ifndef SHUT_RD |
| # define SHUT_RD 0 |
| #endif |
| #ifndef SHUT_WR |
| # define SHUT_WR 1 |
| #endif |
| #ifndef SHUT_RDWR |
| # define SHUT_RDWR 2 |
| #endif |
| |
| #ifndef NULL |
| # define NULL ((void*)0) |
| #endif |
| |
| #ifdef HAVE_GETTIMEOFDAY |
| # if defined(_WIN32) && !defined(__CYGWIN__) |
| # undef HAVE_GETTIMEOFDAY |
| # endif |
| #endif |
| |
| |
| #ifdef HAVE_NANOSLEEP |
| static const struct timespec sm_tmout = {0, 1000}; |
| # define short_sleep() nanosleep(&sm_tmout, NULL) |
| #elif defined(HAVE_USLEEP) |
| # define short_sleep() usleep(1) |
| #else |
| # define short_sleep() (void)0 |
| #endif |
| |
| static volatile int going_select = 0; |
| static volatile int select_ends = 0; |
| static volatile int gerror = 0; |
| static int timeout_mils; |
| |
| #ifndef HAVE_GETTIMEOFDAY |
| static volatile long long select_elapsed_time = 0; |
| |
| static long long time_chk(void) |
| { |
| long long ret = time(NULL); |
| if (-1 == ret) |
| gerror = 4; |
| return ret; |
| } |
| #endif |
| |
| |
| static void* select_thrd_func(void* param) |
| { |
| #ifndef HAVE_GETTIMEOFDAY |
| long long start, stop; |
| #endif |
| fd_set rs; |
| struct timeval tmot = {0, 0}; |
| MHD_socket fd = *((MHD_socket*)param); |
| |
| FD_ZERO(&rs); |
| FD_SET(fd, &rs); |
| tmot.tv_usec = timeout_mils * 1000; |
| #ifndef HAVE_GETTIMEOFDAY |
| start = time_chk(); |
| #endif |
| going_select = 1; |
| if (0 > select ((int)(fd) + 1, &rs, NULL, NULL, &tmot)) |
| gerror = 5; |
| #ifndef HAVE_GETTIMEOFDAY |
| stop = time_chk(); |
| select_elapsed_time = stop - start; |
| #endif |
| select_ends = 1; |
| return NULL; |
| } |
| |
| |
| static MHD_socket create_socket(void) |
| { return socket (AF_INET, SOCK_STREAM, 0); } |
| |
| static void close_socket(MHD_socket fd) |
| { |
| #ifdef MHD_POSIX_SOCKETS |
| close(fd); |
| #else |
| closesocket(fd); |
| #endif |
| } |
| |
| static MHD_socket |
| create_socket_listen(int port) |
| { |
| MHD_socket fd; |
| struct sockaddr_in sock_addr; |
| fd = create_socket(); |
| if (MHD_INVALID_SOCKET == fd) |
| return fd; |
| |
| memset (&sock_addr, 0, sizeof (struct sockaddr_in)); |
| sock_addr.sin_family = AF_INET; |
| sock_addr.sin_port = htons(port); |
| sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| |
| if (bind (fd, (const struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0 || |
| listen(fd, SOMAXCONN) < 0) |
| { |
| close_socket(fd); |
| return MHD_INVALID_SOCKET; |
| } |
| return fd; |
| } |
| |
| #ifdef HAVE_GETTIMEOFDAY |
| #define diff_time(tv1, tv2) ((long long)(tv1.tv_sec-tv2.tv_sec)*10000 + (long long)(tv1.tv_usec-tv2.tv_usec)/100) |
| #else |
| #define diff_time(tv1, tv2) ((long long)(tv1-tv2)) |
| #endif |
| |
| static long long test_run_select(int timeout_millsec, int use_shutdown, long long delay_before_shutdown) |
| { |
| pthread_t select_thrd; |
| MHD_socket fd; |
| #ifdef HAVE_GETTIMEOFDAY |
| struct timeval start, stop; |
| #else |
| long long start; |
| #endif |
| |
| fd = create_socket_listen(0); |
| if (MHD_INVALID_SOCKET == fd) |
| return -7; |
| going_select = 0; |
| select_ends = 0; |
| gerror = 0; |
| timeout_mils = timeout_millsec; |
| if (0 != pthread_create (&select_thrd, NULL, select_thrd_func, (void*)&fd)) |
| return -8; |
| while (!going_select) {short_sleep();} |
| #ifdef HAVE_GETTIMEOFDAY |
| gettimeofday (&start, NULL); |
| #else |
| start = time_chk(); |
| #endif |
| if (use_shutdown) |
| { |
| #ifdef HAVE_GETTIMEOFDAY |
| struct timeval current; |
| do {short_sleep(); gettimeofday(¤t, NULL); } while (delay_before_shutdown > diff_time(current, start)); |
| #else |
| while (delay_before_shutdown > time_chk() - start) {short_sleep();} |
| #endif |
| shutdown(fd, SHUT_RDWR); |
| } |
| #ifdef HAVE_GETTIMEOFDAY |
| while (!select_ends) {short_sleep();} |
| gettimeofday (&stop, NULL); |
| #endif |
| if (0 != pthread_join(select_thrd, NULL)) |
| return -9; |
| close_socket(fd); |
| if (gerror) |
| return -10; |
| #ifdef HAVE_GETTIMEOFDAY |
| return (long long)diff_time(stop, start); |
| #else |
| return select_elapsed_time; |
| #endif |
| } |
| |
| static int test_it(void) |
| { |
| long long duration2; |
| #ifdef HAVE_GETTIMEOFDAY |
| long long duration0, duration1; |
| duration0 = test_run_select(0, 0, 0); |
| if (0 > duration0) |
| return -duration0; |
| |
| duration1 = test_run_select(50, 0, 0); |
| if (0 > duration1) |
| return -duration1 + 20; |
| |
| duration2 = test_run_select(500, 1, (duration0 + duration1) / 2); |
| if (0 > duration2) |
| return -duration2 + 40; |
| |
| if (duration1 * 2 > duration2) |
| { /* Check second time to be sure. */ |
| duration2 = test_run_select(500, 1, (duration0 + duration1) / 2); |
| if (0 > duration2) |
| return -duration2 + 60; |
| if (duration1 * 2 > duration2) |
| return 0; |
| } |
| #else |
| duration2 = test_run_select(5000, 1, 2); |
| if (0 > duration2) |
| return -duration2 + 80; |
| |
| if (4 > duration2) |
| { /* Check second time to be sure. */ |
| duration2 = test_run_select(5000, 1, 2); |
| if (0 > duration2) |
| return -duration2 + 100; |
| if (4 > duration2) |
| return 0; |
| } |
| #endif |
| return 1; |
| } |
| |
| |
| static int init(void) |
| { |
| #ifdef MHD_WINSOCK_SOCKETS |
| WSADATA wsa_data; |
| |
| if (0 != WSAStartup(MAKEWORD(2, 2), &wsa_data) || MAKEWORD(2, 2) != wsa_data.wVersion) |
| { |
| WSACleanup(); |
| return 0; |
| } |
| #endif /* MHD_WINSOCK_SOCKETS */ |
| return 1; |
| } |
| |
| static void cleanup(void) |
| { |
| #ifdef MHD_WINSOCK_SOCKETS |
| WSACleanup(); |
| #endif /* MHD_WINSOCK_SOCKETS */ |
| } |
| |
| int main(void) |
| { |
| int res; |
| if (!init()) |
| return 19; |
| |
| res = test_it(); |
| |
| cleanup(); |
| if (gerror) |
| return gerror; |
| |
| return res; |
| } |
| ]])], [$1='yes'], [$1='no'], [$1='guessing no']) |
| CC="$MHD_CST_SAVE_CC" |
| CFLAGS="$MHD_CST_SAVE_CFLAGS" |
| LIBS="$MHD_CST_SAVE_LIBS" |
| AS_UNSET([[MHD_CST_SAVE_CC]]) |
| AS_UNSET([[MHD_CST_SAVE_CFLAGS]]) |
| AS_UNSET([[MHD_CST_SAVE_LIBS]]) |
| AC_LANG_POP([C]) |
| ] |
| ) |