gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnurl] 50/151: multi: add curl_multi_wakeup()


From: gnunet
Subject: [gnurl] 50/151: multi: add curl_multi_wakeup()
Date: Fri, 20 Dec 2019 14:25:59 +0100

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to branch master
in repository gnurl.

commit f3c35e371cc70f1b6bc33f7faa904d37d1567eb3
Author: Gergely Nagy <address@hidden>
AuthorDate: Sun Nov 17 15:12:15 2019 +0100

    multi: add curl_multi_wakeup()
    
    This commit adds curl_multi_wakeup() which was previously in the TODO
    list under the curl_multi_unblock name.
    
    On some platforms and with some configurations this feature might not be
    available or can fail, in these cases a new error code
    (CURLM_WAKEUP_FAILURE) is returned from curl_multi_wakeup().
    
    Fixes #4418
    Closes #4608
---
 docs/TODO                        |   8 --
 docs/libcurl/Makefile.inc        |   1 +
 docs/libcurl/curl_multi_poll.3   |  11 ++-
 docs/libcurl/curl_multi_wakeup.3 |  47 +++++++++
 docs/libcurl/libcurl-errors.3    |   2 +
 docs/libcurl/symbols-in-versions |   1 +
 include/curl/multi.h             |  10 ++
 lib/multi.c                      | 114 +++++++++++++++++++++-
 lib/multihandle.h                |  10 ++
 lib/strerror.c                   |   3 +
 packages/OS400/curl.inc.in       |   6 +-
 tests/data/Makefile.inc          |   2 +-
 tests/data/test1135              |   1 +
 tests/data/test1538              |   3 +-
 tests/data/test1564              |  31 ++++++
 tests/data/test1565              |  41 ++++++++
 tests/libtest/Makefile.inc       |  10 +-
 tests/libtest/lib1564.c          | 142 +++++++++++++++++++++++++++
 tests/libtest/lib1565.c          | 204 +++++++++++++++++++++++++++++++++++++++
 tests/libtest/test.h             |  54 +++++++++++
 20 files changed, 684 insertions(+), 17 deletions(-)

diff --git a/docs/TODO b/docs/TODO
index 46b015c02..e75c1647b 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -51,7 +51,6 @@
  2.4 Split connect and authentication process
  2.5 Edge-triggered sockets should work
  2.6 multi upkeep
- 2.7 curl_multi_unblock
 
  3. Documentation
  3.2 Provide cmake config-file
@@ -448,13 +447,6 @@
 
  See https://github.com/curl/curl/issues/3199
 
-2.7 curl_multi_unblock
-
- A portable way to unblock curl_multi_wait from another thread.
-
- See https://github.com/curl/curl/issues/4418 and
- https://github.com/curl/curl/wiki/curl_multi_unblock
-
 3. Documentation
 
 3.2 Provide cmake config-file
diff --git a/docs/libcurl/Makefile.inc b/docs/libcurl/Makefile.inc
index bd88c9c38..e1185e729 100644
--- a/docs/libcurl/Makefile.inc
+++ b/docs/libcurl/Makefile.inc
@@ -54,6 +54,7 @@ man_MANS = \
  curl_multi_socket_all.3 \
  curl_multi_strerror.3 \
  curl_multi_timeout.3 \
+ curl_multi_wakeup.3 \
  curl_multi_wait.3 \
  curl_share_cleanup.3 \
  curl_share_init.3 \
diff --git a/docs/libcurl/curl_multi_poll.3 b/docs/libcurl/curl_multi_poll.3
index 9fc72c55d..bde80447a 100644
--- a/docs/libcurl/curl_multi_poll.3
+++ b/docs/libcurl/curl_multi_poll.3
@@ -48,10 +48,16 @@ total number of file descriptors on which interesting 
events occurred. This
 number can include both libcurl internal descriptors as well as descriptors
 provided in \fIextra_fds\fP.
 
+The \fIcurl_multi_wakeup(3)\fP function can be used from another thread to
+wake up this function and return faster. This is one of the details
+that makes this function different than \fIcurl_multi_wait(3)\fP which cannot
+be woken up this way.
+
 If no extra file descriptors are provided and libcurl has no file descriptor
 to offer to wait for, this function will instead wait during \fItimeout_ms\fP
 milliseconds (or shorter if an internal timer indicates so). This is the
-detail that makes this function different than \fIcurl_multi_wait(3)\fP.
+other detail that makes this function different than
+\fIcurl_multi_wait(3)\fP.
 
 This function is encouraged to be used instead of select(3) when using the
 multi interface to allow applications to easier circumvent the common problem
@@ -107,4 +113,5 @@ CURLMcode type, general libcurl multi interface error code. 
See
 .SH AVAILABILITY
 This function was added in libcurl 7.66.0.
 .SH "SEE ALSO"
-.BR curl_multi_fdset "(3), " curl_multi_perform "(3), " curl_multi_wait "(3)"
+.BR curl_multi_fdset "(3), " curl_multi_perform "(3), "
+.BR curl_multi_wait "(3), " curl_multi_wakeup "(3)"
diff --git a/docs/libcurl/curl_multi_wakeup.3 b/docs/libcurl/curl_multi_wakeup.3
new file mode 100644
index 000000000..01b462f2f
--- /dev/null
+++ b/docs/libcurl/curl_multi_wakeup.3
@@ -0,0 +1,47 @@
+.\" **************************************************************************
+.\" *                                  _   _ ____  _
+.\" *  Project                     ___| | | |  _ \| |
+.\" *                             / __| | | | |_) | |
+.\" *                            | (__| |_| |  _ <| |___
+.\" *                             \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <address@hidden>, et al.
+.\" *
+.\" * This software is licensed as described in the file COPYING, which
+.\" * you should have received as part of this distribution. The terms
+.\" * are also available at https://curl.haxx.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" **************************************************************************
+.TH curl_multi_wakeup 3 "17 Nov 2019" "libcurl 7.68.0" "libcurl Manual"
+.SH NAME
+curl_multi_wakeup - wakes up a sleeping curl_multi_poll call
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLMcode curl_multi_wakeup(CURLM *multi_handle);
+.ad
+.SH DESCRIPTION
+This function can be called from any thread and it wakes up a
+sleeping \fIcurl_multi_poll(3)\fP call that is currently (or will be)
+waiting for activity or a timeout.
+
+If the function is called when there is no \fIcurl_multi_poll(3)\fP call,
+it will cause the next call to return immediately.
+
+Calling this function only guarantees to wake up the current (or the next
+if there is no current) \fIcurl_multi_poll(3)\fP call, which means it is
+possible that multiple calls to this function will wake up the same waiting
+operation.
+
+This function has no effect on \fIcurl_multi_wait(3)\fP calls.
+.SH RETURN VALUE
+CURLMcode type, general libcurl multi interface error code.
+.SH "SEE ALSO"
+.BR curl_multi_poll "(3), " curl_multi_wait "(3)"
diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3
index 1f985da8e..0305af43e 100644
--- a/docs/libcurl/libcurl-errors.3
+++ b/docs/libcurl/libcurl-errors.3
@@ -297,6 +297,8 @@ An easy handle already added to a multi handle was 
attempted to get added a
 second time. (Added in 7.32.1)
 .IP "CURLM_RECURSIVE_API_CALL (8)"
 An API function was called from inside a callback.
+.IP "CURLM_WAKEUP_FAILURE (9)"
+Wakeup is unavailable or failed.
 .SH "CURLSHcode"
 The "share" interface will return a CURLSHcode to indicate when an error has
 occurred.  Also consider \fIcurl_share_strerror(3)\fP.
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index f3131d2a2..d82439a5b 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -342,6 +342,7 @@ CURLM_INTERNAL_ERROR            7.9.6
 CURLM_OK                        7.9.6
 CURLM_OUT_OF_MEMORY             7.9.6
 CURLM_RECURSIVE_API_CALL        7.59.0
+CURLM_WAKEUP_FAILURE            7.68.0
 CURLM_UNKNOWN_OPTION            7.15.4
 CURLOPTTYPE_FUNCTIONPOINT       7.1
 CURLOPTTYPE_LONG                7.1
diff --git a/include/curl/multi.h b/include/curl/multi.h
index b39218395..89f099259 100644
--- a/include/curl/multi.h
+++ b/include/curl/multi.h
@@ -72,6 +72,7 @@ typedef enum {
                             attempted to get added - again */
   CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a
                                callback */
+  CURLM_WAKEUP_FAILURE, /* wakeup is unavailable or failed */
   CURLM_LAST
 } CURLMcode;
 
@@ -187,6 +188,15 @@ CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle,
                                       int timeout_ms,
                                       int *ret);
 
+/*
+ * Name:     curl_multi_wakeup()
+ *
+ * Desc:     wakes up a sleeping curl_multi_poll call.
+ *
+ * Returns:  CURLMcode type, general multi error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle);
+
  /*
   * Name:    curl_multi_perform()
   *
diff --git a/lib/multi.c b/lib/multi.c
index 9faad0e2e..f30e41a65 100755
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -46,6 +46,7 @@
 #include "connect.h"
 #include "http_proxy.h"
 #include "http2.h"
+#include "socketpair.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -367,6 +368,21 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* 
socket hash */
 
   /* -1 means it not set by user, use the default value */
   multi->maxconnects = -1;
+
+#ifdef ENABLE_WAKEUP
+  if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
+    multi->wakeup_pair[0] = CURL_SOCKET_BAD;
+    multi->wakeup_pair[1] = CURL_SOCKET_BAD;
+  }
+  else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 ||
+          curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) {
+    sclose(multi->wakeup_pair[0]);
+    sclose(multi->wakeup_pair[1]);
+    multi->wakeup_pair[0] = CURL_SOCKET_BAD;
+    multi->wakeup_pair[1] = CURL_SOCKET_BAD;
+  }
+#endif
+
   return multi;
 
   error:
@@ -1005,7 +1021,8 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
                                  unsigned int extra_nfds,
                                  int timeout_ms,
                                  int *ret,
-                                 bool extrawait) /* when no socket, wait */
+                                 bool extrawait, /* when no socket, wait */
+                                 bool use_wakeup)
 {
   struct Curl_easy *data;
   curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
@@ -1059,6 +1076,12 @@ static CURLMcode Curl_multi_wait(struct Curl_multi 
*multi,
   curlfds = nfds; /* number of internal file descriptors */
   nfds += extra_nfds; /* add the externally provided ones */
 
+#ifdef ENABLE_WAKEUP
+  if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+    ++nfds;
+  }
+#endif
+
   if(nfds > NUM_POLLS_ON_STACK) {
     /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
        big, so at 2^29 sockets this value might wrap. When a process gets
@@ -1117,6 +1140,14 @@ static CURLMcode Curl_multi_wait(struct Curl_multi 
*multi,
     ++nfds;
   }
 
+#ifdef ENABLE_WAKEUP
+  if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+    ufds[nfds].fd = multi->wakeup_pair[0];
+    ufds[nfds].events = POLLIN;
+    ++nfds;
+  }
+#endif
+
   if(nfds) {
     int pollrc;
     /* wait... */
@@ -1140,6 +1171,29 @@ static CURLMcode Curl_multi_wait(struct Curl_multi 
*multi,
 
         extra_fds[i].revents = mask;
       }
+
+#ifdef ENABLE_WAKEUP
+      if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+        if(ufds[curlfds + extra_nfds].revents & POLLIN) {
+          char buf[64];
+          while(1) {
+            /* the reading socket is non-blocking, try to read
+               data from it until it receives an error (except EINTR).
+               In normal cases it will get EAGAIN or EWOULDBLOCK
+               when there is no more data, breaking the loop. */
+            if(sread(multi->wakeup_pair[0], buf, sizeof(buf)) < 0) {
+#ifndef USE_WINSOCK
+              if(EINTR == SOCKERRNO)
+                continue;
+#endif
+              break;
+            }
+          }
+          /* do not count the wakeup socket into the returned value */
+          retcode--;
+        }
+      }
+#endif
     }
   }
 
@@ -1174,7 +1228,8 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
                           int timeout_ms,
                           int *ret)
 {
-  return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE);
+  return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE,
+                         FALSE);
 }
 
 CURLMcode curl_multi_poll(struct Curl_multi *multi,
@@ -1183,7 +1238,55 @@ CURLMcode curl_multi_poll(struct Curl_multi *multi,
                           int timeout_ms,
                           int *ret)
 {
-  return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE);
+  return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE,
+                         TRUE);
+}
+
+CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
+{
+  /* this function is usually called from another thread,
+     it has to be careful only to access parts of the
+     Curl_multi struct that are constant */
+
+  /* GOOD_MULTI_HANDLE can be safely called */
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+#ifdef ENABLE_WAKEUP
+  /* the wakeup_pair variable is only written during init and cleanup,
+     making it safe to access from another thread after the init part
+     and before cleanup */
+  if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
+    char buf[1];
+    buf[0] = 1;
+    while(1) {
+      /* swrite() is not thread-safe in general, because concurrent calls
+         can have their messages interleaved, but in this case the content
+         of the messages does not matter, which makes it ok to call.
+
+         The write socket is set to non-blocking, this way this function
+         cannot block, making it safe to call even from the same thread
+         that will call Curl_multi_wait(). If swrite() returns that it
+         would block, it's considered successful because it means that
+         previous calls to this function will wake up the poll(). */
+      if(swrite(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
+        int err = SOCKERRNO;
+        int return_success;
+#ifdef USE_WINSOCK
+        return_success = WSAEWOULDBLOCK == err;
+#else
+        if(EINTR == err)
+          continue;
+        return_success = EWOULDBLOCK == err || EAGAIN == err;
+#endif
+        if(!return_success)
+          return CURLM_WAKEUP_FAILURE;
+      }
+      return CURLM_OK;
+    }
+  }
+#endif
+  return CURLM_WAKEUP_FAILURE;
 }
 
 /*
@@ -2309,6 +2412,11 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
 
     Curl_hash_destroy(&multi->hostcache);
     Curl_psl_destroy(&multi->psl);
+
+#ifdef ENABLE_WAKEUP
+    sclose(multi->wakeup_pair[0]);
+    sclose(multi->wakeup_pair[1]);
+#endif
     free(multi);
 
     return CURLM_OK;
diff --git a/lib/multihandle.h b/lib/multihandle.h
index b65bd9638..a26fb619a 100644
--- a/lib/multihandle.h
+++ b/lib/multihandle.h
@@ -24,6 +24,7 @@
 
 #include "conncache.h"
 #include "psl.h"
+#include "socketpair.h"
 
 struct Curl_message {
   struct curl_llist_element list;
@@ -66,6 +67,10 @@ typedef enum {
 
 #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX)
 
+#if defined(USE_SOCKETPAIR) && !defined(USE_BLOCKING_SOCKETS)
+#define ENABLE_WAKEUP
+#endif
+
 /* This is the struct known as CURLM on the outside */
 struct Curl_multi {
   /* First a simple identifier to easier detect if a user mix up
@@ -134,6 +139,11 @@ struct Curl_multi {
                                     previous callback */
   bool in_callback;            /* true while executing a callback */
   long max_concurrent_streams; /* max concurrent streams client to support */
+
+#ifdef ENABLE_WAKEUP
+  curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
+                                   0 is used for read, 1 is used for write */
+#endif
 };
 
 #endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/lib/strerror.c b/lib/strerror.c
index ba2e7a6f9..baf5451ae 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -389,6 +389,9 @@ curl_multi_strerror(CURLMcode error)
   case CURLM_RECURSIVE_API_CALL:
     return "API function called from within callback";
 
+  case CURLM_WAKEUP_FAILURE:
+    return "Wakeup is unavailable or failed";
+
   case CURLM_LAST:
     break;
   }
diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in
index 8be6c8986..9a44eae49 100644
--- a/packages/OS400/curl.inc.in
+++ b/packages/OS400/curl.inc.in
@@ -1804,7 +1804,11 @@
      d                 c                   6
      d  CURLM_ADDED_ALREADY...
      d                 c                   7
-     d  CURLM_LAST     c                   8
+     d  CURLM_RECURSIVE_API_CALL...
+     d                 c                   8
+     d  CURLM_WAKEUP_FAILURE...
+     d                 c                   9
+     d  CURLM_LAST     c                   10
       *
      d CURLMSG         s             10i 0 based(######ptr######)              
 Enum
      d  CURLMSG_NONE   c                   0
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 23215655b..c45bced5b 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -179,7 +179,7 @@ test1525 test1526 test1527 test1528 test1529 test1530 
test1531 test1532 \
 test1533 test1534 test1535 test1536 test1537 test1538 \
 test1540 test1541 \
 test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
-test1558 test1559 test1560 test1561 test1562 test1563 \
+test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 \
 \
 test1590 test1591 test1592 test1593 test1594 test1595 test1596 \
 \
diff --git a/tests/data/test1135 b/tests/data/test1135
index eca6860fb..37a55427f 100644
--- a/tests/data/test1135
+++ b/tests/data/test1135
@@ -92,6 +92,7 @@ CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM 
*multi_handle,
 CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
 CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
 CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle);
 CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
 CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
 CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
diff --git a/tests/data/test1538 b/tests/data/test1538
index 36f53040b..3b22ebc27 100644
--- a/tests/data/test1538
+++ b/tests/data/test1538
@@ -139,7 +139,8 @@ m5: Invalid socket argument
 m6: Unknown option
 m7: The easy handle is already added to a multi handle
 m8: API function called from within callback
-m9: Unknown error
+m9: Wakeup is unavailable or failed
+m10: Unknown error
 s0: No error
 s1: Unknown share option
 s2: Share currently in use
diff --git a/tests/data/test1564 b/tests/data/test1564
new file mode 100644
index 000000000..279665bd1
--- /dev/null
+++ b/tests/data/test1564
@@ -0,0 +1,31 @@
+<testcase>
+<info>
+<keywords>
+multi
+wakeup
+</keywords>
+</info>
+
+# Server-side
+<reply>
+</reply>
+
+# Client-side
+<client>
+<server>
+none
+</server>
+<tool>
+lib1564
+</tool>
+<name>
+wakeup before poll with no easy handles
+</name>
+<command>
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+</verify>
+</testcase>
diff --git a/tests/data/test1565 b/tests/data/test1565
new file mode 100644
index 000000000..f554e0f34
--- /dev/null
+++ b/tests/data/test1565
@@ -0,0 +1,41 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+multi
+multi-threaded
+wakeup
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Content-Length: 3
+
+OK
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<tool>
+lib1565
+</tool>
+<name>
+wakeup from another thread
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/1
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+</verify>
+</testcase>
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index 9ba72d7de..374a66747 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -31,7 +31,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect     
           \
  lib1534 lib1535 lib1536 lib1537 lib1538 \
  lib1540 lib1541 \
  lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \
- lib1558 lib1559 lib1560 \
+ lib1558 lib1559 lib1560 lib1564 lib1565 \
  lib1591 lib1592 lib1593 lib1594 lib1596 \
  lib1900 lib1905 lib1906 lib1907 \
  lib2033
@@ -536,6 +536,14 @@ lib1559_LDADD = $(TESTUTIL_LIBS)
 lib1560_SOURCES = lib1560.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
 lib1560_LDADD = $(TESTUTIL_LIBS)
 
+lib1564_SOURCES = lib1564.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1564_LDADD = $(TESTUTIL_LIBS)
+lib1564_CPPFLAGS = $(AM_CPPFLAGS)
+
+lib1565_SOURCES = lib1565.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1565_LDADD = $(TESTUTIL_LIBS)
+lib1565_CPPFLAGS = $(AM_CPPFLAGS)
+
 lib1591_SOURCES = lib1591.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
 lib1591_LDADD = $(TESTUTIL_LIBS)
 lib1591_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1591
diff --git a/tests/libtest/lib1564.c b/tests/libtest/lib1564.c
new file mode 100644
index 000000000..225c8c6d7
--- /dev/null
+++ b/tests/libtest/lib1564.c
@@ -0,0 +1,142 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <address@hidden>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "testutil.h"
+#include "warnless.h"
+#include "memdebug.h"
+
+#define TEST_HANG_TIMEOUT 60 * 1000
+#define WAKEUP_NUM 1234567
+
+int test(char *URL)
+{
+  CURLM *multi = NULL;
+  int numfds;
+  int i;
+  int res = 0;
+  struct timeval time_before_wait, time_after_wait;
+
+  (void)URL;
+
+  start_test_timing();
+
+  global_init(CURL_GLOBAL_ALL);
+
+  multi_init(multi);
+
+  /* no wakeup */
+
+  time_before_wait = tutil_tvnow();
+  multi_poll(multi, NULL, 0, 1000, &numfds);
+  time_after_wait = tutil_tvnow();
+
+  if(tutil_tvdiff(time_after_wait, time_before_wait) < 500) {
+    fprintf(stderr, "%s:%d curl_multi_poll returned too early\n",
+            __FILE__, __LINE__);
+    res = TEST_ERR_MAJOR_BAD;
+    goto test_cleanup;
+  }
+
+  abort_on_test_timeout();
+
+  /* try a single wakeup */
+
+  multi_wakeup(multi);
+
+  time_before_wait = tutil_tvnow();
+  multi_poll(multi, NULL, 0, 1000, &numfds);
+  time_after_wait = tutil_tvnow();
+
+  if(tutil_tvdiff(time_after_wait, time_before_wait) > 500) {
+    fprintf(stderr, "%s:%d curl_multi_poll returned too late\n",
+            __FILE__, __LINE__);
+    res = TEST_ERR_MAJOR_BAD;
+    goto test_cleanup;
+  }
+
+  abort_on_test_timeout();
+
+  /* previous wakeup should not wake up this */
+
+  time_before_wait = tutil_tvnow();
+  multi_poll(multi, NULL, 0, 1000, &numfds);
+  time_after_wait = tutil_tvnow();
+
+  if(tutil_tvdiff(time_after_wait, time_before_wait) < 500) {
+    fprintf(stderr, "%s:%d curl_multi_poll returned too early\n",
+            __FILE__, __LINE__);
+    res = TEST_ERR_MAJOR_BAD;
+    goto test_cleanup;
+  }
+
+  abort_on_test_timeout();
+
+  /* try lots of wakeup */
+
+  for(i = 0; i < WAKEUP_NUM; ++i)
+    multi_wakeup(multi);
+
+  time_before_wait = tutil_tvnow();
+  multi_poll(multi, NULL, 0, 1000, &numfds);
+  time_after_wait = tutil_tvnow();
+
+  if(tutil_tvdiff(time_after_wait, time_before_wait) > 500) {
+    fprintf(stderr, "%s:%d curl_multi_poll returned too late\n",
+            __FILE__, __LINE__);
+    res = TEST_ERR_MAJOR_BAD;
+    goto test_cleanup;
+  }
+
+  abort_on_test_timeout();
+
+#if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) \
+    && !defined(__CYGWIN__)
+  /* Even lots of previous wakeups should not wake up this.
+
+     On Windows (particularly when using MinGW), the socketpair
+     used for curl_multi_wakeup() is really asynchronous,
+     meaning when it's called a lot, it can take some time
+     before all of the data can be read. Sometimes it can wake
+     up more than one curl_multi_poll() call. */
+
+  time_before_wait = tutil_tvnow();
+  multi_poll(multi, NULL, 0, 1000, &numfds);
+  time_after_wait = tutil_tvnow();
+
+  if(tutil_tvdiff(time_after_wait, time_before_wait) < 500) {
+    fprintf(stderr, "%s:%d curl_multi_poll returned too early\n",
+            __FILE__, __LINE__);
+    res = TEST_ERR_MAJOR_BAD;
+    goto test_cleanup;
+  }
+
+  abort_on_test_timeout();
+#endif
+
+test_cleanup:
+
+  curl_multi_cleanup(multi);
+  curl_global_cleanup();
+
+  return res;
+}
diff --git a/tests/libtest/lib1565.c b/tests/libtest/lib1565.c
new file mode 100644
index 000000000..b2fa40aaa
--- /dev/null
+++ b/tests/libtest/lib1565.c
@@ -0,0 +1,204 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <address@hidden>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "testutil.h"
+#include "warnless.h"
+#include "memdebug.h"
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#include <unistd.h>
+
+#define TEST_HANG_TIMEOUT 60 * 1000
+#define CONN_NUM 3
+#define TIME_BETWEEN_START_SECS 2
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static CURL *pending_handles[CONN_NUM];
+static int pending_num = 0;
+static int test_failure = 0;
+
+static CURLM *multi = NULL;
+static const char *url;
+
+static void *run_thread(void *ptr)
+{
+  CURL *easy = NULL;
+  int res = 0;
+  int i;
+
+  (void)ptr;
+
+  for(i = 0; i < CONN_NUM; i++) {
+    sleep(TIME_BETWEEN_START_SECS);
+
+    easy_init(easy);
+
+    easy_setopt(easy, CURLOPT_URL, url);
+    easy_setopt(easy, CURLOPT_VERBOSE, 0L);
+
+    pthread_mutex_lock(&lock);
+
+    if(test_failure) {
+      pthread_mutex_unlock(&lock);
+      goto test_cleanup;
+    }
+
+    pending_handles[pending_num] = easy;
+    pending_num++;
+    easy = NULL;
+
+    pthread_mutex_unlock(&lock);
+
+    multi_wakeup(multi);
+  }
+
+test_cleanup:
+
+  curl_easy_cleanup(easy);
+
+  pthread_mutex_lock(&lock);
+
+  if(!test_failure)
+    test_failure = res;
+
+  pthread_mutex_unlock(&lock);
+
+  return NULL;
+}
+
+int test(char *URL)
+{
+  int still_running;
+  int num;
+  int i;
+  int res = 0;
+  CURL *started_handles[CONN_NUM];
+  int started_num = 0;
+  int finished_num = 0;
+  pthread_t tid = 0;
+  struct CURLMsg *message;
+
+  start_test_timing();
+
+  global_init(CURL_GLOBAL_ALL);
+
+  multi_init(multi);
+
+  url = URL;
+
+  res = pthread_create(&tid, NULL, run_thread, NULL);
+  if(0 != res) {
+    fprintf(stderr, "%s:%d Couldn't create thread, errno %d\n",
+            __FILE__, __LINE__, res);
+    goto test_cleanup;
+  }
+
+  while(1) {
+    multi_perform(multi, &still_running);
+
+    abort_on_test_timeout();
+
+    while((message = curl_multi_info_read(multi, &num)) != NULL) {
+      if(message->msg == CURLMSG_DONE) {
+        res = message->data.result;
+        if(res)
+          goto test_cleanup;
+        multi_remove_handle(multi, message->easy_handle);
+        finished_num++;
+      }
+      else {
+        fprintf(stderr, "%s:%d Got an unexpected message from curl: %i\n",
+              __FILE__, __LINE__, (int)message->msg);
+        res = TEST_ERR_MAJOR_BAD;
+        goto test_cleanup;
+      }
+
+      abort_on_test_timeout();
+    }
+
+    if(CONN_NUM == finished_num)
+      break;
+
+    multi_poll(multi, NULL, 0, TEST_HANG_TIMEOUT, &num);
+
+    abort_on_test_timeout();
+
+    pthread_mutex_lock(&lock);
+
+    while(pending_num > 0) {
+      res_multi_add_handle(multi, pending_handles[pending_num - 1]);
+      if(res) {
+        pthread_mutex_unlock(&lock);
+        goto test_cleanup;
+      }
+
+      started_handles[started_num] = pending_handles[pending_num - 1];
+      started_num++;
+      pending_num--;
+    }
+
+    pthread_mutex_unlock(&lock);
+
+    abort_on_test_timeout();
+  }
+
+  if(CONN_NUM != started_num) {
+    fprintf(stderr, "%s:%d Not all connections started: %d of %d\n",
+            __FILE__, __LINE__, started_num, CONN_NUM);
+    goto test_cleanup;
+  }
+
+  if(CONN_NUM != finished_num) {
+    fprintf(stderr, "%s:%d Not all connections finished: %d of %d\n",
+            __FILE__, __LINE__, started_num, CONN_NUM);
+    goto test_cleanup;
+  }
+
+test_cleanup:
+
+  pthread_mutex_lock(&lock);
+  if(!test_failure)
+    test_failure = res;
+  pthread_mutex_unlock(&lock);
+
+  if(0 != tid)
+    pthread_join(tid, NULL);
+
+  curl_multi_cleanup(multi);
+  for(i = 0; i < pending_num; i++)
+    curl_easy_cleanup(pending_handles[i]);
+  for(i = 0; i < started_num; i++)
+    curl_easy_cleanup(started_handles[i]);
+  curl_global_cleanup();
+
+  return test_failure;
+}
+
+#else /* without pthread, this test doesn't work */
+int test(char *URL)
+{
+  (void)URL;
+  return 0;
+}
+#endif
diff --git a/tests/libtest/test.h b/tests/libtest/test.h
index bb1acca0e..3c8323de4 100644
--- a/tests/libtest/test.h
+++ b/tests/libtest/test.h
@@ -355,6 +355,60 @@ extern int unitfail;
 
 /* ---------------------------------------------------------------- */
 
+#define exe_multi_poll(A,B,C,D,E,Y,Z) do {                          \
+  CURLMcode ec;                                                     \
+  if((ec = curl_multi_poll((A), (B), (C), (D), (E))) != CURLM_OK) { \
+    fprintf(stderr, "%s:%d curl_multi_poll() failed, "              \
+            "with code %d (%s)\n",                                  \
+            (Y), (Z), (int)ec, curl_multi_strerror(ec));            \
+    res = (int)ec;                                                  \
+  }                                                                 \
+  else if(*((E)) < 0) {                                             \
+    fprintf(stderr, "%s:%d curl_multi_poll() succeeded, "           \
+            "but returned invalid numfds value (%d)\n",             \
+            (Y), (Z), (int)*((E)));                                 \
+    res = TEST_ERR_NUM_HANDLES;                                     \
+  }                                                                 \
+} WHILE_FALSE
+
+#define res_multi_poll(A, B, C, D, E) \
+  exe_multi_poll((A), (B), (C), (D), (E), (__FILE__), (__LINE__))
+
+#define chk_multi_poll(A, B, C, D, E, Y, Z) do {     \
+  exe_multi_poll((A), (B), (C), (D), (E), (Y), (Z)); \
+  if(res)                                            \
+    goto test_cleanup;                               \
+} WHILE_FALSE
+
+#define multi_poll(A, B, C, D, E) \
+  chk_multi_poll((A), (B), (C), (D), (E), (__FILE__), (__LINE__))
+
+/* ---------------------------------------------------------------- */
+
+#define exe_multi_wakeup(A,Y,Z) do {                     \
+  CURLMcode ec;                                          \
+  if((ec = curl_multi_wakeup((A))) != CURLM_OK) {        \
+    fprintf(stderr, "%s:%d curl_multi_wakeup() failed, " \
+            "with code %d (%s)\n",                       \
+            (Y), (Z), (int)ec, curl_multi_strerror(ec)); \
+    res = (int)ec;                                       \
+  }                                                      \
+} WHILE_FALSE
+
+#define res_multi_wakeup(A) \
+  exe_multi_wakeup((A), (__FILE__), (__LINE__))
+
+#define chk_multi_wakeup(A, Y, Z) do { \
+  exe_multi_wakeup((A), (Y), (Z));     \
+  if(res)                              \
+    goto test_cleanup;                 \
+} WHILE_FALSE
+
+#define multi_wakeup(A) \
+  chk_multi_wakeup((A), (__FILE__), (__LINE__))
+
+/* ---------------------------------------------------------------- */
+
 #define exe_select_test(A, B, C, D, E, Y, Z) do {               \
     int ec;                                                     \
     if(select_wrapper((A), (B), (C), (D), (E)) == -1) {         \

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]